条款21,必须返回对象时,别妄想返回其reference
绝不要返回pointer或reference指向一个local stack对象;
绝不要返回reference指向一个heap-allocated对象,因为需要严密考虑delete。如果一行多次使用这样的reference而只是临时使用,必定会造成内存泄露。
const Rational& operator* (const Rational& lhs,const Rational& rhs)
{
Rational* result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
return *result;
}
Rational w,x,y,z;
w = x * y * z;
上述代码就是,同一语句内调用了两次operator*,因而两次使用new,也就需要两次delete。但却没有合理的办法让operator使用者进行那些delete调用,因为没有合理的办法让他们取得operator返回的reference背后隐藏的指针。
绝不要返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象。这里需要考虑单线程和多线程。
条款22,将成员变量声明为private
切记将成员变量声明为private。这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者以充分的实现弹性。
protected并不比public更具有封装性。
条款23,宁以non-member,non-friend替换member函数
宁可以non-member non-friend函数替换member函数。这样让更少的函数访问private变量,可以增加封装性、包裹弹性和机能扩充性。
条款24,若所有参数皆需类型转换,请为此采用non-member函数
如果你需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member。因为对this指针所指的对象进行类型转换前,不能判断其为要转换的类型,故无法调用其member函数。
条款25,考虑写出一个不抛异常的swap函数
当std::swap对你的类型效率不高时2,提供一个swap成员函数,并确定这个函数不抛出异常。
如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于classes而非templates,也请特化std::swap。
调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何命名空间资格修饰。
为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西。
条款26,尽可能延后变量定义式的出现时间
尽可能延后定义式的出现。这样做可以增加程序的清晰度并改善程序效率。
条款27,尽量少做转型动作
如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts。如果有个设计需要转型动作,试着发展无需转型的替代设计。
如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需要将转型放进他们自己的代码内。
宁可使用C+±style新式转型,也不要使用旧式转型。
条款28,避免返回handles指向对象内部成分
避免返回handles(包括reference、指针、迭代器)指向对象内部。这样可以增加封装性。
条款29,为“异常安全”而努力是值得的
当异常被抛出时,带有异常安全性的函数会:(1)不泄露任何资源(2)不允许数据败坏。
异常安全函数提供三个保证之一:(1)基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。没有任何对象或数据结构会因此而败坏,所有对象都处于一种内部前后一致的状态。(2)强烈保证:如果异常被抛出,程序状态不改变。如果函数成功,则是完全成功。如果函数失败,程序会回溯到调用函数之前的状态。(3)不抛掷保证:承诺绝不抛出异常。
“强烈保证”往往能够以copy-and-swap实现出来,但“强烈保证”并非对所有函数都可实现或具备现实意义。
函数提供的“异常安全保证”通常最高等于其所调用的各个函数的“异常安全保证”中的最弱者。
条款30,透彻了解inlining的里里外外
将大多数inlineing限制在小型、被频繁调用的函数上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。
不要只因function templates出现在头文件,就将它们声明为inline。因为毕竟只是模板,不是最终的函数代码。