程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Effective C++(12) 復制對象時要復制每一個成分

Effective C++(12) 復制對象時要復制每一個成分

編輯:C++入門知識

問題聚焦:

負責拷貝的兩個操作:拷貝構造函數和重載賦值操作符。 一句話總結,確保被拷貝對象的所有成員變量都做一份拷貝。

Demo
void logCall(const std::string& funcName);  // log函數

class Date{ ... };

class Customer {
public:
    ....
    Customer(const Customer& rhs);
    Customer& operator=(const Customer& rhs);
private:
    std::string name;
    Date lastTransaction;
};

Customer::Customer(const Customer& rhs) 
    : name(rhs.name)      // 忽略了lastTransaction成員變量
{
    logCall("Customer copy constructor");
}

Customer& Customer::operato=(const Customer& rhs)
{
    logCall("Customer copy assignment operator");
    name = rhs.name;         // 忽略了lastTransaction成員變量
    return *this;
}

上述代碼的問題很明顯,就是沒有拷貝lastTransaction成員變量,更為嚴重的是,編譯器並不會提醒你這個錯誤。 結論就是,如果為class添加一個成員變量,必須同時修改copying函數(包括拷貝構造函數和重載賦值操作符)


如果發生了繼承,會帶來什麼樣的危險呢?
class PriorityCustomer: public Custoer {
public:
    ...
    PriorityCustomer(const PriorityCustoer& rhs);
    PriorityCustomer& operato=(const PriorityCustomer& rhs);
    ...
private:
    int priority;
};

PriorityCustomer&
PriorityCustomer::PriorityCustomer (const PriorityCustomer& rhs)
    : priority(rhs.priority)
{
    logCall("PriorityCustoer copy constructor");
}

PriorityCustomer&
PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
    logCall("PriorityCustomer copy assignment operator");
    priority = rhs.priority;
    return *this;
}

問題:
PriorityCustomer的拷貝函數們拷貝了它聲明的成員變量,卻忽略了它所繼承的Customer成員變量的拷貝,也沒有指定實參傳遞給Customer的構造函數,因此,PriorityCustomer對象的Customer成分會被不帶實參的Customer默認構造函數初始化。

改進:為子類編寫拷貝函數時,必須要復制其基類部分。
PriorityCustomer&
PriorityCustomer::PriorityCustomer (const PriorityCustomer& rhs)
    : Customer(rsh), priority(rhs.priority)
{
    logCall("PriorityCustoer copy constructor");
}

PriorityCustomer&
PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
    logCall("PriorityCustomer copy assignment operator");
    Customer::operator=(rhs);
    priority = rhs.priority;
    return *this;
}

結論
確保賦值所有local成員變量
調用所有base classes內的適當的拷貝函數

需要注意的一點是:
雖然重載賦值操作符和拷貝函數的代碼長得很像,但是很難復用它們,因為重載賦值操作符時調用拷貝構造函數就像在構造一個已經存在的對象; 同樣地,令拷貝構造函數調用重載賦值操作符一樣沒有意義。
所以,復用這兩個代碼的方式就是寫一個新的成員函數給兩者調用。

小結:
拷貝構造函數和重載賦值操作符應該確保復制“對象內的所有成員變量”及“所有base class成分”; 不要嘗試拷貝構造函數調用重載賦值操作符或是反過來。



參考資料: 《Effective C++ 3rd》

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved