1. 程式人生 > >物件作為引數和返回值 複製(拷貝)建構函式

物件作為引數和返回值 複製(拷貝)建構函式

先看一道搜狗的校園招聘題:  不考慮任何編譯器優化(如:NRVO),下述程式碼的第10行會發生

#include <stdio.h>//1
class B//2
{//3
};//4
B func(const B& rhs){//5
  return rhs;//6
}//7
int main(int argc,char **argv){//8
  B b1,b2;//9
  b2=func(b1);//10
}//11

答案是 :一次拷貝建構函式,一次解構函式,一次賦值運算子。  為了說明這個問題,我們把程式碼改寫一下,如下

#include<iostream>
#include<stdio.h>

using namespace std;

class B
{
public:
    B()
    {
        cout<<"建構函式"<<endl;
    }
    ~B()
    {
        cout<<"解構函式"<<endl;
    }

    B(const B& b)
    {
        cout<<"拷貝建構函式"<<endl;
    }
    B& operator=(const B& b)
    {
        cout<<"賦值建構函式"<<endl;
    }
};

B func1(B& b)
{
    return b;
}

B func2(B b)
{
    return b;
}

B& func3(B b)
{
    return b;
}

int main()
{
    B b1,b2;
    cout<<endl;
    cout<<"以下是func1的結果:"<<endl;
    b2 = func1(b1);

    cout<<endl;
    cout<<"以下是func2的結果:"<<endl;
    b2 = func2(b1);
    cout<<endl;

    cout<<endl;
    cout<<"以下是func3的結果:"<<endl;
    b2 = func3(b1);
    cout<<endl;

    return 0;
}

結果如下:這裡寫圖片描述 主要考察的是賦值建構函式的用法,使用賦值建構函式有3中情況: 1 明確表示由一個物件初始化另一個物件時: B b1; B b2(b1);

2 當物件作為函式實參傳遞給函式形參時,會呼叫拷貝建構函式,生成一個無名的區域性物件,但如果引數是引用或者是指標,則不用呼叫拷貝建構函式: func2(b1)

3 當一個自動儲存類物件作為函式返回值時,會呼叫拷貝建構函式生成一個無名的物件,返回給函式,,但如果返回值型別是引用或者指標,則 不呼叫: return b;

對於func1, 引數是引用型別,不呼叫建構函式,不生成臨時物件 在返回物件時,首先要用拷貝建構函式生成一個無名物件並返回給呼叫函式,所以有一次拷貝建構函式, 接著執行賦值操作,呼叫賦值運算子, 此時,func1執行結束,無名的物件被釋放,呼叫一次解構函式。

對於func2, 傳遞引數時呼叫一次拷貝建構函式,生成一個無名物件, 返回時,呼叫一次拷貝建構函式,又生成一個無名物件, 接著是賦值運算子,呼叫賦值運算子 func2結束,釋放無名物件,因為有兩個,所以呼叫兩次解構函式。

對於func3: 傳遞引數時呼叫一次拷貝建構函式,生成一個無名物件, 返回時,因為是引用,所以不呼叫,也不生成物件 接著是賦值運算子,呼叫賦值運算子 func3結束,釋放無名物件,因為只有一個,所以呼叫一次解構函式

注意:在func3中,有些編譯器可能編譯出錯。因為無名變數是區域性變數,在函式執行結束時就會釋放,引用這樣的變數會導致程式出現錯誤。