来自类实现接口的析构函数在引用为接口时未调用

Chr*_*vic 2 c++ virtual destructor interface c++11

我有一个基本的界面(在Visual Studio 2013中使用Microsofts C++语法),它公开了这样的简单函数:

__interface IDisposable {
    void Dispose();
};
__interface IBase : public IDisposable {
    void Foo();
    void Bar();
};
Run Code Online (Sandbox Code Playgroud)

有一种特定类型的类必须继承这些方法,因此这些类具有以下结构:

class Derived : public IBase {
public:
    Derived();
    ~Derived();
    void Dispose();
    void Foo();
    void Bar();
}
Run Code Online (Sandbox Code Playgroud)

这些类也有一些变量,我想使用智能指针,但这些指针确实产生泄漏,因为~Derived()在下面的场景中没有调用析构函数:

  • 我有一个Stack<IBase*>用来处理一堆这些对象(例如Derived,Derived2等).
  • 一旦pCurrent应该从堆栈中删除最顶层的元素()与相应的数据(m_Data)我调用处理资源的方法:((IDisposable*)pCurrent->m_Data)->Dispose();
  • 这个调用之后是一个delete pCurrent->m_Data显然尝试调用~IBase()哪个不明显存在,因为它是一个接口.因此,所提到的~Derived()也不会被调用,并且任何智能指针Derived也不会被删除,这会导致严重的内存泄漏.

令人惊讶的是手动删除工作:

auto p = new Derived();
delete p; // ~Derived() is properly called as we are not handling a IBase* object
Run Code Online (Sandbox Code Playgroud)

我在考虑使用虚拟析构函数,但是没有办法在这些接口中定义析构函数来实现~Derived()虚拟析构函数.

是否有更好的approch能够调用正确的析构函数?

dte*_*ech 5

我在评论中提到的问题是,在多态层次结构的底部没有虚拟析构函数,因此在删除IBase指针时会出现未定义的行为.

它适用于你的孤立示例,因为auto在这种情况下推断出一个Derived *类型,因此在这个"静态上下文"中调用适当的析构函数.

但一般来说,您应该考虑根据标准,删除没有虚拟析构函数的基类指针是UNDEFINED BEHAVIOR.这意味着"任何事情"都可能发生,通常最终发生的事情是调用基类类型的析构函数,这意味着对于层次结构中的每个其他类型,您都没有获得正确的破坏行为.

至于使用MS __interface- 除非你真的必须这样做,否则你不必处理它所施加的限制.我建议你远离MS的语言"扩展",并坚持使用旧的标准和可移植的C++,它适用于那里的每个编译器.无论如何,C++中没有接口.

你应该设计你的层次结构,这样你绝对有一个虚拟析构函数,如果不是在最底层,那么至少在你将使用的"基础级别"指针.要么做IBase一个class虚拟析构函数,或者做一个class Base : public IBase有一个,并使用它作为你的"多态根".

struct A {
  ~A() { std::cout << "destroying A" << std::endl; }
};

struct Base {
  ~Base() { std::cout << "destroying Base" << std::endl; }
};

struct Derived : Base {
  ~Derived() { std::cout << "destroying Derived" << std::endl; }
  A a;
};
Run Code Online (Sandbox Code Playgroud)

然后:

  Base * p = new Derived;
  delete p; // destroying Base - bad bad, Derived is not destroyed, neither is A
Run Code Online (Sandbox Code Playgroud)

但它只需要更改为~Base()虚拟并获得预期的输出:

destroying Derived
destroying A
destroying Base
Run Code Online (Sandbox Code Playgroud)

通常,您不要在接口上建立类(如"用于基础"),只是为了扩展它们.

问题是当你在多态的级别上有一个虚拟析构函数时,它会从vtable中调用,这意味着它将为每个唯一类型调用适当的析构函数,因为每个唯一类型都有自己的专用vtable.如果你没有虚拟析构函数,那么标准会说"未定义的行为"会让不同的编译器供应商选择这样做,并且他们最终做了最合乎逻辑的事情 - 调用析构函数来获取指针的类型.你不能要求更多......然而这是不可取的,因为你最终会在基类型之上跳过每个成员或继承类型的所有销毁.