Zero-Initialize
初始化(Initialize)的動作雖不起眼,但有它的重要性。
zero-initialize(0做初始化)並不是在宣告時必做的事情,可以使用有意義的值去初始化物件,此篇文章
只是介紹zero-initialize的方法,請記得並沒有硬性規定要zero-initialize,當存在有意義的初始值時,則
就可以利用有意義的初始值去初始化,若沒有實際意義的初始化的值時,可以建議以0做初始化。

先來討論基本的(fundamental)型態(char,int,float,double,bool),怎麼以零初始化這些型態呢?
如下:
	char ch = 0;
	int iNum = 0;
	float fNum = 0.0f;
	double dNum = 0.0;
	bool bFlag = 0;
0是一個通用的初值設定(initializer,或翻作初始式)0會轉型(cast)成所初始化的型態,
是一個很特別的值(special value),在C++中有另外一種寫法,但效果是一樣的:
	char ch = char();
	int iNum = int();
	float fNum = float();
	double dNum = double();
	bool bFlag = bool();
	
這種方式並沒有什麼,有看過一次你就懂了。對於指標型態的變數是否適用呢?
	char *pCh = 0;
	int *pi = 0;
	float *pf = 0;
	double *pd = 0;
C/C++的定義中,聲明指標型態中有一個特別的值"null pointer",用來與其它指標的值(物件的位址或函式的位址)
做區別,"null pointer"是指沒有指向任何的物件,但它與未初化的(uninitialized)指標不同,未初始化的指標可能指
向任何的位址。在C語言中,指標型態的變數若卻初始化成"null pointer",建議以NULL來初始化,而C++語言中,則
建議以0來做初始化。在C++NULL的定義是0,可以在標題檔中找到它的定義:
	
	#ifndef NULL
	#ifdef  __cplusplus
	#define NULL    0
	#else
	#define NULL    ((void *)0)	
	#endif
	#endif
C++中值接使用0能避免macro的呼叫,0NULLC++是一樣的,但C++之父建議使用0,詳情請參考0 and NULL
除了指標型態的變數,也包括指標變數哦!所以可以使用0來初始化。
對基本型態而言,如果以動態配置的方式(dynamic allocate)產生出的記憶體,要如何以0初始化呢?
Ex:
	C語言:
		char* dyMem = (char *)malloc(sizeof(char));
		if( dyMem == NULL )	//動態配置失敗,會傳回NULL
		{
			printf( "Dynamic allocated failed! "); 
			......	//處理動態配置失敗
		}
		else
		{
			*dyMem = 0;	//設為0
		}
	or
		char* dyMem = NULL ;	//初始化為"null pointer"
		if( (dyMem = (char *)malloc(sizeof(char)))==NULL )//動態配置失敗,會傳回NULL
		{
			printf( "Dynamic allocated failed! "); 
			......	//處理動態配置失敗
		}
		else
		{
			*dyMem = 0;	//設為0
		}
	or
		char* dyMem = (char *)calloc(1,sizeof(char));
		if( dyMem == NULL )	//動態配置失敗,會傳回NULL
		{
			printf( "Dynamic allocated failed! "); 
			......	//處理動態配置失敗
		}
		
	C++語言:
		char* dyMem = new char(0);	//動態配置並初始化為0
		if( dyMem == 0 )	//動態配置失敗,會傳回0
		{
			cout << "Dynamic allocated failed! " ;
			......	//處理動態配置失敗
		}
	or	
		char* dyMem = 0;	//初始化為"null pointer"
		if( (dyMem = new char(0)) == 0 ) //動態配置失敗,會傳回0
		{
			cout << "Dynamic allocated failed! " ;
			......	//處理動態配置失敗
		}
C語言當中是無法利用malloc()在動態配置的動作下初始化記憶體空間,而必須自己加上設定為0的動作,
若不加的話,記憶體的內容是前次程式所殘留下來的無效值。而可以利用calloc()來配置出每個bit都是0的記憶
體空間,所以就不必做zero-intializeC++中可以在動態配置的動作下初始化單一元素的記憶體空間,不須自己加上去。
C++C動態配置的差別在於new會呼叫建構子而malloc/calloc不會呼叫建構子,請不要混用。

