C++除了自動提供默認構造函數和析構函數,還有一種叫復制構造函數,用于將一個對象復制到新創建的對象中。它用于初始化過程中,而不是常規的賦值過程。原型通常如:
Classname ( const Classname & );
新建一個對象并初始化為同類現有對象時,復制構造函數都會被調用,如下4種聲明:
StringBad ditto(motto);
StringBad metoo = motto;
StringBad also = StringBad(motto);
StringBad * ps = new StringBad(motto);
由于按值傳遞對象將調用復制構造函數,應該按引用傳遞對象,節省調用時間和空間。
默認的復制構造函數將逐個復制非靜態成員(成員復制也稱為淺復制),復制的是成員的值。如果需要用到靜態成員,需要顯式定義一個復制構造函數。函數頭如:
StringBad::StringBad(const StringBad & s)
淺復制還有一個隱患,當成員包含指針,在調用了默認復制構造函數之后,會出現兩個指針指向同一個地址的情況。此時如果用delete釋放內存很容易不小心釋放兩次,此時將導致不確定的、可能有害的后果。解決辦法是定義一個顯式復制構造函數,進行深度復制!生成一個指向數據的副本,并將其地址賦給新的指針。
通常還需要看一看默認的賦值運算符。上面4種情況總是會調用復制構造函數,但使用=時也可能會調用賦值運算符(與具體實現有關,比如先用復制構造函數創建一個臨時對象,然后再通過賦值將臨時對象的值復制到新對象中。
賦值運算符原型:
Classname & Classname::Operator=(const Classname &);
同樣必須用深度復制解決值傳遞出現的問題。
補充:復制構造函數與返回對象的關系
一般而言,如果方法或函數要返回局部對象,則應返回對象,而不是指向對象的引用。在這種情況下,將使用復制構造函數來生成返回的對象。如果方法或函數要返回一個沒有公有復制構造函數的類(如ostream)的對象,則必須返回一個指向這種對象的引用。最后,有些方法和函數(如重載的賦值運算符)可以返回對象,也可以返回指向對象的引用,在這種情況下,應首選引用,因為其不會調用復制構造函數,效率更高。
新聞熱點
疑難解答