【问题标题】:Do I need a virtual destructor for a second interface class?我需要第二个接口类的虚拟析构函数吗?
【发布时间】:2023-02-01 23:07:37
【问题描述】:

我有名为“Base”和“Derived”的课程。

struct Base {
    Base() = default;
    virtual ~Base() = default;
    Base(const Base&) = delete;
    Base& operator=(const Base&) = delete;

    virtual void DoStuff() = 0;
};

“Base”类需要虚拟析构函数,这是可以理解的。我也不允许复制这个类

struct Derived : Base {
    Derived() = default;
    ~Derived() override = default;

    void DoStuff() override { /*...*/ }
};
int main()
{
    std::shared_ptr<Base> a = std::make_shared<Derived>();
    a->DoStuff();
    return 0;
}

现在让我们介绍其他类,我不知道,CallableDerivedCallable

struct Callable
{
    virtual void Call() = 0;
};
struct DerivedCallable : Base, Callable
{
    DerivedCallable() = default;
    ~DerivedCallable() override = default;

    void DoStuff() override { /*...*/ }
    void Call() override { /*...*/ }
};
int main()
{
    std::shared_ptr<Base> a = std::make_shared<Derived>();
    a->DoStuff();

    {
        auto callableA = std::dynamic_pointer_cast<DerivedCallable>(a);
        if(callableA) {
            callableA->Call();
        }
    }

    std::shared_ptr<Base> b = std::make_shared<DerivedCallable>();
    b->DoStuff();
    
    {
        auto callableB = std::dynamic_pointer_cast<DerivedCallable>(b);
        if(callableB) {
            callableB->Call();
        }
    }

    return 0;
}

Derived没有继承自Callable,所以callableA是nullptr,因此if语句不会执行Call()函数。

另一方面,DerivedCallable 继承自 Callablestd::dynamic_pointer_cast 会将对象的引用计数增加到 2,因此当 callableB 超出范围时,对象不会被释放,只有引用计数将减少到 1,然后主函数将释放 b

Callable 需要有一个虚拟析构函数吗?

【问题讨论】:

  • 自己看看:en.cppreference.com/w/cpp/types/is_polymorphic。在你的类型上使用它。顺便问个好问题,+1
  • @Edziju Base& operator(const Base&) = delete; 是什么?和派生()=默认值;派生()覆盖=默认;?
  • 我的错,应该是operator=~Derived() override = default

标签: c++


【解决方案1】:

如果您打算通过基类指针删除派生类对象,则只需要一个虚拟析构函数。

由于您使用的是std::shared_ptr,因此您不需要任何虚拟析构函数,因为 shared_ptr 存储了一个正确类型的删除器(无论您如何转换)。

如果你打算拥有一个带有Callable指针的DerivedCallable对象(std::unique_ptr&lt;Callable&gt;或其他在Callable*上调用delete的东西),那么它应该有一个虚拟析构函数。但是,如果您只有 Callable* 的非拥有引用,那么您并不严格需要虚拟析构函数。

当类已经有其他虚拟成员时添加一个虚拟析构函数是非常便宜的,所以添加它很好,这样您就不必担心不小心 delete-ing 出错了。

【讨论】:

  • 我也那么认为。只需要确保:D。谢谢
【解决方案2】:

这取决于。理论上,Base 不会需要一个虚拟析构函数。当你拖着一个动态类型与其静态类型不同的对象时,你需要一个析构函数是虚拟的。

在您的示例中,您有真正指向 Derivied 对象的 Base 指针。如果您不将 ~Base() 设为虚拟,那么销毁该对象将表现出未定义的行为——可能是因为无法销毁该对象的 Derived 部分。

因此,只要您不打算通过特定基类拥有一个(拥有!)指向您的对象的指针,该基类的析构函数就不需要是虚拟的。

【讨论】:

  • “未能销毁对象的派生部分。”.相反,它会是 UB,即使在实践中,很可能就是这种行为。
  • 我也那么认为。只需要确保:D。谢谢
  • @Jarod42 是的。固定的。
猜你喜欢
  • 2011-08-02
  • 2010-10-15
  • 2021-11-06
  • 2011-02-03
  • 2011-03-03
  • 2015-09-02
  • 1970-01-01
  • 2011-03-30
  • 1970-01-01
相关资源
最近更新 更多