之前介紹是單一的變數,若是同一型態所構成的陣列,能不能以0初始化呢?
	char chArray[10] = { 0 };
	int iArray[10] = { 0 };
	float fArray[10] = { 0.0f };
	double dArray[10] = { 0.0 };
上面的陣列都有10個元素(element),每個元素的型態都是一樣的,只有single element initializer(單一元素初值設定)
,對陣列[0]zero做初始化,雖只有單一元素初值設定,不過剩餘的9個元素依然會被隱含的初始化為zero,有點不清楚
沒有關係,以下是怎麼初始化呢?

	int iArray[10] = { 1,2,3,4 };

iArray[0]會初始化為1
iArray[1]會初始化為2
iArray[2]會初始化為3
iArray[3]會初始化為4
其餘iArray[4] ~ iArray[9] 會被隱含的初始化為0。

註:隱含的是意思就是說無法從程式碼中看到元素初始化的動作,而是電腦加上去的。

若以動態配置的方式所產生的陣列,要如何初始化呢?不管是在CC++動態配置的方式都無法同時初始化值,必
須自己加上去這個動作。當然可以使用迴圈的方式一個一個是設定為0,但有另外的選擇,可以使用memset函數來
處理,它的函式宣告是這樣的:
	void *  memset ( void * buffer, int c, size_t num ); 
byte的方式,從buffer所指向的位置,填補num個字元c,如:
	
C語言:
	#define	N 10	 //N表示動態配置出的記憶體大小
	int* dyMem = (int *)malloc(sizeof(int)*N);
	memset(dyMem, 0, sizeof(int)*N);
or
	#define N 10
	int *dyMem = (int *)calloc(N,sizeof(int));
C++:
	const int N = 5;	//N表示動態配置出的記憶體大小
	int* dyMem = new int[5];
	memset(dyMem, 0, sizeof(int)*N);
	

接下來要介紹,不同資料型態所構成的型態,稱為Aggregate type。有structclassunionarray什麼是Aggregate type呢?節錄至C++ IOS/IEC section 8.5.1 

Aggregate是一個陣列(array)或類別(class)/結構(struct),而類別(class)/結構(struct)的條件有四:
	
   •沒有使用者所宣告的建構子(no user-declared constructors)
    •沒有私有或保護等級的非靜態資料成員(no private or protected non-static data member)
    •沒有基礎類別(no base classes)
    •沒有虛擬函數(no virtual functions.)   	

Note:一個Aggregate陣列(array)或類別(class)/結構(struct)可能含有一個類別(class)/結構(struct)型態的成員
,成員具有使用者所宣告的建構子。所以具有使用者所宣告建構子的資料成員,是aggregate type
請問一下何謂使用者宣告的建構子呢?其實就是非編譯器所產生的建構子,在C++ ISO/IEC section 12.1 5 paragraph
中是這樣描述的:
如果類別中沒有任何使用者所宣告的建構子,就會有一個預設建構子被編譯器暗中的宣告出來,這一個被編譯器隱含
出來的預設建構子是一個沒有用處(trivial)的建構子。
作者認為,編譯器所產生的建構子被稱為non-user-declared constructors(非使用者宣告的建構子)。

另外 section 8.5.1 Aggregates 中第14 paragraph說明有關static aggregate物件初始化的方式,這與另一篇文章
compile-time最後所描述的內容有關,可以參考看看。 

清楚何謂Aggregate type之後,Aggregate type可分為POD Aggregate typenon-POD Aggregate type
POD Aggregate type主要限制是不能擁有下列的描述:
non-static data (including arrays) of any pointer-to-member type
non-static data (including arrays) of any non-POD class typenon-static data of any reference typeuser-defined copy assignment operator
user-defined destructor
處處是定義,僅做參考。

