2010年9月7日 星期二

[C++ 小學堂] 類別 : 複製構造函數

使用類創建對象時, 構造函數被自動調用來完成對像的初始化, 那麼是否能夠像簡單變量的初始化一樣, 直接用一個對像來初始化另一個對像呢? 複製構造函數就是答案. 
如下代碼: 

  1. ...  
  2. point pt1(2,3);  
  3. point pt2=pt1; //Or point pt2(pt1);  
  4. ...  

上述代碼用物件pt1初始化pt2, 相當於將pt1中的每個成員數據的值複製於pt2中, 這是表面現象, 實際上系統調用一個複製構造函數. 如果類定義沒有顯示定義該複製構造函數時, 編譯器會隱式定義一個預設的複製構造函數, 它是一個inline, public 類型的成員函數, 其原型如下: 
point::point(const point &); 
當然可以顯示定義複製構造函數以完成其他動作, 在有的情況下系統提供的預設構造函數足以滿足需要, 但在某些場合的確必須客制定義構造函數. 
範例代碼: 
---------------------------Point8_9.h---------------------------
  1. #include "main.h"  
  2.   
  3. class Point8_9{  
  4.     int xPos;  
  5.     int yPos;  
  6.   
  7. public:  
  8.     Point8_9(int x=0,int y=0){  
  9.         cout << "<調用構造函數>" << endl;  
  10.         xPos = x;  
  11.         yPos = y;  
  12.     }  
  13.   
  14.     Point8_9(const Point8_9& pt){  
  15.         cout << "<<調用複製構造函數>>" << endl;  
  16.         xPos = pt.xPos;  
  17.         yPos = pt.yPos;  
  18.     }  
  19.   
  20.     void print(){  
  21.         cout << "xPos: " << xPos << ",yPos: " << yPos << endl;  
  22.     }  
  23. };  
---------------------------example_ch8.cpp---------------------------
  1. ...  
  2. void example809(){  
  3.     Point8_9 pt1(3,4);  
  4.     pt1.print();  
  5.     Point8_9 pt2 = pt1;  
  6.     pt2.print();  
  7.     Point8_9 pt3;  
  8.     pt3.print();  
  9.     Point8_9 pt4(pt3);  
  10.     pt4.print();  
  11. }  
  12. ...  
結果: 
<調用構造函數>
xPos: 3,yPos: 4
<<調用複製構造函數>>
xPos: 3,yPos: 4
<調用構造函數>
xPos: 0,yPos: 0
<<調用複製構造函數>>
xPos: 0,yPos: 0

Ps. 一但顯示定義了構造函數, 編譯器便不會再提供預設的複製構造函數. "point pt2=pt1;" 等價於 "point pt2(pt1);". 另外如果成員變量為const或引用類型, 則需透過初始化表達式進行初始化. 

預設複製構造函數帶來的問題: 
預設的複製構造函數並非萬用, 在某些情況需由程序員顯示定義複製構造函數, 請參考如下代碼: 
---------------------------Computer8_10.h---------------------------
  1. #include "main.h"  
  2.   
  3. class Computer8_10{  
  4.     char* brand;  
  5.     float price;  
  6. public:  
  7.     Computer8_10(const char* sz,float p){  
  8.         brand = new char[strlen(sz)+1];  
  9.         strcpy(brand,sz);  
  10.         price = p;  
  11.     }  
  12.   
  13.     ~Computer8_10(){  
  14.         if(brand!=NULL)  
  15.           delete[] brand;  
  16.         cout << "清理動態內存 brand" << endl;  
  17.     }  
  18.   
  19.     void print(){  
  20.         cout << "品質: " << brand << endl;  
  21.         cout << "價格: " << price << endl;  
  22.     }  
  23. };  
---------------------------example_ch8.cpp---------------------------
  1. ...  
  2. void example810(){  
  3.     Computer8_10 comp("Dell",9000);  
  4.     comp.print();  
  5.     Computer8_10 comp2(comp);  
  6.     comp2.print();  
  7. }  
  8. ...  
說明: 
按造預設複製構造函數, 語句"Computer8_10 comp2(comp);" 等價於如下代碼: 
comp2.brand = comp1.brand;
comp2.price=comp1.price;

其中後一句"comp2.price=comp1.price;" 是沒有問題的, 但 "comp2.brand = comp1.brand;" 卻給程序帶來致命的問題, 經過賦值操作後, 兩個對象中的brand指針都是指向同一塊動態內存, 當comp1, comp2撤銷時, 其釋放函數都要釋放同一塊內存, 可是對像的撤銷有先後, 一旦一個對像被撤銷後, 另一個對像的brand指針變成了 wild 指針, 使用該指針在次釋放動態內存將會引發內存錯誤. 不僅是重複釋放內存問題, 預設複製構造函數還有可能為程序帶來不可預測錯誤.

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...