【问题标题】:Is there a way to forbid subclassing of my class?有没有办法禁止我的类的子类化?
【发布时间】:2010-11-17 20:11:55
【问题描述】:

假设我有一个名为“Base”的类,以及一个名为“Derived”的类,它是 Base 的子类,可以访问 Base 的受保护方法和成员。

我现在要做的就是让它没有其他类可以继承 Derived。在 Java 中,我可以通过将 Derived 类声明为“final”来实现这一点。有什么 C++ 技巧可以给我同样的效果吗?

(理想情况下,我想让除 Derived 之外的任何类都不能继承 Base。我不能只将所有代码放入同一个类或使用friend关键字,因为 Base 和 Derived 都是模板化,Base 的模板参数比 Derived 少....)

【问题讨论】:

  • C++ FAQ Lite 对此有 a topic
  • 我会包含一些源代码 - 因为它涉及模板。
  • 确实如此……然而,这些技术似乎都不是完全令人满意的:#1 意味着 Derived 的用户必须使用非标准的习惯用法才能使用该类;他们不能像往常一样直接声明它;相反,您必须调用命名构造函数。 #2 is not 不会在尝试子类化时产生任何编译时错误(这是我正在寻找的效果) #3 将向我的对象添加一个 vtable,我希望避免这种情况。
  • 在这种情况下,您的问题(是否有技术)的答案几乎肯定是“否”。
  • @Jeremy - 没错,正如大卫建议的那样,你可能不走运。

标签: c++ final keyword protected subclassing


【解决方案1】:

您可以为 'Derived' 使用私有构造函数,并为实例化使用公共静态 Create 函数

【讨论】:

    【解决方案2】:

    禁止子类化的最简单方法是将构造函数设为私有:

    class Foo
    {
    private:
        Foo() {}
    
    public:
        static Foo* CreateFoo() { return new Foo; }
    };
    

    编辑:感谢 Indeera 指出这需要一个静态工厂方法

    【讨论】:

    • 啊,但是您将如何创建 Foo 的实例? :)
    • 其实Foo::CreateFoo(),虽然它为什么返回一个指针对我来说是个谜,因为这个类可以完美地复制......
    • 你说的最简单的方法。有哪些替代方案?
    【解决方案3】:

    没有简单而干净的方法。

    标准库所做的只是使析构函数成为非虚拟的。这不会阻止子类化,但它向用户发出了一个强烈的信号,表明它不是为继承而设计的,这意味着在使用派生类时必须非常小心。

    但最终,您是否需要绝对让子类化不可能?表明“从这个类派生是一个坏主意”不是很好吗?

    如果他们真的愿意,人们总是可以破坏你的代码。你能做的最好的就是让他们知道他们应该做什么和不应该做什么,并希望他们不会主动尝试破坏你的代码。

    保护您的代码免受墨菲的侵害,而不是马基雅维利。 ;)

    【讨论】:

    • 是否有工具会在您忽略此“强信号”时发出警告。
    【解决方案4】:

    由于您使用的是模板,我认为您关于防止除 Derived 以外的任何类从 Base 到子类的问题的最后一部分可以使用适当的部分专业化来完成。

    以下代码 sn-p 是我想出的,但所需的复杂性只会加强 jalf 的答案。这值得么?如果这比制定我在实践中会使用的技术更能帮助我理解部分专业化。

    我使用 COMMON 表示 Base 和 Derived 之间的共享模板参数,并使用 EXTRA 表示您所说的 Derived 具有的额外参数。这些的实际数量可能是我碰巧分别为它们选择了一个和两个。

    // Forward declaration of class Derived
    template< class COMMON
            , class EXTRA1
            , class EXTRA2 >
    class Derived;
    
    
    // Definition of general class template Base
    template< class SUBCLASS
            , class COMMON >
    class Base
    {
    private:
        Base() {}
    };
    
    
    // Definition of partial specialisation of template class Base to open up
    // access to the constructor through friend declaration.
    template< class COMMON
            , class EXTRA1
            , class EXTRA2 >
    class Base< Derived< COMMON, EXTRA1, EXTRA2 >
              , COMMON >
    {
    private:
        Base() {}
    
        friend class Derived< COMMON, EXTRA1, EXTRA2 >;
    };
    
    
    // Definition of class Derived
    template < class COMMON
             , class EXTRA1
             , class EXTRA2 >
    class Derived
        : public Base< Derived< COMMON, EXTRA1, EXTRA2 >
                     , COMMON >
    {
    public:
        static Derived* create() { return new Derived; }
    
    private:
        Derived() : Base< Derived< COMMON, EXTRA1, EXTRA2 >
                        , COMMON >()
        {
        }
    };
    
    
    // Definition of class HonestDerived.
    // It supplies itself as the SUBCLASS parameter to Base.
    template < class COMMON
             , class EXTRA1
             , class EXTRA2 >
    class HonestDerived
        : public Base< HonestDerived< COMMON, EXTRA1, EXTRA2 >
                     , COMMON >
    {
    public:
        HonestDerived() : Base< HonestDerived< COMMON, EXTRA1, EXTRA2 >
                              , COMMON >()
        {
        }
    };
    
    
    // Definition of class DishonestDerived
    // It supplies Derived rather than itself as the SUBCLASS parameter to Base.
    template < class COMMON
             , class EXTRA1
             , class EXTRA2 >
    class DishonestDerived
        : public Base< Derived< COMMON, EXTRA1, EXTRA2 >
                     , COMMON >
    {
    public:
        DishonestDerived() : Base< Derived< COMMON, EXTRA1, EXTRA2 >
                                 , COMMON >()
        {
        }
    };
    
    
    template< class COMMON, class EXTRA1, class EXTRA2 >
    class DerivedFromDerived
        : public Derived< COMMON, EXTRA1, EXTRA2 >
    {
    public:
        DerivedFromDerived() : Derived< COMMON, EXTRA1, EXTRA2 >()
        {
        }
    };
    
    // Test partial specialisation gives Derived access to the Base constructor
    Derived< int, float, double >* derived
        = Derived< int, float, double >::create();
    
    // Test that there is no access to the Base constructor for an honest subclass
    // i.e. this gives a compiler error
    HonestDerived< int, float, double > honestDerived;
    
    // Test that there is no access to the Base constructor for a dishonest subclass
    // i.e. this gives a compiler error
    DishonestDerived< int, float, double > dishonestDerived;
    
    // Test that there is no access to the Derived constructor
    // i.e. this gives a compiler error
    DerivedFromDerived< int, float, double > derivedFromDerived;
    

    此代码已使用 gcc 4.3.2 测试。

    请注意,友元声明的替代方法是使构造函数在 Base 的部分特化中受到保护,但这样会允许像 DishonestDerived 这样的类工作。

    【讨论】:

      【解决方案5】:

      从 C++11 开始,您可以将 final 关键字(技术上是一个特殊的标识符,因为它实际上不是关键字)添加到您的类中,例如

      class Derived final
      {
      ...
      

      你可以在http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final阅读更多关于final关键字的信息

      【讨论】:

      • +1 使用 C++11 提供此解决方案很有趣。指向更详细描述的附加链接将使此答案更有帮助。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-27
      • 1970-01-01
      • 2023-03-16
      • 2012-11-14
      • 2010-09-06
      • 2019-09-26
      • 1970-01-01
      相关资源
      最近更新 更多