读取未初始化的值会导致不明确的行为。
在C++中,规则很简单:确保每一个构造函数都将对象的每一个成员初始化。
class PhoneNumber{...};
class ABEntry{ //ABEntry = "Address Book Entry"
public:
ABEntry(const std::string& name, const std::strings& address,
const std::list<PhoneNumber>& phones);
private:
std::strings theName;
std::string theAddress;
std::list<PhoneNumber> thePhones;
int numTimesConsulted;
};
ABEntry::ABEntry(const std::string& name, const std::strings& address,
const std::list<PhoneNumber>& phones)
{
theName = name; //这些都是赋值,不是初始化
theAddress = address;
thePhone = phones;
numTimesConsulted = 0;
}
这种操作会导致ABEntry对象带有你指定的值,但不是最佳做法。在ABEntry构造函数内,theName,theAddress,thePhones都不是被初始化,而是被赋值。ABEntry构造函数的一个较佳写法是,使用成员列初始列替换赋值动作。
ABEntry::ABEtry(const std::string& name, const std::string& address,
const std::list<PhoneNUmbers>& phones)
:theName(name), //这些都是初始化
theAddress(address),
thePhone(phones),
numTimesConsulted(0)
{}
甚至当你default构造一个成员变量,你都可以使用成员初值列,只要指定无物作为初始化实参即可。
ABEntry::ABEtry()
:theName(),
theAddress(),
thePhone(),
numTimesConsulted(0) //初始化numTimesConsulted
{}
记住一个规则:规定总是在初始值列中列出所有成员变量,以免还得记住哪些成员变量(如果它们在初值列中被遗漏的话)可以无需赋值。
许多class拥有多个构造函数,每个构造函数都有自己的成员初始列。这种情况可以合理的遗漏哪些”赋值和初始化变现一样好”的成员变量,改用它们的赋值操作,并将哪些赋值操作移往某个函数(通常是private),共所有构造函数调用。
C++有着固定的成员初始化次序。基类比派生类先初始化,class的成员变量总是以其声明的顺序被初始化。
所谓static对象,其寿命从构造出来知道程序结束为止。函数内的static对象称为local static对象(因为它们对于函数而言是local),其他static称为non-static对象。程序结束时static对象会自动销毁,也就是它们的析构函数会在main()结束时被自动调用。
当两个源文件都至少含有一个non-static的对象。如果某编译单元内的non-local static对象的初始化使用了么另一个non-local static对象,它所用的这个对象可能尚未被初始化,因为C++对"定义于不同编译单元内的non-static 对象"的初始化次序并无明确定义。
例:
class FileSystem //来自你的程序库
{
public:
...
std::size_t numDisks() const; //众多成员函数之一
...
};
extern FileSystem tfs; //预备给客户使用的对象
class Director //客户的程序库
{
public:
Diretory(params);
...
};
Director::Directory(params)
{
...
std::size_t disks = tfs.numDisks(); //使用tfs对象
...
}
Directory tempDir(params); //为临时文件而做出的目录
除非tfs在tempDir之前被初始化。
解决办法:这个办法的基础在于:C++保证函数local static对象在“该函数调用期间”“首次遇上该对象的定义”时被初始化。
class FileSystem{...}
FileSystem& tfs()
{
static FileSystem fs;
return fs;
}
class Directory{...};
Directory::Directory(params)
{
...
std::size_t disks = tfs().numDisks();
...
}
Directory& tempDis()
{
static Directory td;
return td;
}
结论:为内置类型对象进行手工初始化,因为C++不保证初始化它们。
构造函数最好使用成员初始列,而不要在本体内使用赋值操作。初始列列出的成员变量,其排列次序应该和它们在class声明次序相同。
为避免“跨编译单元初始化次序”问题,请以local static对象替换non-local static 对象。