class 中实现接口的析构函数在引用为接口时未调用
Destructor from class implementing interface not called when referencing as interface
我有一个基本接口(在 Visual Studio 2013 中使用 Microsoft 的 C++ 语法作为接口),它公开了像这样的简单函数:
__interface IDisposable {
void Dispose();
};
__interface IBase : public IDisposable {
void Foo();
void Bar();
};
有一种特定类型的 类 必须继承这些方法,因此这些 类 具有以下结构:
class Derived : public IBase {
public:
Derived();
~Derived();
void Dispose();
void Foo();
void Bar();
}
这些 类 确实也有一些变量,我想使用智能指针,但这些指针确实会产生泄漏,因为在以下情况下未调用析构函数 ~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
我正在考虑使用虚拟析构函数,但是没有办法在这些接口中定义析构函数来使 ~Derived()
虚拟。
是否有更好的方法来调用正确的析构函数?
执行此操作的通常方法是使调用 delete 的任何类型都具有 virtual
析构函数,如:
virtual ~IDisposable() = default;
根据 C++ 的规则,所有派生的 类 也将有一个 virtual
析构函数。
我的建议是将结果也存储在 unique_ptr
中,这样您就不必自己调用 delete
,如:
std::unique_ptr<IDisposable> p(new Derived());
一种侵入性较小的方法是,您可以使用 shared_ptr
来保存对象,而不是声明析构函数 virtual
,因为它使用不同的方法(模板化构造函数和类型擦除来保持跟踪实际对象类型)以正确调用析构函数,如:
std::shared_ptr<IDisposable> p = std::make_shared<Derived>();
由于我不确定 __interface
关键字的作用(这是 Microsoft 特有的),您可能需要那样做,但是 virtual
析构函数与 unique_ptr
解决方案是首选,因为它不会将引用计数添加到组合中。
我在评论中提到的问题是你在多态层次结构的底部没有虚拟析构函数,因此当你删除 IBase
指针时你会得到未定义的行为。
它在您的孤立示例中有效,因为 auto
在这种情况下推断出 Derived *
类型,因此在此 "static context".
中调用了适当的析构函数
但一般来说,您应该考虑根据标准,删除一个没有虚析构函数的基 class 指针是未定义的行为。这意味着 "anything" 可能发生,通常最终发生的是调用基 class 类型的析构函数,这意味着对于层次结构中的每个其他类型,您都没有获得正确的析构行为。
至于使用 MS 的 __interface
- 除非你真的必须这样做,否则你将不必处理它强加的限制。我建议您远离 MS 语言 "extensions" 并坚持使用良好的旧标准和可移植 C++,它适用于所有编译器。无论如何,C++ 中没有接口。
你应该设计你的层次结构,这样你绝对有一个虚拟析构函数,如果不是在最底部,那么至少在你将使用的 "base level" 指针处。使用虚拟析构函数使 IBase
成为 class
,或者使 class Base : public IBase
具有虚拟析构函数,并将其用作您的 "polymorphic root".
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;
};
然后:
Base * p = new Derived;
delete p; // destroying Base - bad bad, Derived is not destroyed, neither is A
但只需要将 ~Base()
更改为虚拟,您就会得到预期的输出:
destroying Derived
destroying A
destroying Base
通常,您不基于(如 "use for foundation")classes 在接口上,只是为了扩展它们。
问题在于,当您在多态级别拥有虚拟析构函数时,它会从 vtable 中调用,这意味着它将为每个唯一类型调用适当的析构函数,因为每个唯一类型都有自己的析构函数专用虚表。如果你没有虚拟析构函数,标准说 "undefined behavior" 这让不同的编译器供应商可以随心所欲地选择,他们最终会做最合乎逻辑的事情 - 调用析构函数的类型指针。你不能要求更多......但它是不可取的,因为你最终会跳过基本类型之上的每个成员或继承类型的所有破坏。
我有一个基本接口(在 Visual Studio 2013 中使用 Microsoft 的 C++ 语法作为接口),它公开了像这样的简单函数:
__interface IDisposable {
void Dispose();
};
__interface IBase : public IDisposable {
void Foo();
void Bar();
};
有一种特定类型的 类 必须继承这些方法,因此这些 类 具有以下结构:
class Derived : public IBase {
public:
Derived();
~Derived();
void Dispose();
void Foo();
void Bar();
}
这些 类 确实也有一些变量,我想使用智能指针,但这些指针确实会产生泄漏,因为在以下情况下未调用析构函数 ~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
我正在考虑使用虚拟析构函数,但是没有办法在这些接口中定义析构函数来使 ~Derived()
虚拟。
是否有更好的方法来调用正确的析构函数?
执行此操作的通常方法是使调用 delete 的任何类型都具有 virtual
析构函数,如:
virtual ~IDisposable() = default;
根据 C++ 的规则,所有派生的 类 也将有一个 virtual
析构函数。
我的建议是将结果也存储在 unique_ptr
中,这样您就不必自己调用 delete
,如:
std::unique_ptr<IDisposable> p(new Derived());
一种侵入性较小的方法是,您可以使用 shared_ptr
来保存对象,而不是声明析构函数 virtual
,因为它使用不同的方法(模板化构造函数和类型擦除来保持跟踪实际对象类型)以正确调用析构函数,如:
std::shared_ptr<IDisposable> p = std::make_shared<Derived>();
由于我不确定 __interface
关键字的作用(这是 Microsoft 特有的),您可能需要那样做,但是 virtual
析构函数与 unique_ptr
解决方案是首选,因为它不会将引用计数添加到组合中。
我在评论中提到的问题是你在多态层次结构的底部没有虚拟析构函数,因此当你删除 IBase
指针时你会得到未定义的行为。
它在您的孤立示例中有效,因为 auto
在这种情况下推断出 Derived *
类型,因此在此 "static context".
但一般来说,您应该考虑根据标准,删除一个没有虚析构函数的基 class 指针是未定义的行为。这意味着 "anything" 可能发生,通常最终发生的是调用基 class 类型的析构函数,这意味着对于层次结构中的每个其他类型,您都没有获得正确的析构行为。
至于使用 MS 的 __interface
- 除非你真的必须这样做,否则你将不必处理它强加的限制。我建议您远离 MS 语言 "extensions" 并坚持使用良好的旧标准和可移植 C++,它适用于所有编译器。无论如何,C++ 中没有接口。
你应该设计你的层次结构,这样你绝对有一个虚拟析构函数,如果不是在最底部,那么至少在你将使用的 "base level" 指针处。使用虚拟析构函数使 IBase
成为 class
,或者使 class Base : public IBase
具有虚拟析构函数,并将其用作您的 "polymorphic root".
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;
};
然后:
Base * p = new Derived;
delete p; // destroying Base - bad bad, Derived is not destroyed, neither is A
但只需要将 ~Base()
更改为虚拟,您就会得到预期的输出:
destroying Derived
destroying A
destroying Base
通常,您不基于(如 "use for foundation")classes 在接口上,只是为了扩展它们。
问题在于,当您在多态级别拥有虚拟析构函数时,它会从 vtable 中调用,这意味着它将为每个唯一类型调用适当的析构函数,因为每个唯一类型都有自己的析构函数专用虚表。如果你没有虚拟析构函数,标准说 "undefined behavior" 这让不同的编译器供应商可以随心所欲地选择,他们最终会做最合乎逻辑的事情 - 调用析构函数的类型指针。你不能要求更多......但它是不可取的,因为你最终会跳过基本类型之上的每个成员或继承类型的所有破坏。