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

2021-06-18 03:32:22 字數 3225 閱讀 1013

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

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

看這個例子的輸出結果:

constructor with argument//

cexample

aaa(2);

constructor with argument

//cexample

bbb(3)

;assignment operator//

bbb = aaa;

copy constructor//

cexample

ccc = aaa;

copy constructor 

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

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

第乙個輸出: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 

實際上是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;那麼又會觸發拷貝建構函式,就這下永遠的遞迴下去。

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

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

附帶說明,在下面幾種情況下會呼叫拷貝建構函式:

a、   顯式或隱式地用同型別的乙個物件來初始化另外乙個物件。如上例中,用物件c初始化d;

b、  作為實參(argument)傳遞給乙個函式。如cclass(const cclass c_class)中,就會呼叫cclass的拷貝建構函式;

c、  在函式體內返回乙個物件時,也會呼叫返回值型別的拷貝建構函式;

d、  初始化序列容器中的元素時。比如 vectorsvec(5),string的預設建構函式和拷貝建構函式都會被呼叫;

e、  用列表的方式初始化陣列元素時。string a = ; 會呼叫string的拷貝建構函式。

如果在沒有顯式宣告建構函式的情況下,編譯器都會為乙個類合成乙個預設的建構函式。如果在乙個類中宣告了乙個建構函式,那麼就會阻止編譯器為該類合成預設的建構函式。和建構函式不同的是,即便定義了其他建構函式(但沒有定義拷貝建構函式),編譯器總是會為我們合成乙個拷貝建構函式。

另外函式的返回值是不是引用也有很大的區別,返回的不是引用的時候,只是乙個簡單的物件,此時需要呼叫拷貝建構函式,否則,如果是引用的話就不需要呼叫拷貝建構函式。

#includeusing namespace std;

class a

a(const a& other) //建構函式過載

a(a other)

void print()

{ cout《答案:編譯錯誤。在複製建構函式中傳入的引數是a的乙個例項。由於是傳值,把形參拷貝到實參會呼叫複製建構函式。因此如果允許複製建構函式傳值,那麼會形成永無休止的遞迴並造成棧溢位。因此c++的標準不允許複製建構函式傳值引數,而必須是傳引用或者常量引用。在visual studio和gcc中,都將編譯出錯。

拷貝建構函式的引數為什麼必須是引用?

1 include2 include 3using namespace std 4class foo5 13 foo foo f ss f.ss 1417 foo operator const foo f1 1822 void test foo f2 2326 27 28int main 29 如果...

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

在c 中,建構函式,拷貝建構函式,析構函式和賦值函式 賦值運算子過載 是最基本不過的需要掌握的知識。但是如果我問你 拷貝建構函式的引數為什麼必須使用引用型別?這個問題,你會怎麼回答?或許你會回答為了減少一次記憶體拷貝?很慚愧的是,我的第一感覺也是這麼回答。不好還好,我有理性這個好品質。思索一下以後,...

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

在c 中,建構函式,拷貝建構函式,析構函式和賦值函式 賦值運算子過載 是最基本不過的需要掌握的知識。但是如果我問你 拷貝建構函式的引數為什麼必須使用引用型別?這個問題,你會怎麼回答?或許你會回答為了減少一次記憶體拷貝?很慚愧的是,我的第一感覺也是這麼回答。不好還好,我有理性這個好品質。思索一下以後,...