前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++核心准则​讨论:将基类的析构函数设为公共和虚拟的,或受保护的和非虚拟的

C++核心准则​讨论:将基类的析构函数设为公共和虚拟的,或受保护的和非虚拟的

作者头像
面向对象思考
发布2020-12-15 15:03:45
1.1K0
发布2020-12-15 15:03:45
举报

Discussion: Make base class destructors public and virtual, or protected and non-virtual

讨论:将基类的析构函数设为公共和虚拟的,或受保护的和非虚拟的

Should destruction behave virtually? That is, should destruction through a pointer to a base class be allowed? If yes, then base's destructor must be public in order to be callable, and virtual otherwise calling it results in undefined behavior. Otherwise, it should be protected so that only derived classes can invoke it in their own destructors, and non-virtual since it doesn't need to behave virtually.

析构函数应该是虚函数吗?也就是说,是否应该允许通过指向基类的指针进行销毁?如果是,则base的析构函数必须是公共的才能被调用,否则虚拟调用它会导致未定义的行为。否则,应该对其进行保护,以便只有派生类才能在自己的析构函数中调用它,这个析构函数也应该是非虚的,因为它不需要虚拟地运行。

Example(示例)

The common case for a base class is that it's intended to have publicly derived classes, and so calling code is just about sure to use something like a shared_ptr<base>:

基类的常见用法是希望它具有公共派生的类,因此调用代码几乎可以确保使用诸如shared_ptr <base>之类的东西:

代码语言:javascript
复制
class Base {
public:
    ~Base();                   // BAD, not virtual
    virtual ~Base();           // GOOD
    // ...
};

class Derived : public Base { /* ... */ };

{
    unique_ptr<Base> pb = make_unique<Derived>();
    // ...
} // ~pb invokes correct destructor only when ~Base is virtual

In rarer cases, such as policy classes, the class is used as a base class for convenience, not for polymorphic behavior. It is recommended to make those destructors protected and non-virtual:

在极少数情况下,例如策略类,为方便起见,该类用作基类,而不是多态行为。建议对那些析构函数进行保护和非虚拟化:

代码语言:javascript
复制
class My_policy {
public:
    virtual ~My_policy();      // BAD, public and virtual
protected:
    ~My_policy();              // GOOD
    // ...
};

template<class Policy>
class customizable : Policy { /* ... */ }; // note: private inheritance
Note(注意)

This simple guideline illustrates a subtle issue and reflects modern uses of inheritance and object-oriented design principles.

这个简单的指南说明了一个微妙的问题,并反映了继承和面向对象设计原则的现代用法。

For a base class Base, calling code might try to destroy derived objects through pointers to Base, such as when using a unique_ptr<Base>. If Base's destructor is public and non-virtual (the default), it can be accidentally called on a pointer that actually points to a derived object, in which case the behavior of the attempted deletion is undefined. This state of affairs has led older coding standards to impose a blanket requirement that all base class destructors must be virtual. This is overkill (even if it is the common case); instead, the rule should be to make base class destructors virtual if and only if they are public.

对于基类Base,调用代码可能会尝试通过指向Base的指针销毁派生对象,例如在使用unique_ptr <Base>时。如果Base的析构函数是公共的和非虚拟的(默认值),则可能会意外地在实际上指向派生对象的指针上调用它,在这种情况下,尝试删除的行为是不确定的。这种情况导致较早的编码标准对所有基类析构函数都必须是虚拟的提出了全面的要求。这太过分了(即使是常见情况);相反,规则应该是当且仅当基类析构函数是公共的时,才将它们虚函数化。

To write a base class is to define an abstraction (see Items 35 through 37). Recall that for each member function participating in that abstraction, you need to decide:

编写基类就是定义一个抽象(请参阅第35到37项)。回想一下,对于参与该抽象的每个成员函数,您需要确定:

  • Whether it should behave virtually or not. 它是否应该表现为虚函数。
  • Whether it should be publicly available to all callers using a pointer to Base or else be a hidden internal implementation detail. 是否应该使用指向Base的指针将其公开给所有调用者使用,还是作为隐藏的内部实现细节。

