为什么要在C+;中为抽象类声明虚拟析构函数+;? < >我知道在C++中声明基类的虚析构函数是一个好的做法,但声明“代码>虚拟析构函数是否总是重要的,即使对于抽象的类,它也可以作为接口吗?请提供一些原因和示例。
对于界面来说,这更为重要。类的任何用户都可能持有指向接口的指针,而不是指向具体实现的指针。当他们删除它时,如果析构函数是非虚拟的,他们将调用接口的析构函数(或者编译器提供的默认值,如果您没有指定),而不是派生类的析构函数。即时内存泄漏 比如说为什么要在C+;中为抽象类声明虚拟析构函数+;? < >我知道在C++中声明基类的虚析构函数是一个好的做法,但声明“代码>虚拟析构函数是否总是重要的,即使对于抽象的类,它也可以作为接口吗?请提供一些原因和示例。,c++,inheritance,virtual-destructor,C++,Inheritance,Virtual Destructor,对于界面来说,这更为重要。类的任何用户都可能持有指向接口的指针,而不是指向具体实现的指针。当他们删除它时,如果析构函数是非虚拟的,他们将调用接口的析构函数(或者编译器提供的默认值,如果您没有指定),而不是派生类的析构函数。即时内存泄漏 比如说 class Interface { virtual void doSomething() = 0; }; class Derived : public Interface { Derived(); ~Derived() {
class Interface
{
virtual void doSomething() = 0;
};
class Derived : public Interface
{
Derived();
~Derived()
{
// Do some important cleanup...
}
};
void myFunc(void)
{
Interface* p = new Derived();
// The behaviour of the next line is undefined. It probably
// calls Interface::~Interface, not Derived::~Derived
delete p;
}
是的,这总是很重要的。派生类可以分配内存或保留对其他资源的引用,这些资源在对象被销毁时需要清理。如果没有为接口/抽象类提供虚拟析构函数,那么每次通过基类句柄删除派生类实例时,都不会调用派生类的析构函数 因此,您打开了内存泄漏的可能性
class IFoo
{
public:
virtual void DoFoo() = 0;
};
class Bar : public IFoo
{
char* dooby = NULL;
public:
virtual void DoFoo() { dooby = new char[10]; }
void ~Bar() { delete [] dooby; }
};
IFoo* baz = new Bar();
baz->DoFoo();
delete baz; // memory leak - dooby isn't deleted
这并不总是必需的,但我发现这是一种很好的做法。它的作用是允许通过基类型的指针安全地删除派生对象
例如:
Base *p = new Derived;
// use p as you see fit
delete p;
如果
Base
没有虚拟析构函数,则会出现格式错误,因为它会尝试删除对象,就像它是Base*
一样。这不仅是一种好做法。这是任何类层次结构的规则#1
Animal* pAnimal = GetAnimal();
delete pAnimal;
假设动物是一个抽象类。C++知道正确的析构函数调用的唯一方法是通过虚拟方法调度。如果析构函数不是虚拟的,那么它将只调用Animal的析构函数,而不销毁派生类中的任何对象
在基类中使析构函数为虚拟的原因是,它只是从派生类中删除选择。默认情况下,它们的析构函数变为虚拟 你的问题的答案通常是,但并不总是。如果您的抽象类禁止客户端对指向它的指针调用delete(或者如果它在文档中这样说),那么您可以自由地不声明虚拟析构函数 通过保护其析构函数,可以禁止客户端对指向它的指针调用delete。这样工作,省略虚拟析构函数是完全安全和合理的 最终,您将没有虚拟方法表,并通过指向它的指针向您的客户发出使其不可删除的信号,因此在这些情况下,您确实有理由不将其声明为虚拟的
[见本文第4项:我决定做一些研究,并尝试总结您的答案。以下问题将帮助您决定需要哪种析构函数:
- 否:声明公共非虚拟析构函数以避免类*的每个对象上的v指针
- 是:读下一个问题
- 否:尝试通过重新设计类层次结构使基类抽象化
- 是:读下一个问题
- 否:声明受保护的虚拟析构函数以防止不必要的使用
- 是:声明公共虚拟析构函数(在这种情况下没有开销)
- “S. Meyers。更有效的C++,项目33 Addison Wesley,1996。”
- 当然,这个问题的答案
Base *ptr = new Derived();
delete ptr; // Here the call order of destructors: first Derived then Base.
您希望执行上述删除操作,但如果基类的析构函数不是虚拟的,则只调用基类的析构函数,并且派生类中的所有数据都将保持未删除状态。True,事实上,在该示例中,它可能不仅仅是内存泄漏,还可能崩溃:-/使答案正常工作的关键是“不要求删除的。“通常,如果你有一个抽象基类被设计成一个接口,delete将在接口类上被调用。正如上面John指出的,你的建议是非常危险的。您所依赖的假设是,接口的客户端永远不会破坏只知道基类型的对象。如果抽象类是非虚拟的,唯一可以保证的方法是保护抽象类的dtor。Michel,我说过:)“如果这样做,就保护析构函数。如果这样做,客户端将无法使用指向该接口的指针进行删除。”事实上,它并不依赖于客户端,但它必须强制执行,告诉客户“你做不到……”。我看不出有什么危险。我现在纠正了我回答中糟糕的措辞。它现在明确声明它不依赖于客户机。事实上,我认为很明显,依靠客户做一些事情是行不通的。感谢:)+1提到受保护的析构函数,这是在删除指向基类的指针时意外调用错误析构函数的另一个“出路”。是否要将boost::shared_指针p(新派生)修复为boost::shared_指针