2011-09-13 62 views
34

是否有任何情況下派生類有一個非破壞性的非破壞性?非析構函數表示不應將類用作基類。派生類的非析構函數是否像Java final修飾符的弱形式一樣工作?派生類與非虛擬析構函數

我特別感興趣的是派生類的基類有一個virtual析構函數。

回答

51

是否有任何情況下派生的 類有合法的非虛擬析構函數?

是的。

非虛擬析構函數表示一個類不應該被用作基類的 。

不是真的;非虛擬析構函數表示通過base指針刪除derived的實例將不起作用。例如:

class Base {}; 
class Derived : public Base {}; 

Base* b = new Derived; 
delete b; // Does not call Derived's destructor! 

如果不以上述方式做delete,那麼這將是罰款。但是,如果是這樣的話,那麼你可能會使用組合而不是繼承。

將有一個派生類的非虛擬析構函數像一個 弱形式的Java final修飾符?

不,因爲virtual -ness傳播到派生類。

class Base 
{ 
public: 
    virtual ~Base() {} 
    virtual void Foo() {}; 
}; 

class Derived : public Base 
{ 
public: 
    ~Derived() {} // Will also be virtual 
    void Foo() {}; // Will also be virtual 
}; 

C++ 03或更早版本中沒有用於防止子類(*)的內置語言機制。無論如何,這不是什麼大問題,因爲你應該總是prefer composition over inheritance。也就是說,當「一個」關係比真正的「有一個」關係更有意義時使用繼承。

(*)「最終」改性劑在C++ 11

+17

「'virtual'-ness傳播到派生類。」它呢? [參考標準]。它的確如12.4.7所示:「如果一個類有一個具有虛擬析構函數的基類,它的析構函數(無論是用戶還是隱式聲明的)都是虛擬的 – Raedwald

+2

