2013-03-24 160 views
1

在我的設計過程中,有時會遇到添加/刪除虛擬方法的情況。我知道的經驗法則是,一旦我擁有虛擬方法,我將擁有一個虛擬析構函數。沒有虛擬方法的虛擬析構函數有什麼危害嗎?

我的問題:如果在創建類時立即添加虛擬析構函數(即使沒有虛擬方法),是否有任何傷害?基本上這個想法以後不要忘記。特別是對於n個派生類,我不需要在以後的n個地方改變它。

回答

5

有一個在虛函數表的規模很小開銷的聲明。可能不值得擔心。一個虛擬析構函數也會使你的類成爲一個非集合類,一個非平凡類,一個非標準佈局類,因此也是一個非POD類。這可能是不受歡迎的,這取決於手頭的問題。

但是,我建議特別設計你的類是多態的或不是。如果他們將要多態使用,給他們一個虛擬的析構函數。如果沒有,不要。如果您需要更改它,請在需要時進行更改。

+3

我很驚訝大家在這裏談論虛擬表的開銷,這是邊緣的,但​​不是'虛擬'析構函數的存在使得該類可能不是期望的非POD類的事實。提醒我一個[類似的問題](http://stackoverflow.com/a/8298219/452307)我前段時間回答 – 2013-03-24 11:45:35

+0

@AlokSave被盜! – 2013-03-24 11:48:36

+0

@Alok保存它也使它非聚合,這可能是一個問題。 – juanchopanza 2013-03-24 12:13:24

1

唯一的危害是你的類和所有派生類將有一個v-表,這是一個邊際增加的大小。 即使您決定稍後創建基類析構函數,您也不需要在派生類中進行任何更改。對於任何包含析構函數的方法,您只需要在基類中使用虛擬關鍵字一次。派生類中的相同方法自動變爲虛擬。

作爲替代方案,您可以使您的析構函數受到保護。這將防止使用基類指針的意外調用。

class A 
{ 
    protected: 
    ~A(){} 
}; 

class B : public A 
{}; 

int main(int argc, char *argv[]) 
{ 
    A * p = new B; 
    delete p; 
} 

在我的編譯器,它提供了以下錯誤

錯誤C2248: 'A ::〜A':不能訪問類中聲明保護成員 'A' a.cpp(9) :編譯器生成的 'A ::〜A' 這裏 a.cpp(6):看到 'A'

1

不,即使您沒有任何其他虛擬方法,擁有虛擬析構函數也是非常有意義的。

但是,如果內存使用率很重要,則每個字節都會計數,如果您沒有任何虛擬方法,則可以獲得4或8個字節。在我的應用程序中,我有一些類,其中有數百萬個實例。在那種情況下,在你的班級中擺脫一個v-pointer真的很有意義。

如果將析構函數從非虛擬變爲虛擬(反之亦然),我並不完全遵循爲什麼需要更改派生類。一旦一個方法是虛擬的,即使你沒有指定虛擬,同樣的方法在所有派生類中都是虛擬的。儘管如此,由於風格的原因,可能會建議在派生類中添加虛擬,即使不需要。

1

不要盲目遵守規則。也就是說,遵守規則,但不要盲目做。

真正需要虛擬析構函數的唯一情況是何時通過其基礎對象指針刪除對象。經驗法則概括並簡化了這種情況:如果一個對象可能通過其基礎對象指針被刪除,那麼它將被多形地使用;多態對象可能具有虛擬功能,並且具有虛擬功能的對象可能被多態使用;因此,具有虛函數的對象可能需要虛擬析構函數。

這一切都很好,很規則,大多數都適用,但有一些更重要和更基本的事實很少提及,部分原因是這些規則確實有效。事實上,有價值的對象,還有另一種對象,它沒有一個好名字,但我會稱它們爲實體對象。像實體一樣的對象具有與它們的值分離的身份,它們使用引用語義,不​​應該在沒有充分理由(例如創建單獨的身份)的情況下被複制,它們可能被多態地訪問等等。類似值的對象沒有身份除了它們的價值之外,它們可以被自由地複製,它們不應該被多形地使用,等等。它們是如此不同以至於它們的類有不同的關鍵字是值得的!當你設計你的課程時,你必須決定它屬於哪個類別。那麼你的問題就解決了。實體獲得虛擬析構函數,值不。

0

擁有虛擬析構函數的唯一原因是可以通過指向基類型的指針刪除派生類型的對象。如果這是該課程的設計要求,那麼它必須具有虛擬析構函數,即使它沒有任何其他虛擬函數。如果設計不包括通過指向base的指針刪除,則不需要虛擬析構函數,即使它具有虛函數。腰帶和吊帶人會告訴你無論如何都要使析構函數變成虛擬的,因爲它不會傷害任何東西,而且,你永遠不會知道。這不是技術原因;這是一種政策選擇,可防止不閱讀和遵循文檔的用戶。

相關問題