1. 程式人生 > >C++拷貝建構函式的引數為什麼必須使用引用型別(無限遞迴)

C++拷貝建構函式的引數為什麼必須使用引用型別(無限遞迴)



我轉載這個主要是因為這個風趣幽默哈。。

 在C++中, 建構函式,拷貝建構函式,解構函式和賦值函式(賦值運算子過載)是最基本不過的需要掌握的知識。 但是如果我問你“拷貝建構函式的引數為什麼必須使用引用型別?”這個問題, 你會怎麼回答? 或許你會回答為了減少一次記憶體拷貝? 很慚愧的是,我的第一感覺也是這麼回答。不好還好,我有理性這個好品質。思索一下以後,發現這個答案是不對的。讓我來撕開(有點暴力,但是我喜歡,嘿嘿--齜牙)那件隱藏在真理外的小褲衩,讓它袒露在“登徒子”們的眼前吧。

先從一個小例子開始:(自己測試一下自己看看這個程式的輸出是什麼?)

#include <iostream.h>   
  
class CExample  
{  
    int m_nTest;  
public:  
      
    CExample(int x):m_nTest(x) //帶引數建構函式   
    {   
       cout << "constructor with argument/n";  
    }  
      
   CExample(const CExample & ex) //拷貝建構函式   
    {  
        m_nTest = ex.m_nTest;  
        cout << "copy constructor/n";  
    }  
      
    CExample& operator = (const CExample &ex)//賦值函式(賦值運算子過載)   
    {     
        cout << "assignment operator/n";  
        m_nTest = ex.m_nTest;  
        return *this;       
     } 
    void myTestFunc(CExample ex)  
    {  
    }  
};  
 
int main()  
{  
    CExample aaa(2);  
    CExample bbb(3);  
    bbb = aaa;  
    CExample ccc = aaa;  
    bbb.myTestFunc(aaa);  
          return 0;     
}  

看這個例子的輸出結果:

constructor with argument// CExample aaa(2);
constructor with argument// CExample bbb(3);
assignment operator 
               // bbb = aaa;
copy constructor                      // CExample ccc = aaa;
copy constructor                      //  bbb.myTestFunc(aaa);

如果你能一眼看出就是這個結果的話, 恭喜你,可以站起來扭扭屁股,不用再往下看了。

如果你的結果和輸出結果有誤差, 那拜託你謙虛的看完。

第一個輸出: constructor with argument// CExample aaa(2);

如果你不理解的話, 找個人把你拖出去痛打一頓,然後嘴裡還喊著“我是二師兄,我是二師兄.......”

第二個輸出:constructor with argument// CExample bbb(3);

分析同第一個

第三個輸出: assignment operator                // bbb = aaa;

第四個輸出: copy constructor                      // CExample

ccc = aaa;

這兩個得放到一塊說。 肯定會有人問為什麼兩個不一致。原因是, bbb物件已經例項化了,不需要構造,此時只是將aaa賦值給bbb,只會呼叫賦值函式,就這麼簡單,還不懂的話,撞牆去! 但是ccc還沒有例項化,因此呼叫的是拷貝建構函式,構造出ccc,而不是賦值函式,還不懂的話,我撞牆去!!

第五個輸出: copy constructor                      //  bbb.myTestFunc(aaa);

實際上是aaa作為引數傳遞給bbb.myTestFunc(CExample ex), 即CExample ex = aaa;和第四個一致的, 所以還是拷貝建構函式,而不是賦值函式, 如果仍然不懂, 我的頭剛才已經流血了,不要再讓我撞了,你就自己使勁的再裝一次吧。

通過這個例子, 我們來分析一下為什麼拷貝建構函式的引數只能使用引用型別。

看第四個輸出: copy constructor                      // CExample ccc = aaa;

構造ccc,實質上是ccc.CExample(aaa); 我們假如拷貝建構函式引數不是引用型別的話, 那麼將使得 ccc.CExample(aaa)變成aaa傳值給ccc.CExample(CExample ex),即CExample ex = aaa,因為 ex 沒有被初始化, 所以 CExample ex = aaa 繼續呼叫拷貝建構函式,接下來的是構造ex,也就是 ex.CExample(aaa),必然又會有aaa傳給CExample(CExample ex), 即 CExample ex = aaa;那麼又會觸發拷貝建構函式,就這下永遠的遞迴下去。

所以繞了那麼大的彎子,就是想說明拷貝建構函式的引數使用引用型別不是為了減少一次記憶體拷貝, 而是避免拷貝建構函式無限制的遞迴下去。

所以, 拷貝建構函式是必須要帶引用型別的引數的, 而且這也是編譯器強制性要求的