我是否需要第二个接口类的虚拟析构函数?

Sho*_*out 18 c++ inheritance virtual-functions

我有名为“Base”和“Derived”的类。

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

    virtual void DoStuff() = 0;
};
Run Code Online (Sandbox Code Playgroud)

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

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

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

现在让我们介绍一下其他的类,我不知道,Callable还有DerivedCallable

struct Callable
{
    virtual void Call() = 0;
};
Run Code Online (Sandbox Code Playgroud)
struct DerivedCallable : Base, Callable
{
    DerivedCallable() = default;
    ~DerivedCallable() override = default;

    void DoStuff() override { /*...*/ }
    void Call() override { /*...*/ }
};
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)

Derived不继承自CallablecallableAnullptr 也是如此,因此 if 语句不会执行该Call()函数。

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

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

Art*_*yer 19

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

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

如果您要拥有一个DerivedCallable带有指针的对象Callable(或调用astd::unique_ptr<Callable>的其他对象),那么它应该有一个虚拟析构函数。但是,如果您只拥有 的非拥有引用,那么您并不严格需要虚拟析构函数。deleteCallable*Callable*

当类已经有其他虚拟成员时添加虚拟析构函数非常便宜,因此添加它就可以了,这样您就不必担心意外地delete出错。

  • @MarekR 派生类型的原始指针转换为基类型的shared_ptr(如`std::shared_ptr&lt;Base&gt;(new Derived)`)正确存储派生类型的析构函数。如果它作为原始指针转换为基类型,那么您就有一个基类类型的拥有指针,并且需要一个虚拟析构函数。我确实同意拥有一个虚拟析构函数无论如何都是好的(参见最后一点) (4认同)
  • 当原始指针转换为shared_ptr时,存在一个异常,即shared_ptr持有正确的删除器。如果任何其他成员函数是虚拟的,最好有虚拟析构函数。 (3认同)

bit*_*ask 7

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

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

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