c++中物件的複製就如同「轉殖」,用乙個已有的物件快速地複製出多個完全相同的物件。一般而言,以下三種情況都會使用到物件的複製:
(1)建立乙個新物件,並用另乙個同類的已有物件對新物件進行初始化,例如:
class rect
; rect rect1;
rect rect2(rect1); // 使用rect1初始化rect2,此時會進行物件的複製
(2)當函式的引數為類的物件時,這時呼叫此函式時使用的是值傳遞,也會產生物件的複製,例如:
void fun1(rect rect)
int main()
(3)函式的返回值是類的物件時,在函式呼叫結束時,需要將函式中的物件複製乙個臨時物件並傳給改函式的呼叫處,例如:
rect fun2()
int main()
物件的複製都是通過一種特殊的建構函式來完成的,這種特殊的建構函式就是拷貝建構函式(copy constructor,也叫複製建構函式)。拷貝建構函式在大多數情況下都很簡單,甚至在我們都不知道它存在的情況下也能很好發揮作用,但是在一些特殊情況下,特別是在物件裡有動態成員的時候,就需要我們特別小心地處理拷貝建構函式了。下面我們就來看看拷貝建構函式的使用。
一、預設拷貝建構函式
很多時候在我們都不知道拷貝建構函式的情況下,傳遞物件給函式引數或者函式返回物件都能很好的進行,這是因為編譯器會給我們自動產生乙個拷貝建構函式,這就是「預設拷貝建構函式」,這個建構函式很簡單,僅僅使用「老物件」的資料成員的值對「新物件」的資料成員一一進行賦值,它一般具有以下形式:
rect::rect(const rect& r)
class rect
~rect() // 析構函式,計數器減1
static int getcount() // 返回計數器的值
private:
int width;
int height;
static int count; // 一靜態成員做為計數器
};
int rect::count = 0; // 初始化計數器
int main()
rect(const rect& r) // 拷貝建構函式
~rect() // 析構函式,計數器減1
static int getcount() // 返回計數器的值
private:
int width;
int height;
static int count; // 一靜態成員做為計數器
};
自己編寫拷貝建構函式又可以分為兩種情況——淺拷貝與深拷貝。
二、淺拷貝
所謂淺拷貝,指的是在物件複製時,只是對物件中的資料成員進行簡單的賦值,上面的例子都是屬於淺拷貝的情況,預設拷貝建構函式執行的也是淺拷貝。大多情況下「淺拷貝」已經能很好地工作了,但是一旦物件存在了動態成員,那麼淺拷貝就會出問題了,讓我們考慮如下一段**:
class rect
~rect() // 析構函式,釋放動態分配的空間
} private:
int width;
int height;
int *p; // 一指標成員
};
int main()
在這段**執行結束之前,會出現乙個執行錯誤。原因就在於在進行物件複製時,對於動態分配的內容沒有進行正確的操作。我們來分析一下:
在執行定義rect1物件後,由於在建構函式中有乙個動態分配的語句,因此執行後的記憶體情況大致如下:
在使用rect1複製rect2時,由於執行的是淺拷貝,只是將成員的值進行賦值,所以此時rect1.p和rect2.p具有相同的值,也即這兩個指標指向了堆裡的同乙個空間,如下圖所示:
當然,這不是我們所期望的結果,在銷毀物件時,兩個物件的析構函式將對同乙個記憶體空間釋放兩次,這就是錯誤出現的原因。我們需要的不是兩個p有相同的值,而是兩個p指向的空間有相同的值,解決辦法就是使用「深拷貝」。
三、深拷貝
在「深拷貝」的情況下,對於物件中動態成員,就不能僅僅簡單地賦值了,而應該重新動態分配空間,如上面的例子就應該按照如下的方式進行處理:
class rect
rect(const rect& r)
~rect() // 析構函式,釋放動態分配的空間
} private:
int width;
int height;
int *p; // 一指標成員
};
此時,在完成物件的複製後,記憶體的乙個大致情況如下:
此時rect1的p和rect2的p各自指向一段記憶體空間,但它們指向的空間具有相同的內容,這就是所謂的「深拷貝」。
此外,在與「物件的複製」很類似的「物件的賦值」的情況下,也會出現同樣的問題。在「物件的賦值」一文中再來討論此問題。
通過對物件複製的分析,我們發現物件的複製大多在進行「值傳遞」時發生,這裡有乙個小技巧可以防止按值傳遞——宣告乙個私有拷貝建構函式。甚至不必去定義這個拷貝建構函式,這樣因為拷貝建構函式是私有的,如果使用者試圖按值傳遞或函式返回該類物件,將得到乙個編譯錯誤,從而可以避免按值傳遞或返回物件。
「淺拷貝」與「深拷貝」
c 中物件的複製就如同 轉殖 用乙個已有的物件快速地複製出多個完全相同的物件。一般而言,以下三種情況都會使用到物件的複製 1 建立乙個新物件,並用另乙個同類的已有物件對新物件進行初始化,例如 cpp view plain copy class rect rect rect1 rect rect2 r...
淺拷貝與深拷貝
淺拷貝 1 2 myclass a,b a b 為了封裝性和解耦,同型別的兩個物件之間進行賦值操作時,所有成員變數被複製,包括私有成員 指標變數。類的成員函式在傳遞或返回物件時都會進行物件複製產生臨時物件,比如函式呼叫時實參變為形參,以及函式返回物件。考慮到效能和使用者要求不同,編譯器不複製物件內部...
「淺拷貝」與「深拷貝」
c 中物件的複製就如同 轉殖 用乙個已有的物件快速地複製出多個完全相同的物件。一般而言,以下三種情況都會使用到物件的複製 1 建立乙個新物件,並用另乙個同類的已有物件對新物件進行初始化,例如 cpp view plain copy class rect rect rect1 rect rect2 r...