【问题标题】:How to define sealed class in C++?如何在 C++ 中定义密封类?
【发布时间】:2011-06-10 10:11:07
【问题描述】:

如何阻止类被其他类继承。

【问题讨论】:

  • 恕我直言,使用 /*final*/ class 随便 {...} 就足够了
  • @baris.aydinoz: 不,这无助于编译器推断T 中的一个(n 被覆盖的)虚方法不会在其他任何地方被覆盖,因此调用它需要一个虚查找(如果您指定 final,则没有必要)。

标签: c++ class inheritance derived-class sealed


【解决方案1】:

C++11解决方案

在 C++11 中,您可以通过在定义中使用 final 关键字来封装一个类:

class A final  //note final keyword is used after the class name
{
   //...
};

class B : public A  //error - because class A is marked final (sealed).
{                   //        so A cannot be derived from.
   //...
};

要了解 final 的其他用途,请在此处查看我的答案:


C++03解决方案

Bjarne Stroustrup's code : 我可以阻止人们从我的班级派生吗?

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

通用锁

所以我们可以利用模板来使Usable_lock 足够通用以密封任何类:

template<class T>
class  Generic_lock 
{
    friend T;
    Generic_lock() {}                     //private
    Generic_lock(const Generic_lock&) {}  //private
};

class Usable : public virtual Generic_lock<Usable>
{
public:
    Usable() {}
};

Usable a; //Okay
class DD : public Usable { };

DD dd; //Not okay!

【讨论】:

  • 为什么要在Usable_lock中说private:
  • @Mehrdad:哦……我实际上并没有注意到 private 实际上不需要。毕竟,我在这里使用class 关键字(不是struct)。也许,我只是不敢修改Bjarne's code 的通用性。
  • 请注意,friend T; 仅适用于 C++11,尽管 MSVC 允许在 C++03 中将其作为扩展。
  • 如果我使用私有继承怎么办?class Usable : private virtual Usable_lock
  • 这里私有继承可以吗?实际上,我已经测试过私有虚拟继承可以正常工作。我可以说在这里使用私有继承会更合理吗?毕竟,您不希望有人使用Base* 来操作A 对象,Base 类不是为OOP 设计的。它只是一个实用程序。拜托了。
【解决方案2】:

有两种方法,一种是简单便宜的,一种是正确的。 @Naveen 和 @Nawaz 的两个答案是正确的,这需要为您实际想要密封的每个类手动创建一个 sealer 类。

在 adobe 库中使用的并非万无一失的方法是为此使用模板类。问题是您不能将模板参数声明为朋友,这意味着您将不得不从private 切换到不太安全的protected

template <typename T>
class sealer {
protected: sealer() {}
};
class sealed : virtual sealer<sealed> {};

您可以使用宏自动执行它(我不记得 Adob​​e 代码中宏的确切风格):

#define seal( x ) virtual sealer<x>
class sealed : seal(sealed) 
{};

现在这将抓住那些在不知道自己不应该继承的情况下错误地尝试继承的人:

class derived : sealed {};
int main() {
   derived d;  // sealer<T>::sealer() is protected within this context
}

但它不会阻止真正想要派生的人,因为他们可以通过自己从模板派生来访问构造函数:

class derived : sealed, sealer<sealed> {};
int main() {
   derived d;
};

我不确定这是否会在 C++0x 中改变,我想我记得一些关于是否允许类模板与它的参数之一成为朋友的讨论,但在粗略搜索草稿时,我真的无法确定.如果允许,那么这将是一个很好的通用解决方案:

template <typename T>
class sealer {
   sealer() {}
   friend class T; // Incorrect in C++03
};

【讨论】:

  • protected 不适用于 GCC(未启用 C++98、C++11、C++14)。想知道为什么它应该:derived 继承* sealer&lt;sealed&gt;,实际上,因此直接因此需要初始化它 - 但构造函数仅受保护......
【解决方案3】:

C++11 增加了防止从类继承或简单地防止在派生类中覆盖方法的能力。这是通过特殊标识符final 完成的。例如:

class Base final { };

class Derived1 : Base { }; // ill-formed because the class Base has been marked final

class Base {
    virtual void f() final;
};

class Derived : Base {
    void f(); // ill-formed because the virtual function Base::f has been marked final

请注意,final 不是语言关键字。它在技术上是一个标识符;它只有在那些特定的上下文中使用时才会获得特殊的含义。在任何其他位置,它都可以是有效的标识符。

【讨论】:

    【解决方案4】:

    基于 Bjarne Stroustrup 的 http://www.stroustrup.com/bs_faq2.html#no-derivation 常见问题解答 没有使用朋友关键字的小修改:

    // SEALED CLASS DEFINITIONS
    class Usable_lock {
    protected:
        Usable_lock() {}
        Usable_lock(const Usable_lock&) {}
    };
    #define sealed_class private virtual Usable_lock
    
    // SEALED CLASS USAGE EXMAPLES
    class UsableLast : sealed_class {
    public:
        UsableLast(){}
        UsableLast(char*){}
    };
    class DD : public UsableLast {};
    
    // TEST CODE
    template <class T> T createInstance() {
        return T();
    }
    int main()
    {
        createInstance<UsableLast>();
    //  createInstance<DD>();
        return 0;
    }
    

    【讨论】:

      【解决方案5】:

      以下代码展示了如何在 C++/CLI 中定义密封类。

      class A sealed
      {
          //here goes the class code
      };
      
      class B : public A
      {
      };
      

      现在 B :不能从 A 继承,因为它已被声明为“密封”。也可以在这里找到关于sealed关键字的详细解释http://msdn.microsoft.com/en-us/library/0w2w91tf.aspx

      更新:添加了 C++/CLI ,其他答案也显示了使用 final 关键字实现相同功能的最新 C++11 方法。

      【讨论】:

      • 这是用于 C++/CLI,而不是标准 C++。
      • 您应该提到这是针对 C++/CLI 的。可移植的 C++ 方式是在类名之后指定 final。
      【解决方案6】:

      你不能。 C++ 不是 Java 或 C#。恕我直言,也没有任何意义。

      【讨论】:

      • @moo-juice 如果你没有在你的类中定义任何虚函数,继承它会带来什么危害?
      • 要么从给定的类继承是有用的,那么你为什么要拒​​绝它呢?或者它没有用,没有人会这样做。
      • @wilx: 或者有人可能认为它很有用,即使这样做不安全...假设您的库有一个 X 容器,由指针保存并且 X 没有虚函数 也不是虚拟析构函数。如果用户代码扩展 X 并设法将扩展类的实例放入容器中,并且如果容器清理代码 deleted 指针,那么您将遇到未定义的行为——可能会泄漏资源或死亡。仅仅因为语言允许你做某事,并不意味着它是一个好主意。
      • 这种使用模式的一个常见示例是标准容器。它们不是为扩展而设计的,但仍然有很多人会从它们中派生出来添加功能。如果您通过从std::vector 继承来创建myvector,并通过指向std::vector 的指针删除,那么您将遇到未定义的行为。
      • 在某些情况下,从标准容器继承是可能的并且很有用。让它们成为“最终”将是可怕的损失。这一切都归结为你必须小心,你必须知道你在做什么。这就是 C++ 的精神。你可以轻而易举地射杀自己,但你也可以做出伟大而有用的事情。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-24
      • 2022-08-05
      • 1970-01-01
      • 2011-04-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-26
      相关资源
      最近更新 更多