1. 程式人生 > >C++進階--擁有資源控制代碼的類(淺拷貝,深拷貝,虛建構函式)

C++進階--擁有資源控制代碼的類(淺拷貝,深拷貝,虛建構函式)

// Person通過指標擁有string
class Person {
public:
   Person(string name) { pName_ = new string(name); }
   ~Person() { delete pName_; }

   void printName() { cout << *pName_; }

private:
   string* pName_;
};

int main() {
   
   vector<Person> persons;
   persons.push_back(Person("George")); 

   persons.front().printName(); //這裡會崩

   cout << "Goodbye" << endl;
}

   //persons.push_back(Person("George")); 事實上該行程式碼可以分解成以下步驟
   // 1. "George" 被構造
   // 2. "George"的一個副本儲存到(淺拷貝)
   // 3. "George"被銷燬

// Solution 1: 定義拷貝構造和拷貝賦值實現深拷貝
    Person(const Person& rhs) {   
      pName_ = new string(*(rhs.pName()));
   }
    Person& operator=(const Person& rhs);  
   string* pName() const { return pName_; }

// Solution 2: 禁用拷貝構造和拷貝賦值
// 對C++ 11,使用=delete
// for C++ 03, 宣告但不定義
    Person(const Person& rhs); 
    Person& operator=(const Person& rhs);  

// 如果禁用之後仍然需要拷貝,使用clone()
// 顯式的拷貝
    Person* clone() {  
      return (new Person(*(pName_)));
   }

// 更推薦方法2:
// 1. 因為拷貝構造和拷貝賦值經常並不需要
// 2. 使拷貝顯式,隱式拷貝容易出現bug
// 3. clone可以利用多型實現虛建構函式,自動根據指標所指物件的型別拷貝基類或者派生類物件

class Dog { 
public:
   virtual Dog* clone() { return (new Dog(*this)); }   //co-variant return type 允許覆寫函式具有不同的返回型別,只要返回型別由基類的返回型別派生得到
};

class Yellowdog : public Dog { 
   virtual Yellowdog* clone() { return (new Yellowdog(*this)); }
};

void foo(Dog* d) {      // d 是Yellowdog
   //Dog* c = new Dog(*d); // c 是Dog,不是我們想要的
   Dog* c = d->clone();    // c是Yellowdog
   //... 
   //
}

int main() {
   Yellowdog d;
   foo(&d);
}

// C++ 11 的方法:
//    shared_ptr<string> pName_;
//    大多數情況下用unique_ptr也可以, 但是跟STL container一起使用時必須使用shared_ptr, 
//    因為STL容易要求元素時可拷貝的