C++構造函數、拷貝構造函數、賦值運算符漫談(一)——函數參數傳遞
在討論C++函數參數之前,我們先來看一下C程序是如何調用函數的。
如圖,為C語言的函數調用記錄,C++也類似。當有如下函數:
void foo(X x0);
如果有如下調用方式:
X xx;
foo(xx);
編譯器(對於C)會將實參xx以“位逐次拷貝”方式複製給形參x0(注:X0即在上圖函數活動記錄中的參數位置)。在C++中,如果一個Class也展現了“位逐次拷貝語義”【1】,且用戶沒有定義拷貝構造函數,那麼編譯器按照這種方式進行拷貝沒有問題。但是當一個Class不展現“位逐次拷貝語義”的情況(四種),或者用戶定義了拷貝構造函數呢?這種情況編譯器必須采用一定措施避免這種直接位拷貝的發生(否則拷貝構造函數將不能發生作用)。C++編譯器是這樣做的:
調用函數前,先引入一個X的臨時變量temp,並以實參xx為參數調用拷貝構造函數(這由編譯器安插代碼完成)。如:
X temp;(此處不調用構造函數,隻分配空間)
temp.X::X(xx);(這兩行代碼是由編譯器插入)
之後怎麼辦呢?如果直接foo(temp),問題又回到之前了,因為隻是相當於temp變成了實參,實參到形參x0的拷貝依然是“位逐次拷貝”。所以C++編譯器必須做第二個轉化,即修改函數foo的聲明,將其修改為:
void foo(X& x0);
即函數參數變為引用,傳入的將是temp的地址。
測試:
class X { public: X() {cout<<"X()"<<endl;}; X(const X& x) { cout<<"X(const X& x)"<<endl; } X& operator=(const X&) { cout<<"="<<endl; } ~X() {cout<<"destructor"<<endl;} }; void foo(X x0) { } int _tmain(int argc, _TCHAR* argv[]) { X xx; foo(xx); }
運行結果:
可以看出函數參數的傳入需要調用拷貝構造函數構建一個臨時對象(注:並不是構建形參,形參已經被轉化為引用,不再是對象),另外臨時對象在函數退出時銷毀。
總結:函數形參中,對象值傳遞的方式都轉化為臨時對象和引用傳遞。
【1】一個類不展現“位逐次拷貝”的四種情況:
(1) 這個類有member object,並且這個member object對應的Class定義了拷貝構造函數。
(2) 這個類的父類定義了拷貝構造函數。
(3) 這個類中有虛函數(包括其父類有的情況)。
(4)這個類的繼承連中有虛基類。
最後更新:2017-04-03 12:55:52