在相關說明中,如果您有一個非虛擬基類,並且你的Derived類中有一些虛方法,那麼'Base * b = new Derived(); delete b;'將會是未定義的行爲,並且可能會導致程序崩潰,看起來相當安全,但事實並非如此(這是因爲'b '不會指向'Derived'對象的'開始' - 它將被vtable所需的空間所抵消,然後'delete'將不會在與'new'完全相同的地址上運行,因此它不是一個有效的地址空間,如果你想要在任何地方使用虛擬方法,那麼就在這個基地裏放一個虛擬方法。 –

0

Yes,No和第

虛析構函數無關與形成基礎或派生類的類的能力。這兩者都是合法的。

但是,有一些原因讓析構函數變成虛擬的。看到這裏:http://en.wikipedia.org/wiki/Virtual_destructor#Virtual_destructors。這使得一個班級,除了別的以外,還有一個虛擬桌子,如果它沒有的話。但是,C++不需要虛擬表來進行繼承。

+0

維基百科非常適合作爲研究的起點,它不應該被用作參考來源。 http://www.wikihow.com/Cite-a-Wikipedia-Article-in-MLA-Format –

1

非虛析構引入是完全沒有問題,只要你不希望使用它作爲一個基址指針派生類的時刪除對象。

如果你的派生類以多態的方式傳遞和存儲它與一個基指針,然後刪除它,那麼答案是否定的,使用虛擬析構函數。

+0

我的評論:*非虛擬析構函數完全沒問題,只要你不要不想用它的超類的類型指針刪除對象。* ...即使析構函數是虛擬的,d如果超類析構函數不是虛擬的,則使用超類類型的指針來選擇該對象將調用未定義的行爲。 – Nawaz

+0

Ahhm right,sorry&thx –

+0

這仍然不正確。你需要編輯舊的句子。 – Nawaz

24

如果您永遠不會調用指向派生類對象的基類指針的delete,那麼擁有帶有非虛擬析構函數的Base類是完全有效的。

Follow Herb Sutter's Advice

方針#:只有派生類需要調用虛函數的基本實現,使受保護的虛擬功能。 因爲只有析構函數的特殊情況:

方針#:基類的析構函數應該是公開和虛擬,或保護和非虛。


也許你的問題其實是:
不析構函數派生類需要是虛擬的,如果基類的析構函數是虛擬的?

答案是NO。
如果Base類的析構函數是虛擬的,那麼Derived類的析構函數已經是隱式虛擬的,你不需要明確地將其指定爲虛擬的。

+0

「如果基類析構函數是虛擬的,派生類中的析構函數是否需要虛擬?」是的,那真是我的問題。 – Raedwald

+0

如果派生類中沒有分配數據(堆棧,堆)(即基本大小和派生對象相等),那麼仍然可以使用公共的非虛擬基類析構函數。這可以是需要明確轉換的「typedefs」的解決方案:一個向量基類(一些不可修改的API),帶有具有顯式構造函數的point,normal和direction子類。 – Matthias

2

取決於你的班級的目的。有時它是一個很好的做法,讓您的析構函數的保護,而不是虛擬的 - 這基本上是說:

1

是的,有「不可通過基本類型的指針刪除派生類對象」:

void dothis(Base const&); 

void foo() { 
    Derived d; 
    tothis(d); 
} 

這裏這個類是多態使用的,但delete沒有被調用,因此它很好。

另一個例子是:

std::shared_ptr<Base> create() { return std::shared_ptr<Base>(new Derived); } 

因爲shared_ptr是能夠使用非多態性delete(通過類型擦除)。

我在Clang中實現了一個警告,專門用於檢測delete關於具有非虛擬析構函數的多態非最終類的調用,因此如果您使用clang -Wdelete-non-virtual-dtor,它會針對此情況發出警告。

0

如果您的派生類沒有向基類添加任何數據成員,並且具有空的析構函數體,那麼析構函數是否爲虛擬也無關緊要 - 所有派生析構函數都會調用無論如何基地。不建議這樣做,因爲對於某些人來說,在不知道這些限制的情況下來修改課程太容易了。

如果您從未嘗試通過指向基類的指針刪除對象,那麼您將很安全。這是另一個難以執行的規則,應謹慎使用。

有時候你沒有對基類的任何控制,並且你不得不從它派生,即使析構函數不是虛擬的。

最後,在基類中有一個非虛擬的析構函數並不會對編譯器強制的派生類施加任何限制,所以我不認爲它類似於Java的final。

3

你的問題不是很清楚。如果基類具有虛擬的 析構函數,則派生類將具有一個,無論如何。一旦宣佈了虛擬化,就沒有辦法讓 。

當然有些情況下,從 派生出來的類沒有虛擬析構函數。基類 類析構函數應該是虛擬的,以便您可以通過 指針刪除指向基類的指針。如果推導是私有的,那麼您沒有 擔心此問題,因爲您的Derived*不會轉換爲Base*。 否則,我已經看到了建議,如果基類 析構函數不是虛擬的,它應該被保護;這可以防止發生未定義行爲的情況(通過指向基址的指針刪除) 。然而,在實踐中,許多基類(例如 std::iterator<>)具有語義,因此任何人都不會出現任何人創建指向它們的指針;更少通過這樣的 指針刪除。因此,加入保護可能比它的價值更大。

+0

「你的問題不是很清楚」+1 –

8

Addresssing最新編輯:

編輯:我是在基類派生類中有一個虛析構函數的情況特別感興趣。

在這種情況下,派生類的析構函數不管添加是虛擬的,不管virtual關鍵字或不:

struct base { 
    virtual ~base() {}  // destructor is virtual 
}; 
struct derived : base { 
    ~derived() {}   // destructor is also virtual, because it is virtual in base 
}; 

在任何這不限於析構函數,如果在類型層次結構中,函數成員被聲明爲虛擬的,則相同函數的所有重寫(而不是重載)將是虛擬的,無論它們是否被聲明爲是。析構函數的具體位是~derived()替換virtual ~base()即使成員的名稱不同 - 這是此處析構函數的唯一特殊性。

0

將具有派生類的非虛擬析構函數是否像Java final修飾符的弱形式一樣工作?

根本不是。這裏是我的建議,以防止C++中的子類(如Java中的最終修飾符);使析構函數在一個類中是私有的。那麼你可以防止從它做子類

0

你可能不想在基類中創建虛擬析構函數?在這種情況下沒有析構函數。如果您使用指向基類的指針並在父級創建非虛擬析構函數,那麼一個編譯器會自動生成此警告!如果你想創建最終的父類,你可以阻止它。但最好是將其標記爲最終像:

class Base{ 
public: 
    //No virtual destructor defined 
    virtual void Foo() {}; 
}; 

class Derived final : public Base{ 
public: 
    ~Derived() {} // define some non-virtual destructor 
    void Foo() {}; // Will also be virtual 
}; 

在這種情況下compilator知道你想要什麼,並沒有警告生成。 空虛擬基礎析構函數的決定不是很好,但可以接受。你不需要設置屬性final。但是,這是不是你也want.You不需要定義空的虛擬基礎foo方法太 最好的東西:對compilator

class Base{ 
public: 
    //No virtual destructor defined 
    virtual void Foo() = 0; // abstract method 
}; 
class Derived final : public Base{ 
public: 
    ~Derived() {} // define some non-virtual destructor 
    void Foo() {}; // Will also be virtual 
}; 

這是全透明的代碼。不會產生警告,也不會使用備用代碼。

相關問題