1. 程式人生 > >c++類物件初始化方式總結

c++類物件初始化方式總結

在《inside the c++ object model》一書中談到copy constructor的構造操作,有三種情況下,會以一個object的內容作為另一個object的初值:

第一種情況: XX aa = a;
第二種情況: XX aa(a);
第三種情況: extern fun(XX aa); fun(a)函式呼叫
第四種情況: XX fun(){...}; XX a = fun();函式返回值的時候

下面我們就上述的四種情況來一一驗證:
class ClassTest{
public:
	ClassTest()//定義預設建構函式
	{
		c[0] = '\0';
		cout << "ClassTest()" << endl;
	}
	ClassTest& operator=(const ClassTest &ct) //過載賦值操作符
	{
		strcpy_s(c, ct.c);
		cout << "ClassTest& operator=(const ClassTest &ct)" << endl;
		return *this;
	}
	ClassTest(const char *pc)
	{
		strcpy_s(c, pc);
		cout << "ClassTest (const char *pc)" << endl;
	}  
	ClassTest(const ClassTest& ct)//複製建構函式
	{
		strcpy_s(c, ct.c);
		cout << "ClassTest(const ClassTest& ct)" << endl;
	}
private:
	char c[256];
};

ClassTest func(ClassTest temp){
	return temp;
}

int main(){
	cout << "ct1: ";
	ClassTest ct1("ab");//直接初始化  
	cout << "ct2: ";
	ClassTest ct2 = "ab";//複製初始化  
	/*輸出說明:
	ClassTest ct2 = "ab";
	它本來是要這樣來構造物件的:首先呼叫建構函式ClassTest(const char *pc)函式建立一個臨時物件,
	然後呼叫複製建構函式,把這個臨時物件作為引數,構造物件ct2。然而編譯也發現,複製建構函式是
	公有的,即你明確地告訴了編譯器,你允許物件之間的複製,而且此時它發現可以通過直接呼叫過載的
	建構函式ClassTest(const char *pc)來直接初始化物件,而達到相同的效果,所以就把這條語句優化為
	ClassTest ct2("ab")。
	*/
	cout << "ct3: ";
	ClassTest ct3 = ct1;//複製初始化  
	cout << "ct4: ";
	ClassTest ct4(ct1);//直接初始化  
	cout << "ct5: ";
	ClassTest ct5 = ClassTest();//複製初始化 
	cout << "ct6: ";
	ClassTest ct6;//複製初始化
	ct6 = "caoyan is a good boy!";
	cout << "ct7: ";
	ClassTest ct7;
	ct7 = func(ct6);
	return 0;
}
測試結果:

我們可以看到,比較複雜的是ct6和ct7,其中ct6還是比較好理解的,ct7這種情況比較難懂,為什麼會有兩個拷貝建構函式的呼叫????

第一次拷貝建構函式的呼叫:第一次很簡單,是因為函式引數的傳遞,將ct6作為引數傳遞給temp,用ct6的值初始化temp會呼叫拷貝建構函式;

第二次拷貝建構函式的呼叫:因為要返回一個ClassTest物件,我們的編譯器怎麼做????首先它將temp物件拷貝到func函式的上一級棧幀中,它的上一級棧幀是main函式的棧幀,那麼當函式返回時,引數出棧,temp物件的記憶體空間就會被收回,但是它的值已經被拷貝到main棧幀的一個預留空間中,所以從temp到預留空間的拷貝也是呼叫拷貝建構函式,最後一步就是給ct7賦值,毫無疑問呼叫賦值建構函式;對棧幀不同的同學可以看看《程式設計師的自我修養》一書,裡面講得很詳細!!!!