虚拟析构函数如何工作?
几个小时前,我正在摆弄内存泄漏问题,结果发现我确实弄错了一些有关虚拟析构函数的基本知识!让我解释一下我的课程设计。
class Base
{
virtual push_elements()
{}
};
class Derived:public Base
{
vector<int> x;
public:
void push_elements(){
for(int i=0;i <5;i++)
x.push_back(i);
}
};
void main()
{
Base* b = new Derived();
b->push_elements();
delete b;
}
边界检查器工具报告派生类向量中存在内存泄漏。我发现析构函数不是虚拟的,并且派生类析构函数没有被调用。当我将析构函数设为虚拟时,它令人惊讶地得到了修复。即使不调用派生类析构函数,向量也不会自动释放吗?这是 BoundsChecker 工具的怪癖还是我对虚拟析构函数的理解错误?
Few hours back I was fiddling with a Memory Leak issue and it turned out that I really got some basic stuff about virtual destructors wrong! Let me put explain my class design.
class Base
{
virtual push_elements()
{}
};
class Derived:public Base
{
vector<int> x;
public:
void push_elements(){
for(int i=0;i <5;i++)
x.push_back(i);
}
};
void main()
{
Base* b = new Derived();
b->push_elements();
delete b;
}
The bounds checker tool reported a memory leak in the derived class vector. And I figured out that the destructor is not virtual and the derived class destructor is not called. And it surprisingly got fixed when I made the destructor virtual. Isn't the vector deallocated automatically even if the derived class destructor is not called? Is that a quirk in BoundsChecker tool or is my understanding of virtual destructor wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
当基类没有虚拟析构函数时,通过基类指针删除派生类对象会导致未定义的行为。
您所观察到的(对象的派生类部分永远不会被销毁,因此其成员永远不会被释放)可能是许多可能行为中最常见的,并且是一个很好的例子,说明为什么确保析构函数很重要当您以这种方式使用多态性时,虚拟的。
Deleting a derived-class object through a base-class pointer when the base class does not have a virtual destructor leads to undefined behavior.
What you've observed (that the derived-class portion of the object never gets destroyed and therefore its members never get deallocated) is probably the most common of many possible behaviors, and a good example of why it's important to make sure your destructors are virtual when you use polymorphism this way.
如果基类没有虚拟析构函数,那么代码的结果是未定义的行为,不一定是调用了错误的析构函数。这大概就是 BoundsChecker 正在诊断的内容。
If the base class does not have a virtual destructor, then the result of your code is undefined behaviour, not necessarily the wrong destructor being called. This is presumably what BoundsChecker is diagnosing.
尽管这在技术上是不确定的,但您仍然需要了解最常见的故障方法才能诊断它。常见的失败方法是调用错误的析构函数。我不知道任何实现会以任何其他方式失败,尽管我承认我只使用两种实现。
发生这种情况的原因与当您尝试覆盖非虚拟成员函数并通过基指针调用它时调用“错误”函数的原因相同。
Although this is technically undefined, you still need to know the most common method of failure in order to diagnose it. That common method of failure is to call the wrong destructor. I don't know of any implementation that will fail in any other manner, though admittedly I only use two implementations.
The reason this happens is the same reason the 'wrong' function will get called when you try to override a non-virtual member function and call it through a base pointer.
如果析构函数不是虚拟的,则将调用 Base 析构函数。基础析构函数清理基础对象并完成。基对象析构函数无法了解派生对象,它必须调用派生析构函数,而与任何函数一样,实现这一点的方法是将析构函数设为虚拟。
If the destructor is not virtual then the Base destructor will be called. The base destructor cleans up the Base object and finishes. There is no way for the base object destructor to know about the derived object, it must be the derived destructor called, and the way to do that, as with any function, is to make the destructor virtual.
来自 C++ FAQ Lite:“我的析构函数什么时候应该是虚拟的?”请在此处阅读。
(顺便说一句,C++ FAQ Lite 是解答与 C++ 相关的所有问题的绝佳来源)。
From the C++ FAQ Lite: "When should my destructor be virtual?" Read it here.
(C++ FAQ Lite is an excellent source for all your questions related to C++, by the way).
在 C++ 中,平凡析构函数是一个递归定义的概念——当类的每个成员(以及每个基类)都有一个平凡析构函数时,它是编译器为您编写的析构函数。 (有一个类似的概念称为平凡构造函数。)
当具有非平凡析构函数的对象包含在对象中时(如示例中的
vector
),则外部对象的析构函数(如您的 < code>Derived) 不再是微不足道的。即使您没有编写析构函数,C++ 编译器也会自动编写一个析构函数,该析构函数调用任何具有析构函数的成员的析构函数。因此,即使您没有编写任何内容,编写非虚拟析构函数的注意事项仍然适用。
In C++, a trivial destructor is a recursively defined concept -- it's a destructor that the compiler wrote for you when every member of the class (and every base class) has a trivial destructor. (There's a similar concept called the trivial constructor.)
When an object with a nontrivial destructor is included in an object (like the
vector
in your example), then the destructor of the outside object (like yourDerived
) in is no longer trivial. Even though you didn't write destructor, the C++ compiler automatically wrote a destructor that calls the destructors of any members that have destructors.So, even though you didn't write anything, the caveats of writing a non-virtual destructor still apply.
如果您来自 c#,那么您想知道为什么向量不会自动取消分配是对的。但在 C++ 中,除非您使用 Microsoft Manged Exesions to C++ (C++/CLI),否则自动内存管理不可用。
由于基类中没有虚拟的析构函数,因此派生类对象将永远不会被释放,从而泄漏为派生类的向量数据成员分配的内存。
if you are coming from c#, then you were right in wondering why vector is not automatically de-allocated. But in c++, automatic memory management is not availble unless you use the Microsoft Manged Extesions to C++ (C++/CLI).
since there is no destructor in Base class that is virtual, the derived class object will never be freed and there-by you leak the memory allocated for the vector data member of the the derived class.
析构函数是类的成员函数,其名称与类名相同,前面带有波形符(~)。析构函数用于当对象超出范围时销毁类的对象,或者您可以说所有类销毁的清理都在析构函数中完成。当对象超出范围时,在类中构造对象期间分配的所有内存都会被破坏(或内存释放)。
通过 BoundsCheck 上的示例查找更多详细信息
Destructor is the member function of the class whose name is the same name of the class name and it is preceded by the tilde sign(~). Destructor is used to destroy the object of the class when object goes out of scope or you can say that all clean up of class destruction are to be done in destructor. All the memory gets allocated during construction of the object in class gets destructed (or memory release) when object goes out of scope.
Find more details with example on BoundsCheck