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 對象

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