《Effective C++》笔记:II
条款4:Make sure that objects are initialized before they're used.
译:确定对象被使用前已先被初始化.
C++的初始化
先来一个Java的初始化
class Student{ private String Name; private int Age; Student(){ this.Name = “Destino74”; this.Age = 22; } }
但到了C++里,给成员变量赋值的形式并不是初始化。对象的成员变量初始化发生在进入构造函数本体之前,所以上述只是在成员变量初始化后改变其值,初始化的发生时间在这些成员的default构造函数调用之前。
在C++中,初始化其实就是使用初始化序列(member initialization)替换赋值:
#include <string> class Student { public: Student() : m_name("Destino74"), m_age(20){} ~Student(){} void Show() { printf("name:%s age:%d", m_name.c_str(), m_age); } private: std::string m_name; int m_age; }; int main() { Student stu; stu.Show(); return 0; }
虽然内置对面(m_age)的初始化成本与赋值相同,但大多数类型使用初始化往往效率优于赋值,因为比起使用default构造函数构造出一个右值再用copy assignment操作符把右值赋给左值,只调用一次Copy构造函数往往更加高效。
一般来说,建议在初始化列表里把对象的所有成员逐个初始化,这不单是出于性能上的考虑,还有调试成本的减少:这样做可以使你不必关心哪个成员已经或仍未初始化,也不用担心在使用对象时出现因未初始化而产生的不明确行为。
另外,书中还提到要关注初始化次序。因为C++中有明确的初始化次序:基类总是比衍生类更早初始化,类的成员变量总是以声明次序初始化。
虽然你可以初始化序列中把成员变量初始化序列的出现次序写得乱七八糟,但这不会影响它们的初始化顺序。
最后提到的,就是non-local static变量(全局静态变量)的初始化序列带来的问题。
当在某个文件中用到一个全局静态变量时,如何确保该全局静态变量已经被初始化?
书中原话:C++对“定义于不同编译单内的non-local-static对象”的初始化次序没有明确定义。也就是说,我们是很难或者说没办法决定它们的初始化次序。
但用一个小小的设计就可以解决这个问题,源于local-static对象(局部静态变量)的特性:C++保证,函数内的local static对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。学过单例模式的都很清楚了,维护唯一一个实例,该实例只有在首次调用时被初始化,之后的调用都是对这个对象引用进行操作,这就是对象的延迟初始化(懒汉式)。其优点就不再加以阐述了。
使用构造函数的初始化序列对每个成员变量进行初始化,这往往比赋值高效。初始化序列列出的成员变量,应该和它们在类中的声明次序相同。
为免除“跨编译单元之初始化序列”问题,请以local static对象替换non-local static对象