我們知道如果程式設計師不寫建構函式,編譯器會自動提供乙個無參建構函式。這個預設的建構函式在會呼叫成員變數的預設建構函式,從而完成物件的初始化。若要覆蓋預設建構函式,我們只需提供乙個自定義的建構函式即可。
除預設建構函式外,編譯器還會提供另一種建構函式:拷貝建構函式。當需要根據乙個已有的物件來構造另乙個物件時,拷貝建構函式就會被呼叫。例如我們有乙個包含字串的類,mystring:
[cpp]view plain
copy
print?
class mystring
;
在mystring這個例子中,我們假定建構函式會為字串分配空間,析構函式則會釋放這些空間。
在像下面這樣建立乙個新的mystring物件時,拷貝建構函式會被呼叫:
[cpp]view plain
copy
print?
mystring me("geraldo");
mystring clone = me;
// copy constructor gets called.
拷貝建構函式在以傳值的方式進行引數傳遞時或以傳值方式返回乙個值時會被呼叫。現在有下面這個openfile方法:
[cpp]view plain
copy
print?
static
void openfile(mystring filename)
我們可以這樣呼叫這個方法:
[cpp]view plain
copy
print?
mystring name(「flights.txt」);
openfile(name);
在將name物件作為引數進行傳遞時,拷貝建構函式會被呼叫,來根據name來生成乙個openfile中的mystring型別引數物件。因為我們沒有在mystring中提供乙個拷貝建構函式,因此預設拷貝建構函式會被呼叫。預設的拷貝建構函式是通過對每個字段進行拷貝實現的。在mystring中,預設拷貝建構函式會拷貝int型的length欄位和char*型的str指標。這種方式只會實現指標的複製,指標指向的字串記憶體並不會被複製,只是多了乙個指向這塊記憶體的指標而已。這種方式叫做shallow copy,記憶體的實際布局如下圖所示:
這種方式存在共享記憶體的問題。如果其中的任乙個物件修改了這段記憶體的內容,那麼另乙個物件將在不知情的情況下受到影響。更糟糕的是,如果乙個物件呼叫析構函式,那麼這段記憶體將被**,另乙個物件的指標就成了「髒指標」。
避免shallow copy的負面影響的乙個有效方法是使用deep copy。使用這種方式,我們拷貝的不僅僅是指向字串的指標,字串本身也會被拷貝,其效果如下圖所示:
為了給mystring提供乙個deep copy版本的拷貝建構函式,我們需要自己去宣告自己的拷貝建構函式。在拷貝建構函式中,引數型別為指向同類物件的引用,並且沒有返回值型別。例如在mystring中,我們將這樣宣告拷貝建構函式:
[cpp]view plain
copy
print?
class mystring
;
其實現會類似於這樣:
[cpp]view plain
copy
print?
mystring::mystring(const mystring& s)
在進行物件間的賦值操作時,拷貝建構函式不會被呼叫。例如在下面的例子中,儘管我們提供了deep copy的拷貝建構函式,但在賦值操作中這個拷貝建構函式不會被呼叫,使用的仍是shallow copy。
[cpp]view plain
copy
print?
mystring betty(「betty rubble」); // initializes string to 「betty rubble」
mystring bettyclone; // initializes string to empty string
bettyclone = betty;
這是因為在這裡,呼叫的是「=」運算子而不是拷貝建構函式。「=」運算子預設情況下使用的是shallow copy,不過c++中有運算子過載機制,我們可以將「=」過載為使用deep copy的賦值運算子。
這裡順便提一下,如果你不想其他人通過拷貝建構函式的方式來拷貝你的物件,那麼你可以將拷貝建構函式宣告為private的(從而使預設拷貝建構函式失效),而且不去實現這個函式,那麼編譯器將阻止對這個物件的使用傳值的方式進行傳參等需要使用拷貝建構函式的地方。=
我們可以對「=」進行運算子過載。賦值運算子必須被定義為乙個成員函式,從而保證運算子左邊為乙個物件。每個類中都定義了「=」運算子,預設的「=」運算子使用的是shallow copy的方式。你可以將「=」宣告為private並不提供實現,從而阻止物件到物件間的賦值操作。
「=」運算子與拷貝建構函式的乙個重要區別在於「=」運算子會釋放已有的一些資源,如下面的**所示:
[cpp]view plain
copy
print?
const mystring& operator=(const mystring& rhs)
return *this; // return self-reference so cascaded assignment works
}
1. 當新物件還不存在,需要呼叫建構函式時,將使用拷貝建構函式,包括:
傳值傳參
傳值返回值
使用物件初始化
2. 當新物件已存在,僅僅是物件與物件間的賦值操作時,使用「=」運算子:
物件 -- 物件間的賦值
3. 為什麼「=」運算子不會自動呼叫拷貝建構函式:
我認為這是由拷貝建構函式的性質決定的。拷貝建構函式本質上是乙個建構函式,只有在可以使用建構函式的時候才能被呼叫。
C 拷貝建構函式及重寫operator 的區別
先來 include using namespace std class ca ca const ca c 拷貝建構函式 必須傳引用,如果傳值,就會要求構造臨時物件,需要呼叫拷貝構造,又傳值,又呼叫拷貝構造。ca operator const ca c 重寫operator void show in...
拷貝建構函式,預設拷貝建構函式
拷貝建構函式,預設拷貝建構函式 1.c 的預設拷貝建構函式,從深度拷貝和淺拷貝說起 c 類的預設拷貝建構函式的弊端 c 類的中有兩個特殊的建構函式,1 無參建構函式,2 拷貝建構函式。它們的特殊之處在於 1 當類中沒有定義任何建構函式時,編譯器會預設提供乙個無參建構函式且其函式體為空 2 當類中沒有...
建構函式 拷貝建構函式
建構函式可以分為三類 1 不帶引數的建構函式 在函式體中對資料成員賦初值,這種方式使該類的每乙個物件都得到同一組初值 2 帶引數的建構函式 如果使用者希望對不同的物件賦不同的初值,可以採用帶引數的建構函式。在呼叫不同物件的建構函式時從外面將不同的資料傳遞給建構函式,以實現初始化 3 複製建構函式 建...