条款04:确定对象被使用前已先被初始化

Make sure that objects are initialized before they're used.

读取未初始化的值会导致不明确的行为。array(来自 C part of C++)不保证其内容被初始化,而 vector(来自 STL part of C++)却有此保证。所以最佳的办法是永远在使用对象之前将它初始化,而对于内置类型以外的任何其他东西,初始化责任落在构造函数(constructors)身上,规则很简单,确保每一个构造函数都将对象的每一个成员初始化。

赋值(assignment)和初始化(initialization)

赋值和初始化操作是不一样的,例如:

class PhoneNumber { ... };
class ABEntry {
public:
    ABEntry(const PhoneNumber& phone) {
        thePhone = phone;    // 这是赋值操作,而非初始化
    }
private:
    PhoneNumber thePhone;
};

更好的做法是使用初始化操作:

class PhoneNumber { ... };
class ABEntry {
public:
    ABEntry(const PhoneNumber& phone): thePhone(phone) {}
private:
    PhoneNumber thePhone;
};

基于赋值的版本首先调用 default 构造函数为 thePhone 设初值,然后立刻再对它赋予新值,这相当于浪费了 default 构造函数的操作。而成员初值列(member initialization list)的做法避免了这一问题,因为初值列中针对各个成员变量而设置的实参被拿去作为各成员变量构造函数的实参。

许多 classes 拥有多个构造函数,每个构造函数有自己的成员初值列,合理地在初值列中遗漏那些”赋值表现像初始化一样好“的成员变量,改用它们的赋值操作,并将那些赋值操作移往某个函数,供所有构造函数调用,以避免代码冗余。

C++ 有着十分固定的”成员初始化次序“,次序总是相同的,base classes 更早于其 derived classes,而 class 的成员变量总是以其声明次序被初始化。

static 对象

static 对象,其寿命从被构造出来直到程序结束为止,因此 stack 和 heap-based 对象都被排除,而程序结束时 static 对象会被自动销毁,也就是它们的析构函数会在 main() 结束时自动调用。

问题是:如果某编译单元内的某个 non-loca static 对象的初始化动作使用了另一编译单元内的某个 non-local static 对象,它所用到的这个对象可能尚未被初始化,因为 C++ 对”定义于不同编译单元内的 non-local static 对象“的初始化次序并无明确定义。

class FileSystem {
public:
    std::size_t numDisks() const;
};
extern FileSystem tfs; // 预备给客户使用的对象

解决办法是 Singleton 模式,将每个 non-local static 对象搬到自己的专属函数内。

class FileSystem { ... };
FileSystem& tfs() {
    static FileSystem fs;
    return fs;
}

Last updated

Was this helpful?