2010-06-15 168 views
5

我想阻止一個類能夠將其'this'指針轉換爲其接口之一的指針。我通過使用中間代理類的私有繼承來實現此目的。問題是我發現私有繼承使所有基類的公共靜態成員和類型都無法訪問層次結構中繼承類的所有類。C++私有繼承和靜態成員/類型

class Base 
{ 
public: 
    enum Enum 
    { 
     value 
    }; 
}; 

class Middle : private Base 
{ 
}; 

class Child : public Middle 
{ 
public: 
    void Method() 
    { 
     Base::Enum e = Base::value; // doesn't compile BAD!  
     Base* base = this; // doesn't compile GOOD! 
    } 
}; 

我已經在VS2008(所需版本)和VS2010中都試過這個,都沒有工作。

任何人都可以想到一個解決方法嗎?或者停止轉換的另一種方法?

此外,我是行爲的好奇心,它只是編譯器實現的副作用,還是它的設計?如果通過設計,那麼爲什麼?我一直認爲私有繼承意味着沒有人知道從基地繼承中間。然而,所展示的行爲意味着私有繼承意味着更多,實際上,Child沒有比任何名稱空間更少的對Base的訪問權限!

+0

+1:的確是一個非常有趣的問題。 – 2010-06-15 18:15:23

回答

6

您應該能夠通過完全限定其訪問Base::Enum

class Child : public Middle 
{ 
public: 
    void Method() 
    { 
     ::Base::Enum e = ::Base::value; 
    } 
}; 

這是由語言(C++ 03§11.2/ 3)規定的行爲:

注意:私有基類的成員可能無法作爲繼承的成員名稱訪問,但可以直接訪問。

接下來是一個擴展示例,它與您的示例代碼非常相似。

但是,看起來Visual C++ 2008和Visual C++ 2010都無法正確實現這個功能,所以雖然可以使用::Base::Enum類型,但仍然無法訪問::Base::value。 (實際上,Visual C++似乎得到了很多錯誤,因爲它錯誤地允許你使用不完全合格的Base::Enum)。

要「避開」的問題,你可以使用聲明添加到Middle類:

class Middle : private Base 
{ 
protected: 

    using Base::Enum; 
    using Base::value; 
}; 

這不會讓你在Child類使用Base::EnumBase::value,但它可以讓你使用EnumvalueMiddle::EnumMiddle::value

+1

我提交了關於Microsoft Visual的Visual C++錯誤的錯誤報告:https://connect.microsoft.com/VisualStudio/feedback/details/567693/ – 2010-06-16 03:37:39

+0

感謝您的詳細解答。討厭它是由一個錯誤引起的。不幸的是,在我的現實世界中,添加對Middle類的使用不是一種選擇,因爲Middle是一個將基類作爲Type參數的模板化類。我唯一的選擇是將枚舉移出界面。 – WearyMonkey 2010-06-17 02:22:55

+0

我會注意到,我引用的標準中的「註釋」實際上沒有指定任何內容,只是解釋了這種行爲。我的猜測是,這種行爲的實際規範通過描述名稱解析如何發生的各個部分進行傳播。 – 2010-06-18 01:43:00

1

我只有一個問題:你爲什麼私有繼承?

繼承是很破的概念,在我看來,因爲它違反了一個責任原則:

  • 你繼承接口
  • 你繼承

不幸的是需要繼承的實現對於面向對象代碼中的多態性,所以在這種情況下你不能迴避它。

但是,在這裏你明確地希望不要使用多態,所以我發現自己想知道爲什麼使用繼承,因爲它是它唯一有趣的用法(imo)。

相反,在C++中,你可以使用:

  • 組成,代碼重用
  • 免費功能(在自己的空間中定義的)
  • usingtypedef等..從帶來的對象課外

你的例子似乎在這裏受到限制,但我也把我的枚舉包裝到struct以防止名稱空間污染千個符號(並且因爲struct可以用作名稱空間不能的模板參數)。

struct MyEnum 
{ 
    enum type 
    { 
    value 
    }; 
}; 

class Child 
{ 
public: 
    typedef MyEnum::type Enum; 

    Child(Enum e = MyEnum::value); 

private: 
}; 

我看不出什麼毛病限定名稱,而不是我覺得它可以更容易再次閱讀,因爲你知道我們是在談論這枚舉...

真的,private繼承最好避免(通常用組合物代替)。唯一有效的情況(imo)是空基優化...坦率地說,它通常不是你需要的(像往常一樣進行優化)。

+0

私有繼承的其他用途是如果您需要訪問受保護的類的成員。這不是一個理想的情況,但在極少數情況下會出現。我當然同意這是最好的避免。 – 2010-06-16 13:44:25

+0

我通常更喜歡在此處解耦:我創建一個公開繼承並使用組合的中間類。 – 2010-06-16 14:44:15

+0

我當然同意在可能的情況下組合比繼承更可取。然而,我現實世界中的基類是舊的大代碼庫中的核心類,做適當的重構工作意味着重寫大部分代碼。 – WearyMonkey 2010-06-17 02:26:24