這解決了關於爲抽象基類聲明虛擬析構函數的第二個問題(例如,至少一個成員函數是純虛函數)。這是LLVM clang ++編譯器捕捉潛在問題的真實例子。這發生在Apple Developer爲Mac OS X Mavericks操作系統提供的命令行工具版本中。
假設您有一個派生類的集合,最終派生類具有抽象基類的父類來定義公共接口。然後有必要有一個存儲容器,像一個有意聲明存儲指向每個元素的抽象基類的指針的向量。稍後,遵循良好的工程實踐,需要「刪除」容器元素並將內存返回到堆中。最簡單的方法是按元素遍歷vector元素並調用每個元素的delete操作。
好吧,如果抽象基類沒有將析構函數聲明爲虛擬的,那麼clang ++編譯器會提供一個關於在抽象類上調用非虛析構函數的友好警告。請記住,實際上只有派生類是從操作符new的堆中分配的。從繼承關係派生的類指針類型的確是抽象的基類類型(例如is-a關係)。
如果抽象基類析構函數不是虛擬的,那麼將如何調用正確派生類的析構函數來釋放內存?至多編譯器知道更好(至少可能用C++ 11),並使其發生。如果啓用了-Wall編譯器選項,則至少會出現編譯警告。然而,更糟糕的是,派生類析構函數從來沒有到達過,內存永遠不會返回到堆中。因此,現在有一個內存泄漏,追查和修復可能非常具有挑戰性。它只需要在抽象基類析構函數聲明中添加一個「虛擬」。
示例代碼:
class abstractBase
{
public:
abstractBase() { };
~abstractBase() { };
virtual int foo() = 0;
};
class derived : abstractBase
{
public:
derived() { };
~derived() { };
int foo() override { return 42; }
};
//
// Later on, within a file like main.cpp . . .
// (header file includes are assumed to be satisfied)
//
vector<abstractBase*> v;
for (auto i = 0; i < 1000; i++)
{
v.push_back(new derived());
}
//
// do other stuff, logic, what not
//
//
// heap is running low, release memory from vector v above
//
for (auto i = v.begin(); i < v.end(); i++)
{
delete (*i); // problem is right here, how to find the derived class' destructor?
}
要解決此潛在內存泄漏,抽象基類有聲明它的析構作爲虛擬的。沒有其他要求。抽象基類現在變爲:
class abstractBase
{
public:
abstractBase() { };
virtual ~abstractBase() { }; // insert virtual right here
virtual int foo() = 0;
}
請注意,抽象基類當前具有空構造函數和析構函數體。正如Lightness在上面回答的那樣,編譯器爲抽象基類創建了一個默認構造函數,析構函數和複製構造函數(如果工程師沒有定義的話)。強烈建議您查看C++創建者Bjarne Stroustrup的任何C++編程語言版本,以獲取有關抽象基類的更多詳細信息。
'= 0;'只允許跟隨虛擬功能。 – billz
這不是C++。 –
@billz和軌道亮度競賽:修正了它 – user1911091