1. 程式人生 > >拷貝建構函式的型別為什麼必須使用引用型別

拷貝建構函式的型別為什麼必須使用引用型別

有的看著理所當然的東西往往被我們忽略其深層的意義,就像c++ 拷貝建構函式的引數為什麼必須是引用型別呢,許多初學者都會說,“為了減少一次記憶體複製唄!”(其實剛開始我也是這樣想的),但究竟是不是這樣呢?來,看個小例子(你一定會說:昂....原來是這樣啊!);

#include<iostream>
using namespace std;
class myExample
{
    int mTest;
public:
    myExample(int x):mTest(x){ //帶引數的建構函式
        cout << "我是建構函式!"<<endl;
    }
    myExample(const myExample &ex){  //拷貝建構函式
        mTest=ex.mTest;
        cout <<"我是拷貝建構函式!"<<endl;
    }
    myExample& operator=(const myExample &ex){ //賦值函式(賦值運算子過載)
        cout << "賦值運算子過載" <<endl;
        mTest=ex.mTest;
        return *this;
    }
    void myTestfun(myExample ex){

    }
};

int main(int argc,char *argv[])
{
    myExample a(2);
    myExample b(3);
    b=a;
    myExample c=a;
    b.myTestfun(a);
    return 0;
}

看結果:

我是建構函式!                 //myExample a(2);    
我是建構函式!                 //myExample b(3);
賦值運算子過載                 //b=a;
我是拷貝建構函式!               //myExample c=a;
我是拷貝建構函式!               //b.myTestfun(a);

如果你想的結果和上面的一樣,恭喜你,你不用往下看了.  

我們來分析一下: 

第一個輸出和第二個輸出:呼叫建構函式,就不用解釋了吧!

第三個和第四個為什麼不一樣呢?大家仔細看,b已經被例項化了,不需要構造,在這裡,只是把a的值賦給它,只會呼叫賦值函式,而第四個c還沒有被例項化,因此呼叫拷貝建構函式,構造處c,而不是賦值,

第五個實際上是把a作為引數傳遞給myTestfun(myExample ex),就相當於myExample ex=a;所以呼叫拷貝建構函式.  

通過這個例子,我們來分析一下,拷貝建構函式為什麼必須用引用型別,myExanple c=a;當我們不用引用作為拷貝建構函式的形參時,使得a通過值傳遞傳遞給c,因為要構造物件, 又會呼叫拷貝建構函式,就這樣一直遞迴呼叫遞迴下去了.

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

大家在想想,既然能設計為引用型別呢,那也就能設計成指標型別啊, 那為什麼不呢,唯一區別點就在於效率吧!

如果形參是指標型別的,從編譯的角度看:

   程式在編譯時分別將指標和引用新增到符號表上, 符號表上記錄的是變數名及變數所對應的地址,指標在符號表上對應的地址是指標變數的地址值,指標變數中存的才是對應指向物件的地址值,而引用在符號上對應的地址值為引用的對應的地址值.在這裡, 指標就像中介,我們租房子跟主家談和跟中介談,哪個划算,大家應該清楚吧!