閱讀455 返回首頁    go 阿裏雲 go 技術社區[雲棲]


C++構造函數、拷貝構造函數、賦值運算符漫談(一)——函數參數傳遞

在討論C++函數參數之前,我們先來看一下C程序是如何調用函數的。

 

如圖,為C語言的函數調用記錄,C++也類似。當有如下函數:

void fooX 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

  上一篇:go ant命令征集詳解
  下一篇:go 關於大數據時代傳統商業存儲的思考: 中心存儲 VS 分布式存儲