如何禁止公共继承但允许私有(和受保护)继承

Posted

技术标签:

【中文标题】如何禁止公共继承但允许私有(和受保护)继承【英文标题】:How to prohibit public inheritance but allow private (and protected) inheritance 【发布时间】:2015-07-14 07:39:24 【问题描述】:

C++11 引入了关键字final 来禁止未来的覆盖或禁止继承。可以使用它的最常见示例是用于不打算用作基类的类(例如具有非虚拟析构函数)的情况。但是,有时我们可能希望在两个类之间建立 is-implemented-in-terms-of 关系(即private 继承),而不是 is-a关系(public 继承)。但是,final 禁止这两种继承。

我的问题如下:有没有办法允许private 继承但禁止public 继承(可能不是直接的,但至少我们可以“模拟”它)?在这种情况下,即使我们使用带有非虚析构函数的类也不会有任何问题,因为我们不能通过指向基的指针直接使用派生类,所以我们应该没问题。

我正在考虑这样的代码:

class Base /*final*/ ; // making it final prohibits both private and public inheritance

class PrivateDerived: private Base; // this should work

class PublicDerived: public Base; // this shouldn't

int main()

    PrivateDerived prvd;
    PublicDerived  pubd; // this should not compile

    // Base* pBase = new PrivateDerived; // doesn't work, so we are ok

【问题讨论】:

受保护的默认(其他)构造函数。 @DieterLücking 或析构函数,但这可能会更棘手。 @DieterLücking 即使我将Base 中的ctor 设为受保护,class PublicDerived: public Base; 行仍然可以编译,因为派生类“知道”如何构造基础。事实上,整个程序都可以编译。而且有问题的Base* pBase = new PrivateDerived; 仍然可以。 这是一个 XY 问题吗? 我的后备大脑中潜伏着一些关于重构friend 接口的东西,并且只使用protected 构造,但是现在给出答案太模糊了,需要另一个级别的解耦一个简单的继承。 @MustafaOzturk 提​​案朝着这个方向发展,尽管我的意思是参考。 【参考方案1】:

我不确定这是否是您正在寻找的内容,或者这是否会对您的情况有所帮助。但是,我将演示多态行为。

受保护的构造函数抽象类

class BaseProtected 
// ----- Member Variable Section -----
public: 
    // There Shouldn't Be Public Variables In A Base Class Unless 
    // That Is The Behavior You Are Looking For - Keep In Mind
    // Every Inherited Class & Outside Class Can Change Them.
protected:
    // Member Variables Here To Be Shared With Each Derived Class
private:
    // Member Variables Here To Be Used By Base Class Only

// ----- Member Function Section -----
public:
    virtual ~BaseProtected(); // Virtual Destructor 
    void somefunc() const; // Common Function Between All Derived Class
    virtual void allDerivedMustImplement() const; = 0 // Purely Virtual  

protected:
    // Default Constructor - Can Not Declare An Instance Of Base Class
    BaseProtected(); // Abstract Class         

   // Protected Functions Shared Between Classes
   // Protected Functions That Are Purely Virtual If Needed

private:
    // Private Functions Used By Base Class Only

; // BaseProtected

具有可能继承的派生类

class DerivedWithPossibleInheritance : public BaseProtected 
// ----- Member Variable Section -----
public:
    // Public Member If Giving Free Access   
protected:
    // Protected Members If Being Inherited From
private:
    // Private Members Unique To This Derived Class

// ----- Member Function Section ----- 
public:
    DerivedWithPossibleInheritance(); // Default Constructor
    virtual ~DerivedWithPossibleInheritance(); // Virtual Destructor

    void uniqueFunctionForThisClass() const;
    void allDerivedMustImplement() const override;

private:
    // Private Functions Unique To This Class

; // DerivedWithPossibleInheritance 

无法继承的派生类

class DerivedClassCanNotBeInheritedFrom sealed : public BaseProtected 
// ----- Member Variable Section -----
public: 
    // Public Members Variables
protected:
    // Should Not Have Member Variables Here For This Class Can Not Be Inherited from
