2009-06-16 56 views
11

根據「C++編碼標準」第32條中的描述,我有一個值類。簡而言之,這意味着它提供了值語義,並且沒有任何虛擬方法。是否有可能在編譯時禁止從類中派生?

我不想讓一個類從這個類派生。除此之外,其中一個原因是它具有公共的非虛構析構函數。但是基類應該有一個公共和虛擬的析構函數,或者是非虛擬的。

我不知道編寫值類的可能性,以至於不可能從它派生出來。我想在編譯時禁止它。是否有任何已知的習慣用法呢?如果不是,那麼即將到來的C++ 0x也許有一些新的可能性?還是有沒有這種可能性的充分理由?

回答

6

即使問題沒有標記爲C++ 11,人誰得到這裏應該提到的是C++ 11支持新的上下文標識符final。見wiki page

8

如果您只允許通過工廠方法創建類,則可以使用私有構造函數。

class underivable { 
    underivable() { } 
    underivable(const underivable&); // not implemented 
    underivable& operator=(const underivable&); // not implemented 
public: 
    static underivable create() { return underivable(); } 
}; 
+0

啊,這可能是我的類的靜態工廠方法來保持東西放在一起?聽起來很不錯。 – SebastianK 2009-06-16 11:38:07

+3

不幸的是,這並不能阻止派生。它只是防止派生類型的安裝,除了通過工廠。如果你真的可以預防派生,那就太好了,但據我所知,你不能。 – 2009-06-16 11:43:41

+0

實用的方式是不能實例化派生類型的對象,並且根本無法派生(訪問受保護的靜態成員??) – Motti 2009-06-16 18:02:41

25

了Bjarne Stroustrup的寫關於這個here


從鏈接的相關位:

我可以阻止人們從我的類派生?

是的,但你爲什麼要?有兩個常見的答案:

  • 效率:避免我的功能 調用是虛擬的。
  • 安全:保證我的課沒有(沒有切割的恐懼 例如,可以肯定 ,我可以複製的對象)

用作 基類,以我的經驗,效率理由通常是錯放的恐懼。在C++中,虛擬函數調用速度如此之快,以至於與使用普通函數調用的替代解決方案相比,虛擬函數調用的虛擬函數調用不會產生可測量的運行時開銷。請注意,虛擬函數調用機制通常僅在通過指針或引用進行調用時使用。直接爲命名對象調用函數時,虛擬函數類的開銷很容易被優化。

如果確實需要「封頂」類層次結構以避免虛函數調用,那麼可能會問爲什麼這些函數首先是虛擬的。我看到了一些例子,其中性能關鍵的功能因爲沒有正當理由而變得虛擬,僅僅是因爲「我們通常這樣做」。

這個問題的另一個變種,如何防止出於邏輯原因導出,有一個解決方案。不幸的是,這個解決方案並不漂亮。它依賴於層次結構中派生最多的類必須構建虛擬基礎的事實。例如:

class Usable; 

class Usable_lock { 
    friend class Usable; 
private: 
    Usable_lock() {} 
    Usable_lock(const Usable_lock&) {} 
}; 

class Usable : public virtual Usable_lock { 
    // ... 
public: 
    Usable(); 
    Usable(char*); 
    // ... 
}; 

Usable a; 

class DD : public Usable { }; 

DD dd; // error: DD::DD() cannot access 
     // Usable_lock::Usable_lock(): private member 

(從D&E秒11.4.3)。

2

嗯,我有一個類似的問題。這是在SO上發佈的here。這個問題是另一回事。即只允許派生你允許的類。檢查它是否解決您的問題。

這在編譯時間完成。

4

好好看看here
這真的很酷,但它是一個黑客。
不知道爲什麼stdlib不會用它自己的容器做到這一點。

0

此解決方案不起作用,但我將其作爲不做的一個示例。


我沒有使用C++有一段時間了,但據我記得,你得到你想通過析構函數私人的東西。

UPDATE:

在Visual Studio 2005中,你會得到警告或錯誤。檢查了下面的代碼:

class A 
{ 
public: 
    A(){} 
private: 
    ~A(){} 
}; 

class B : A 
{ 
}; 

現在,

B B;

會產生一個錯誤 「錯誤C2248: 'A ::〜A':不能訪問類 'A' 聲明私有成員」

B *b = new B(); 

會產生警告「警告C4624: 'B':析構函數無法生成,因爲基類析構函數無法訪問「。

它看起來像一個半解決方案,但正如orsogufo指出的,這樣做會導致A類不可用。離開答案

1

我一般會做到這一點,如下所示:

// This class is *not* suitable for use as a base class 

評論之後,在頭和/或文檔。如果你的班級的客戶不遵守包裝上的說明,那麼在C++中,他們可能會出現未定義的行爲。未經許可而派生只是這種情況的特例。他們應該使用組合。

順便說一句,這有點誤導:「一個基類應該有一個公共的,虛擬的或者受保護的和非虛擬的析構函數」。

對於要用作運行時多態性基類的類也是如此。但是,如果派生類永遠不會通過指向基類類型的指針來引用,那就沒有必要。具有僅用於靜態多態性的值類型可能是合理的,例如模擬動態綁定。令人困惑的是,繼承可以用於C++中的不同目的,需要基類的不同支持。這意味着,雖然你不想動態多態性與你的類,它可能仍然是良好的創建派生類提供他們正確使用。

相關問題