Effective C++读书笔记之四:确定对象被使用前已先被初始化

Item 04:Make sure that opjects are initialized before they‘re used

关于“将对象初始化”这事,C++似乎反复无常。如果你这么写:

int x;

在某些语境下x保证被初始化(为0),但在其他语境中却不保证。如果你这么写:

class Point{
int x,y;
};
...
Point p;

p的成员变量有时被初始化,有时候不会。

通常如果你使用C part of C++而且初始化可能招致运行期成本,那么就不保证发生初始化。一旦进入non-C part of C++,规则有些变化。这就很好地解释了为什么array(来自C part of C++)不保证其内容被初始化,而vector(来自STL part of C++)却有此保证。

表面上这似乎是个无法决定的状态,而最佳的处理方法就是:永远在使用对象之前先将它初始化。例如:

int x = 0;
const char* text = “A C-style string”;

double d;
std::cin>>d; //以读取的方式完成初始化

至于内置类型以外的任何其他东西,初始化责任则落在构造函数身上。规则很简单:确保每一个构造函数都将每一个成员初始化。规则很容易奉行,但是重要的是别混淆了赋值和初始化。如:

clall PhoneNumber{...};
class ABEntry
{
public:
	ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones);
private:
	std::string theName;
	std::string theAddress;
	std::list<PhoneNumber> thePhones;
	int numTimesConsulted;
};
ABEntry::ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones)
{
	theName=name;
	theAddress=address;
	thePhones=phones;
	numTimesConsulted=0;//这些都是赋值,不是初始化 
}

C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。ABEntry构造函数的一个较佳写法是,使用成员初值列来替换赋值动作

ABEntry::ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones)
:theName(name);
theAddress(address);//现在,这些都是初始化 
thePhones(phones);
numTimesConsulted(0)
{}//现在,构造函数本体不必有任何动作 

对大多数类型而言,比起先调用defult构造函数然后再调用copy assignment操作符,单只调用一次copy构造函数式比较高效的。

许多classes拥有多个构造函数,每个构造函数有自己的成员初值列。如果这种classes存在许多成员变量/或base classes,多份成员初值列的存在就会导致不受欢迎的重复和无聊的工作。这种情况下可以合理地在初值列中遗漏那些“赋值表现像初始化一样好”的成员变量,改用它们的赋值操作,并将那些赋值操作移往某个函数,供所有构造函数调用。

函数内的static对象称为local static对象(因为它们对函数而言是local),其他static对象称为non-local static对象。

由于C++对“定义于不同的编译单元的non-local static”对象的初始化相对次序并无明确定义,因此可能会产生这样一个问题:如果某编译单元内的某个non-local static对象的初始化动作使用了另一编译单元的某个non-local static对象,它所用到的这个对象可能尚未初始化。请看如下代码:

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

class Directory
{
public:
	Directory(params);
	...
};
Directory::Directory(params)
{
	...
	std::size_t disks=tfs.numDisks();//使用tfs对象
	... 
};

Directory tempDir(params);

现在,问题出来了,除非tfs在tempDir之前先被初始化,否则tempDir的构造函数会用到未被初始化的tfs。

使用Singleton模式可以解决这个问题。

class FileSystem{...};//同前
FileSystem& tfs() //这个函数用来替换tfs对象
{
	static FileSystem fs;
	return fs;
} 
class Directory{...};//同前
Directory::Directory(params)
{
	...
	std::size_t disks = tfs().numDisks();
	...
} 
Directory& tempDir()//这个函数用来替换temoDir对象 
{
	static Directory td;
	return td;
}

请记住:
1.为内置型对象进行手工初始化,因为C++不保证初始化它们。
2.构造函数最好使用成员初值列,而不要在构造函数本体使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
3.为免除“跨编译单元之初始次序:问题,请以local static对象替换non-local static 对象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章