让接口容易被正确使用,不易被误用

  1. 好的接口很容易被正确使用,不容易被误用。我们应该在所有的接口中努力达成这些性质
  2. “促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容
  3. “阻止误用”的办法包括建立新类型、限制类型上的操作,束缚对象值,以及消除客户的资源管理责任
  4. shared_ptr支持自定义删除器,这可防范DLL问题,可被用来自动解除互斥锁(mutexes)

设计class犹如设计type

  1. 定义一个class就是定义了一个新的type,设计优秀的type很艰难,因此设计优秀的class也很不容易。
  2. 设计class之前,需要考虑到以下问题:
    (1)新type类型对象的创建与销毁,决定构造函数和析构函数以及内存分配和释放函数
    (2)对象初始化和赋值的行为差别,决定构造函数和赋值操作符
    (3)对象被以值传递,决定拷贝构造函数
    (4)新type的合法值,比如月份class就只有1-12这12个合法值
    (5)新type是否需要继承或被继承,如果被继承则析构函数是否需要为virtual
    (6)新type是否需要转换,决定类型转换函数
    (7)新type需要什么样的操作符和函数
    (8)哪些函数需要被驳回,决定是否声明为private或delete函数
    (9)如何获取新type的成员
    (10)新type的“未声明接口”
    (11)新type是否很一般化通用,如果是就需要考虑定义为一个新的class template
    (12)是否真的需要一个新type
  3. class的设计就是type的设计,在定义一个新type之前,确保自己想过上述这些主题。

宁以传常量引用替换传值

  1. 默认情况下C++以传值方式传递至函数,但是这些副本都是由对象的拷贝构造函数产出,这可能使得传值成为昂贵费时的操作
  2. 为了避免传值时可能昂贵的开销,我们可以使用传引用的方式传递,同时为了避免在函数中对原对象进行修改加上const修饰。并且以传引用的方式也可以避免对象切割问题,使用传值传递一个派生类对象实参到一个基类对象形参,派生类部分将会被切割掉。
  3. C++编译器底层往往是使用指针来实现引用,因此传引用通常意味着传递的是真正的指针,当对象的类型是内置类型时,传值往往会比传引用效率更高,这个规则同样适用于STL的迭代器和函数对象。
  4. 并不是所有小型type都是传值的合格候选人,对象小不代表其copy函数不昂贵,许多对象比如STL中的很多容器内含的东西只比指针多一些,但复制却要复制每一样东西。
  5. 并且作为用户自定义类型其大小容易有所变化因此不适合用作传值。

必须返回对象时,别忘想返回其reference

  1. 任何函数如果返回一个reference或者指针指向某个local对象,都将一败涂地,因为local对象过了生命周期会被销毁。
  2. 如果返回一个堆上new出来对象的引用,但是随之而来的问题是分配内存释放的问题。
  3. 如果使用static修饰局部变量带来问题是多线程安全性和多个函数其实会指向同一个变量从而造成困扰。
  4. 当我们必须在返回一个reference和返回一个object之间选择时,我们的工作就是挑出行为正确的那个。

将成员变量声明为private

  1. 类接口如果都是函数,用户就不用考虑是以函数方式还是变量方式调用了,全部都用函数调用(加上括号)即可。
  2. 使用函数可以让我们对成员变量的处理有更精确的控制,如果是public的成员变量,每个人都可以读写它,但是函数的话我们可以设置读和写的控制。
  3. 将成员变量隐藏在函数接口的背后,可以为“所有可能的实现”提供弹性。当有成员变量进行修改时可以及时通知其他对象或者验证class的约束条件等。
  4. 切记将成员变量声明为private,这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者以充分的实现弹性。
  5. protected并不比private更具封装性。

宁以non-memeber、non-friend替换memeber函数

  1. 在对象内部愈少代码可以访问到数据,数据的封装性也就愈好,而non-member non-friend函数想比member函数访问的数据更少,因此提供的封装性也就愈好。
  2. 一个函数“成为class的non-member”,并不意味着它“不可以是另一个class的memeber”。在C++中比较自然的做法是让non-member函数位于和需要操作类在同一个命名空间中。namespace可以跨越多个源码文件但是class不能。
  3. C++标准程序库并不是拥有单一、整体、庞大的某个头文件,而是有数十个头文件(<vector>、<algorithm>等)组成,每个头文件声明std的某些机能。
  4. 类中的成员函数比如重载的运算符默认会将自身类型作为运算符的第一个参数,但是只有当参数被列于参数列内这个参数才能进行隐式类型转换。所以当所有的参数都需要进行类型转换时,将这个函数声明为非成员函数。

考虑写出一个不抛异常的swap函数

  1. 当namespace对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。
  2. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者,对于classes(而非templates),也请特例化std::swap。
  3. 调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。
  4. 为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西,std里的都由标准委员会制定。