註:使用編譯器是VC6.0,這是一個與C++ Standard不完全相容的的編譯器
所以很多的VC6.0並無法完全與標準相容,所以在編譯有關C++標準的程式,可能會出現問題。
但在Visual C++ 7.1 Version已經十分相容與C++ standard,但仍有出入,可以在MSDN中
查到出入與支授的部份:
Standard Compliance Issues in Visual C++
另外有一篇C++ Primer作者Stanley Lippman受訪問的文章,裡面有說到VC++的過去、現在與未來改良方向:對新任微軟Visual C++架構設計師的採訪
開始介紹如何以0來初始化POD Aggregate struct,如下:
	struct student
	{
		unsigned int iNum;
		char* chName;
	};
	...
	void main()
	{
		struct student stu1 = { 0 };
		...
	}  
iNum會被{ 0 }中的0初始化為0,而chName則是暗中被初始化為0,這道理與陣列是一樣的。
在C++中宣告式中的struct可以不寫,但C語言中不能省略。Aggregate type初始化的方式須使用"{ initializer-list }"
,但若是以相同Aggregate型態的物件來初始化時,則可以使用"( )"的方式來初始化,為什麼呢?明明Aggregate type
中不沒有constructors(包括copy constructorno-argument constructor),那是因為compiler會有預設的copy 
constructorno-argument constructor,預設的copy constructor會以memberwise的方式將用來初始化物件內的值
逐一複製到被初始化的物件中,而no-argument constructor是一個trivial的建構子,來看看例子:
	struct student
	{
        		int iNum;
		char sex;
	};	
	                                                                                
	int main()
	{
		struct student s1 = { 10 , 'M' };
		struct student s2(s1);
		struct student s3;

		cout<<s1.iNum<<" "<<s1.sex<<endl
		      <<s2.iNum<<" "<<s2.sex<<endl
		      <<s3.iNum<<" "<<s3.sex<<endl;
		return 0;
	}
s2就是呼叫compiler所產生的預設的copy constructor
s3就是呼叫compiler所產生的預設的no-argument constructor,但這個constructortrivial,沒有任何的作用產生
所以s1印出來應該是10 Ms2也是一樣,而s3印出來無效的值。清楚了吧!!
若動態配置的方式與陣列一樣,舉一個C++的例子:
	struct student
	{
		int iNum;
		char sex;
	};
                                                                                
	int main()
	{
		const int N = 5;
		student *pStu = new student[N];
		memset(pStu,0,sizeof(student)*N);
		return 0;
	}
注意請不要將sizeof(student)改為(sizeof(int)+sizeof(char)),兩個在一般情況下是不一樣的。
因為有struct member alignment的問題,這與記憶體存取的效率有關,暫不討論,請參考相關的文章。
介紹完由基本型態所型態的POD Aggregate type之後,來簡單介紹由有使用者所宣告建構子的資料成員所組成的
POD Aggregate type,如下:
	#include <iostream>
	#include <string>
	using namespace std;

	struct student
	{
		int iNum;
		string Name;
	};

	void main()
	{
	    student st1 = { 0 };
	}

使用VC6.0編譯的結果:
error C2552: 'st1' : non-aggregates cannot be initialized with initializer list
error C2552 MSDN中有說明原因:
Compiler Error C2552
其中有一句話
《In addition, Visual C++ does not allow data types in an aggregate that themselves contain constructors.(譯:除此之外,Visual C++6.0不允許aggregate中的資料型態含有建構子)
所以在VC6.0中,禁止使用含有建構子的型態作為aggregate type的資料成員,但在Visual C++ .Net 2003中,
已支援此項目,參考這裡:
Support Aggregate Initialization
如果是使用G++編譯器的話,並不會產生如何的問題,可以放心使用。
為什麼會照成這樣的結果呢?因為就在於每個編譯器符合標準C++的程度不同。
但Visual C++已經在往這方面改進了。可以參考之前所介紹的文章對新任微軟Visual C++架構設計師的採訪
POD Aggregate type大致介紹到此,而有關non-POD Aggregate type,這需要搭配到constructor,可以
參考其它的文章。
回目錄
Written By James On 2004/02/08 

Hosted by www.Geocities.ws

1