private:
    // Private Members Variables

// ----- Member Function Section ------
public:
    DerivedClassCanNotBeInheritedFrom(); // Default Constructor
    virtual ~DerivedClassCanNotBeInheritedFrom(); // Default Virtual Destructor

    void anotherUniqueFunctionForThisClass() const;
    void allDerivedMustImplement() const override;

protected:
    // There Should Not Be Any Functions Here This Can Not Be Inherited From

private:    
    // Private Member Functions Here

; // DerivedClassCanNotBeInheritedFrom

我在这里演示的是在使用继承和多态性时密封的关键字。如果您不想派生您的类,请使用sealed 关键字。

对于声明任何基类、独立类或具有私有构造函数的单例类,都需要使用friend关键字。这里有很多类型的实现要涉及到来展示它们,但是防止类被继承的概念是相同的。就我个人而言,我没有使用关键字final,但我使用了关键字sealed,它的效果非常好。

我没有使用公共类以外的继承:所以回答你关于受保护或私有继承的问题并不是我真正熟悉的事情。但也许使用sealed 关键字可能会对您有所帮助。

【讨论】:

感谢您的努力,但不幸的是 sealed 是一个编译器扩展(如果我没记错的话,它只适用于 VS),更重要的是,C++11 引入了 final 作为防止继承的标准关键字(与sealed 的效果基本相同)。但是,我想阻止公共继承,但允许私有/受保护的继承。 好吧,我不是 100% 确定,但是是的,我正在使用 VS2012 和 VS2013 是的,我确实理解你的问题,但我从来没有想出这样的场景来回答你的问题有点超出我自己的能力。【参考方案2】:

有趣的问题!如果您不介意放弃析构函数的琐碎性,我认为以下工作可以完成:

#include <type_traits>

template <typename T>
class Base 
protected:
    ~Base() 
        static_assert(!std::is_convertible<T*,Base*>::value, "Invalid use of public inheritance.");
        
;

class Derived : public Base<Derived> 
;

int main() 
    Derived d;

上面的代码编译失败:static_assert 被触发是因为Derived* 可以转换为Base&lt;Derived&gt;*。但是,如果您将继承更改为 protectedprivate,则代码会编译。

不幸的是,用户仍然可以朝自己的脚开枪:

class Bad : public Base<Derived> 
;

【讨论】:

@JerryJeremiah 如果Derived 公开派生自Base&lt;Derived&gt;,则std::is_convertible&lt;Derived*, Base&lt;Derived&gt;*&gt;::value == true。因此否定是false 并且static_assert 触发。但是,如果继承是private,那么std::is_convertible&lt;Derived*, Base&lt;Derived&gt;*&gt;::value == false @CassioNeri 谢谢,这似乎确实可以完成工作!我想知道在处理例如交易时使用std::is_convertible 有多“安全”。使用多重继承......但是,我们不要让问题过于复杂:)(特别是,你为什么使用is_convertible 而不是is_base_of?哦,我明白了,is_base_of 对任何类型的继承都返回 true,而不仅仅是public。)另外,你为什么说你必须在Base 中有一个微不足道的析构函数?在静态断言之后,我似乎可以毫无问题地向它添加东西。 @vsoftco 是的,您可以向析构函数中添加内容。我的观点是,因为你自己定义了析构函数,编译器不会认为它微不足道。因此,如上所述定义析构函数可能会产生成本。如果您出于其他原因必须声明析构函数,则 static_assert 具有“无成本”。如果Base 有构造函数,您也可以将static_assert 放在所有 中以获得相同的效果(如果有多个构造函数,则要付出代码重复的代价)。

以上是关于如何禁止公共继承但允许私有(和受保护)继承的主要内容,如果未能解决你的问题,请参考以下文章

私有继承、公有继承和受保护继承之间的区别

私有继承、公有继承和受保护继承之间的区别

面向对象编程中啥是公共的、私有的和受保护的?

在 C++ 中,为啥语言不强制对类/结构的公共、私有和受保护成员进行分组?

C ++ - 定义 - 私有继承[重复]

多态性和受保护的继承问题