文章目錄
-
- C++自動編寫函數及明确駁回
- 構造/析構/指派運算
了解C++默默編寫并調用哪些函數
若不想使用編譯器自動生成的函數,應該明确拒絕
為多态基類聲明virtual析構函數
别讓異常逃離析構函數
絕不在構造和析構過程中調用virtual
令operator=傳回一個reference to *this
在operator=中處理“自我指派”
複制對象時勿忘其每一個成分
C++自動編寫函數及明确駁回
C++自動建構:無參構造函數、copy構造函數、析構函數和=指派函數
// 空類
class Empty {};
// C++自動添加代碼
class Empty
{
public:
Empty() {...} // 自動添加無參構造函數
Empty(const Empty &rhs) {...} // 自動添加copy構造函數
~Empty() {...} // 自動添加析構函數
Empty& operator=(const Empty &rhs) {...} // 自動添加=指派函數
};
如何不讓c++自動建立這些函數?将成員函數聲明為private而且故意不實作它們:
class Empty
{
public:
Empty() {...}
~Empty() {...}
private:
Empty(const Empty &); // 拒絕自動生成copy構造函數
Empty& operator=(const Empty &); // 拒絕自動生成=指派函數
};
構造/析構/指派運算
考慮計時類:
class TimeKeeper
{
public:
TimeKeeper();
~TimeKeeper();
...
virtual int getTime();
};
class AtomicClock: public TimeKeeper {...}; // 原子鐘
class WaterClock: public TimeKeeper {...}; // 水鐘
class WristWatch: public TimeKeeper {...}; // 腕表
當
TimeKeeper
析構函數不為
virutal
時會怎樣?
TimeKeeper *ptk = getTimeKeeper();
...
delete ptk;
這種操作必然帶來以下兩個問題:
- 使用者可能忘記調用delete;
- 由于基類TimeKeeper的析構函數并非virtual,導緻delete時不會調用子類的析構函數,進而導緻ptk隻有部分被銷毀,屬于子類那部分沒有被完全銷毀。
因而,多态基類的析構函數要加 virtual 關鍵字。但如果類并不作為多态基類,在其析構函數添加virtual關鍵字會導緻額外的記憶體開銷(vptr/vtbl),且會阻礙程式的移植性。
針對構造和析構函數:
TimeKeeper::TimeKeeper()
{
getTime();
}
TimeKeeper::~TimeKeeper()
{
try {
// 可能異常代碼
// ...
}
catch (...) {
}
}
- 會造成異常的代碼不建議放在析構函數中,除非不得已(事實上,不建議異常代碼存在);
-
隻會調用TimeKeeper的方法,而不會調用子類的重載方法:getTime
AtomicClock pac;
// TimeKeeper構造函數 --> TimeKeeper.getTime() --> AtomicClock構造函數 --> AtomicClock.getTime()
子類構造時必定先調用基類TimeKeeper的構造函數,而在基類構造函數中調用了virtual方法getTime,此時子類的getTime并沒有構造完成,是以調用的是基類的方法。
針對指派操作:
TimeKeeper& operator=(const TimeKeeper &rhs)
{
// 假如rhs等于*this?即自我指派
// 1. 異常自我指派
delete pb; // 删除資源
pb = new Bitmap(*rhs.pb); // 由于rhs就是本身,而pb已被删除
// 2. 證同測試解決自我指派
if (this == &rhs)
return *this;
// 3. 異常安全性解決自我指派
Bitmap *pOrig = pb; // 先記錄原先資源
pb = new Bitmap(*rhs.pb); // 更新資源
delete pOrig; // 删除原先資源
// 拷貝所有相關資源
...
...
return *this;
}
- 讓=指派操作傳回
使得連鎖指派變得可能;*this
- 對于其他例如
、+=
等操作,最好也傳回*=
;*this
- 針對自我指派,可以引入“證同測試”或者實作代碼的“異常安全”;
- 拷貝時不要遺忘所有成員變量,要注意深淺複制處理。