前言
若要將a物件複製給b物件,編譯器應當做何工作?C++初學者也許會直接說” a物件的成員複製給b物件的成員 “。在很多情況下,這種說法正確,事實上C++會給類定義一個預設的複製函式,它所做的工作也正是如此。但,下面問題來了:如果類的成員當中有指標,這種做法還行嗎?本文將對這個問題作出例項分析。
一個典型錯誤示例
下面這個程式碼示例用來檢驗前言中所提到的問題:
1 #include <iostream> 2 #include <cstdlib> 3 #include <fstream> 4 #include <string> 5 6 using namespace std; 7 8 class A { 9 public: 10 // 建構函式為指標成員開闢空間並賦初值0 11 A() { 12 num_p = new int; 13 *num_p = 0; 14 } 15 // 給指標所指物件賦值 16 void setNum(int num) { 17 *num_p = num; 18 } 19 // 獲取指標所指物件 20 int getNum() const { 21 int num = *num_p; 22 return num; 23 } 24 private: 25 int *num_p; 26 }; 27 28 int main() 29 { 30 A a1; 31 32 // 設定a1指標成員所指物件的值 33 a1.setNum(1); 34 // 呼叫系統自動合成的複製函式 35 A a2 = a1; 36 // 觀察得出a1,a2的指標成員所指物件均為整數1。 37 cout << "a1`s num: " << a1.getNum() << endl; 38 cout << "a2`s num: " << a2.getNum() << endl; 39 40 // 修改a1指標成員所指物件的值 41 a1.setNum(2); 42 // 觀察得出不單a1的指標成員所指物件改了,a2的也跟著變了。 43 cout << "a1`s num: " << a1.getNum() << endl; 44 cout << "a2`s num: " << a2.getNum() << endl; 45 46 return 0; 47 }
執行結果:
我們可以觀察到,當類中具有指標成員時,如果使用預設的複製函式,那麼此後當某個物件修改了其指標成員所指物件,那麼該類派生的其他物件的指標成員所指物件也會跟著改變。這顯然不符合程式設計規範,下面我們將提出解決方案。
解決思路
既然系統自帶的複製函式無法滿足我們的要求,那麼我們可以自定義一個以指定編譯器在複製物件時的操作。
一個正確示例
下面這個程式碼給出了一個具體解決方案,複製函式啟用時,拷貝指標所指物件,而不是指標本身:
1 #include <iostream> 2 #include <cstdlib> 3 #include <fstream> 4 #include <string> 5 6 using namespace std; 7 8 class A { 9 public: 10 // 建構函式為指標成員開闢空間並賦初值0 11 A() { 12 num_p = new int; 13 *num_p = 0; 14 } 15 // 自定義複製函式 16 A(const A & a) { 17 num_p = new int; 18 *num_p = a.getNum(); 19 } 20 // 給指標所指物件賦值 21 void setNum(int num) { 22 *num_p = num; 23 } 24 // 獲取指標所指物件 25 int getNum() const { 26 int num = *num_p; 27 return num; 28 } 29 private: 30 int *num_p; 31 }; 32 33 int main() 34 { 35 A a1; 36 37 // 設定a1指標成員所指物件的值 38 a1.setNum(1); 39 // 呼叫自定義的複製函式 40 A a2=a1; 41 // 觀察得出a1,a2的指標成員所指物件均為整數1。 42 cout << "a1`s num: " << a1.getNum() << endl; 43 cout << "a2`s num: " << a2.getNum() << endl; 44 45 // 修改a1指標成員所指物件的值 46 a1.setNum(2); 47 // 觀察得出a1的指標成員所指物件改了,a2的沒變。 48 cout << "a1`s num: " << a1.getNum() << endl; 49 cout << "a2`s num: " << a2.getNum() << endl; 50 51 return 0; 52 }
執行結果:
這一次,兩個物件沒有出現“ 相互干擾 ”了。
說明
1. 複製函式不單在A a = b的形式下啟用,在作為函式引數傳遞時,生成容器時也會啟用,這點要注意到。
2. 注意複製函式的原型( 形參為const引用且無返回 )