從一道題談C++中構造函數調用構造函數(轉載)

原文鏈接:http://bbs.chinaunix.net/thread-1253107-1-1.html

題目如下:問下列代碼的打印結果爲0嗎?

#include <stdlib.h>
#include 
<iostream>
using namespace std;

struct CLS
{
    
int m_i;
    CLS( 
int i ) : m_i(i){}
    CLS()
    {
        CLS(
0);
    }
};
int main()
{
    CLS obj;
    cout 
<< obj.m_i << endl;

    system(
"PAUSE");
    
return 0;
}
打印結果是不定的,不一定爲0

代碼奇怪的地方在於構造函數中調用了自己的另一個構造函數

我們知道,當定義一個對象時,會按順序做2件事情:
1)分配好內存(非靜態數據成員是未初始化的)
2)調用構造函數(構造函數的本意就是初始化非靜態數據成員)

顯然上面代碼中,CLS obj;這裏已經爲obj分配了內存,然後調用默認構造函數,但是默認構造函數還未執行完,卻調用了另一個構造函數,這樣相當於產生了一個匿名的臨時CLS對象,它調用CLS(int)構造函數,將這個匿名臨時對象自己的數據成員m_i初始化爲0;但是obj的數據成員並沒有得到初始化。於是obj的m_i是未初始化的,因此其值也是不確定的

從這裏,我們歸納如下:
1)在c++裏,由於構造函數允許有默認參數,使得這種構造函數調用構造函數來重用代碼的需求大爲減少
2)如果僅僅爲了一個構造函數重用另一個構造函數的代碼,那麼完全可以把構造函數中的公共部分抽取出來定義一個成員函數(推薦爲private),然後在每個需要這個代碼的構造函數中調用該函數即可
3)偶爾我們還是希望在類的構造函數裏調用另一個構造函數,可以按下面方式做:
在構造函數裏調用另一個構造函數的關鍵是讓第二個構造函數在第一次分配好的內存上執行,而不是分配新的內存,這個可以用標準庫的placement new做到:

    先看看標準庫中placement new的定義

inline void *__cdecl operator new(size_t, void *_P)
{
    
return (_P); 

可見沒有分配新的內存。

正確的方式:

struct CLS
{
    
int m_i;
    CLS( 
int i ) : m_i(i){}
    CLS()
    {
        
new (this)CLS(0);
    }
};

 


另: 若構造函數調用自身,則會出現無限遞歸調用,是不允許


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