文章目录
一、内置类型必须手工初始化
因为读取未初始化的值会导致不明确的行为。在C++里的C语言部分中,对象的初始化没有保证;但对于其他部分,却有此保证。
对于这种情况,最好的办法就是:永远在使用对象前,手工去先将其初始化。
二、内置类型以外的初始化依靠于其构造函数
这里有一个规则:必须确保每个构造函数中,都将对象中的每一个成员初始化。
但别混淆了赋值和初始化,最好是使用成员初值列替换赋值动作。
结合下面的例子理解:
class Player
{
public:
Player(const string& name, const int& level);
private:
string theName;
int theLevel;
};
//下面是赋值
Player::Player(const string& name, const int& level)
{
theName = name;
theLevel = level;
}
//下面是成员初值列
Player::Player(const string& name, const int& level)
:theName(name),
theLevel(level)
{ }
上面第一个实现的就是赋值动作的构造函数,而第二个则是成员初值列。
(一)使用成员初值列的原因
1.效率较高
为什么会说效率较高呢?C++规定,对象的成员的初始化动作是发生在进入构造函数的函数体之前的。也就是说赋值动作的构造函数中,成员变量theName和theLevel都先通过调用了default(默认)构造函数来设为"没有意义"的初值,再去调用copy(赋值)构造来赋值。而成员初值列的做法则避免了这一问题,不需要去调用defaule构造函数。对于大多数类型,相比之下,成员初值列的方法高效了许多。对于内置类型,其初始化和赋值的成本相同,但为了一致性,最好也通过成员初值列来初始化。
但这里也要注意一个问题:总是在初值列中列出所有成员变量。不然还得去记住那些成员变量无需初值,若遗漏了,没有初值,还是会掉入"不明确行为"的深渊。
2.若有成员变量是const或reference(引用),只能初始化。
如果成员变量是const或引用,那么赋值操作则是错误的,一定需要初值,那么只能通过初值化列表来初始化,而避免需要记住那些成员可以赋值,那些不可以。最简单的做法就是:总是用成员初值列,有时是必要的操作,而又往往比赋值更高效。
(二)多个构造函数的成员初值列重复问题
许多classes拥有多个构造函数,而每个构造函数有自己的成员初值列,如果在多个构造函数的成员初值列中,许多成员变量都是一样的,就会导致不受欢迎的重复(在初值列中)和无聊的工作(对程序员而言),在这种情况下就可以将那些 “赋值表现和初始化一样好” (内置类型)的成员变量,改用赋值操作,写成一个private函数,供所有构造函数调用。
三、初始化次序的问题
固定的"成员初始化次序":基类早于其派生类被初始化,而类的成员总是以其声明次序被初始化。
四、请记住
- 为内置型对象进行手工初始化,因为C++不保证初始化它们。
- 构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
- 为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象(因为static是内部链接,其它编译单元不可见)