文章目录
条款26:尽可能延后变量定义式的出现时间
std::string encryptPassword(const std::string& password)
{
using namespace std;
string encrypted;
if (password.length() < MinimumPasswordLength) {
throw logic_error("Password is too short");
}
...
return encrypted;
}
上述这个案例中encrypted对象没有被使用,但是经历了一次构造和析构过程。将其延后到异常抛出之后,就可以避免这种问题。
如条款4解释“通过default构造函数构造出对象后再对它赋值比直接在构造时指定初值效率更差”,不应该只是延后变量的定义,甚至延后到能够给它初值为止。从而避免毫无意义的default构造行为。
条款27:尽量少做转型动作
C++提供了四种新式转型:
1、const_cast:将对象的常量性移除。
2、dynamic_cast:用于“安全向下转型”,决定某对象是否归属体系中的某个类型。(效率低)
3、reinterpret_cast:为操作数的位模式提供较低层次的重新解释。例如将pointer to int转为int。
4、static_cast:强迫隐式转换。
转型的缺陷:
(1) 任何类型转换都会令编译器编译出运行期间执行的代码。
int x, y;`在这里插入代码片`
double d = static_cast<double>(x) / y; // x转型为double会产生一些代码,因为底层实现中int不同于double
(2) 单一对象可能拥有一个以上的地址。
class Base {...};
class Derived: public Base {...};
Derived d;
Base* pb = &d; // pb指向&d + 偏移量
Derived *pb1 = &d; // pb1指向&d
(3) 转型可能导致代码似是而非
class Derived: public Base {
public:
virtual void onResize() {
static_cast<Base>(*this).onResize(); // static_cast<Base>(*this)返回*this的副本
...
}
};
(4) dynamic_cast效率很低:每次使用都会进行class名称比较。
条款28:避免返回handles指向对象内部成分
一旦handle被传出去了,就意味着存在“handle比其所指对象更长寿”的风险。
Class GUIObject {...};
const Rectangle boundingBox(const GUIObject &obj);
GUIObject *pgo;
const Pointer* p = &(boundingBox(*pgo).upperLeft());
// 上述boundingBox()返回一个临时的Rectangle对象,当这样语句结束时该对象就会被销毁,但是upperLeft()返回的handle还在。
条款29:为“异常安全”而努力是值得的
具备异常安全性的函数在当异常被抛出时会:
(1) 不泄露任何资源。
(2) 不允许数据被破坏。
这样的函数区分三种级别的保证:
(1) 基本型:异常被抛出,程序内任何事物仍然保持在有效状态。
(2) 强烈型:异常被抛出,程序状态不会改变,即函数成功,就是完全成功;函数失败,就会恢复到“调用函数之前”的状态。
(3) 不抛异常型:承诺绝不抛异常。
条款30:透彻了解inlining的里里外外
inline函数背后的整体观念是,将“对此函数的每一个调用”都以函数本体替换之。将大多数inline限制在小型、被频繁调用的函数身上,可使日后的调试过程和二进制升级更容易,也可以使代码膨胀最小化。