As described in Item 39, for a normal member function, the choice is between allowing it to be called via a pointer to Base non-virtually (but possibly with virtual behavior if it invokes virtual functions, such as in the NVI or Template Method patterns), virtually, or not at all. The NVI pattern is a technique to avoid public virtual functions.

如第39项所述,对于普通成员函数,选择之间是允许以非虚拟方式(通过指向Base的指针)调用它(但如果它调用虚拟函数(例如在NVI或模板方法模式中),则可能具有虚拟行为) ),实际上还是根本没有。NVI模式是一种避免公开虚函数的技术。

Destruction can be viewed as just another operation, albeit with special semantics that make non-virtual calls dangerous or wrong. For a base class destructor, therefore, the choice is between allowing it to be called via a pointer to Base virtually or not at all; "non-virtually" is not an option. Hence, a base class destructor is virtual if it can be called (i.e., is public), and non-virtual otherwise.

析构可以看作只是另一种操作,尽管具有使非虚调用变得危险或错误的特殊语义。因此,对于基类析构函数,选择是根据是否允许通过指向Base的指针实际上调用它。“非虚”不是一种选择。因此,如果可以调用(即是公共的)基类析构函数,则它是虚拟的,否则是非虚拟的。

Note that the NVI pattern cannot be applied to the destructor because constructors and destructors cannot make deep virtual calls. (See Items 39 and 55.)

注意,NVI模式不能应用于析构函数,因为构造函数和析构函数无法进行深度虚拟调用。(请参阅第39和55条。)

Corollary: When writing a base class, always write a destructor explicitly, because the implicitly generated one is public and non-virtual. You can always=defaultthe implementation if the default body is fine and you're just writing the function to give it the proper visibility and virtuality.

推论:编写基类时,请始终显式编写一个析构函数,因为隐式生成的是公共的和非虚的。如果默认函数就很好,那么您只需要决定器可见性和虚函数性,则实现可以直接使用=default。

Exception(例外)

Some component architectures (e.g., COM and CORBA) don't use a standard deletion mechanism, and foster different protocols for object disposal. Follow the local patterns and idioms, and adapt this guideline as appropriate.

某些组件体系结构(例如COM和CORBA)不使用标准的删除机制,而是使用不同的协议来处理对象。遵循特定情况的模式和习惯用法,并适当修改此准则。

Consider also this rare case:

还要考虑这种罕见的情况:

  • B is both a base class and a concrete class that can be instantiated by itself, and so the destructor must be public for B objects to be created and destroyed. B是可以自己实例化的基类和具体类,因此析构函数必须是公共的,才能创建和销毁B对象。
  • Yet B also has no virtual functions and is not meant to be used polymorphically, and so although the destructor is public it does not need to be virtual. 但是B也没有虚函数,并且不打算被多态使用,因此尽管析构函数是公共的,但它并不需要是虚的。

Then, even though the destructor has to be public, there can be great pressure to not make it virtual because as the first virtual function it would incur all the run-time type overhead when the added functionality should never be needed.

然后,即使析构函数必须是公共的,也可能会面临很大的,不将其虚函数化的压力,因为作为第一个虚拟函数,当永远不需要添加的功能时,它将招致所有运行时类型的开销。

In this rare case, you could make the destructor public and non-virtual but clearly document that further-derived objects must not be used polymorphically as B's. This is what was done with std::unary_function.

在这种罕见的情况下,您可以将析构函数设为公共的和非虚拟的,但要清楚地表明,不允许将衍生出的对象用作B的多态形式。这正是std :: unary_function的功能。

In general, however, avoid concrete base classes (see Item 35). For example, unary_function is a bundle-of-typedefs that was never intended to be instantiated standalone. It really makes no sense to give it a public destructor; a better design would be to follow this Item's advice and give it a protected non-virtual destructor.

但是,通常应避免使用具体的基类(请参阅第35项)。例如,unary_function是typedef的捆绑包,不能独立实例化。给它一个公开的析构函数确实没有任何意义。更好的设计是遵循该产品的建议,为其提供受保护的非虚析构函数。

原文链接https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#discussion-make-base-class-destructors-public-and-virtual-or-protected-and-non-virtual

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 面向对象思考 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Discussion: Make base class destructors public and virtual, or protected and non-virtual
    • Note(注意)
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档