C++ 为什么在删除派生类对象时调用基类析构函数(虚拟)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3261694/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why base class destructor (virtual) is called when a derived class object is deleted?
提问by KedarX
A difference between a destructor (of course also the constructor) and other member functions is that, if a regular member function has a body at the derived class, only the version at Derived class gets executed. Whereas in case of destructors, both derived as well as base class versions get executed?
析构函数(当然还有构造函数)和其他成员函数之间的区别在于,如果常规成员函数在派生类中有主体,则只会执行派生类中的版本。而在析构函数的情况下,派生类和基类版本都得到执行?
It will be great to know what exactly happens in case of destructor (maybe virtual) & constructor, that they are called for all its base classes even if the most derived class object is deleted.
很高兴知道在析构函数(可能是虚拟的)和构造函数的情况下到底发生了什么,即使删除了最派生的类对象,它们的所有基类也会被调用。
Thanks in advance!
提前致谢!
采纳答案by Prasoon Saurav
The Standard says
标准说
After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X's direct non-variant members,the destructors for X's direct base classesand, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for X's virtual base classes. All destructors are called as if they were referenced with a quali?ed name, that is, ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed in the reverse order of the completion of their constructor(see 12.6.2). A return statement (6.6.3) in a destructor might not directly return to the caller; before transferring control to the caller, the destructors for the members and bases are called. Destructors for elements of an array are called in reverse order of their construction (see 12.6).
在执行析构函数的主体并销毁主体内分配的任何自动对象后,类 X 的析构函数调用 X 的直接非变体成员的析构函数、X 的直接基类的析构函数,如果 X 是最派生类 (12.6.2),其析构函数调用 X 的虚拟基类的析构函数。调用所有析构函数就好像它们是用限定名称引用的一样,也就是说,忽略更多派生类中任何可能的虚拟覆盖析构函数。基类和成员按其构造函数完成的相反顺序销毁(见 12.6.2)。析构函数中的 return 语句 (6.6.3) 可能不会直接返回给调用者;在将控制权转移给调用者之前,成员和基类的析构函数被调用。数组元素的析构函数按其构造的相反顺序调用(见 12.6)。
Also as per RAIIresources need to be tied to the lifespan of suitable objects and the destructors of respective classes must be called upon to release the resources.
此外,根据RAII资源需要与合适对象的生命周期相关联,并且必须调用相应类的析构函数来释放资源。
For example the following code leaks memory.
例如,以下代码会泄漏内存。
struct Base
{
int *p;
Base():p(new int){}
~Base(){ delete p; } //has to be virtual
};
struct Derived :Base
{
int *d;
Derived():Base(),d(new int){}
~Derived(){delete d;}
};
int main()
{
Base *base=new Derived();
//do something
delete base; //Oops!! ~Base() gets called(=>Memory Leak).
}
回答by stefanB
Constructor and destructor are different from the rest of regular methods.
构造函数和析构函数不同于其他常规方法。
Constructor
构造函数
- can't be virtual
- in derived class you either call explicitly constructor of base class
- or, in case where you don't call base class constructor compiler will insert the call. It will call the base constructor without parameters. If no such constructor exists then you get compiler error.
- 不能是虚拟的
- 在派生类中,您要么显式调用基类的构造函数
- 或者,如果您不调用基类构造函数,编译器将插入调用。它将调用不带参数的基本构造函数。如果不存在这样的构造函数,则会出现编译器错误。
struct A {};
struct B : A { B() : A() {} };
// but this works as well because compiler inserts call to A():
struct B : A { B() {} };
// however this does not compile:
struct A { A(int x) {} };
struct B : A { B() {} };
// you need:
struct B : A { B() : A(4) {} };
Destructor:
析构函数:
- when you call destructor on derived class over a pointer or a reference, where the base class has virtual destructor, the most derived destructor will be called first and then the rest of derived classes in reversed order of construction. This is to make sure that all memory has been properly cleaned. It would not work if the most derived class was called last because by that time the base class would not exists in memory and you would get segfault.
- 当您通过指针或引用在派生类上调用析构函数时,其中基类具有虚拟析构函数,将首先调用最派生的析构函数,然后以相反的构造顺序调用其余的派生类。这是为了确保所有内存都已正确清理。如果最后调用最派生的类,它将不起作用,因为到那时基类将不存在于内存中,并且您会得到段错误。
struct C
{
virtual ~C() { cout << __FUNCTION__ << endl; }
};
struct D : C
{
virtual ~D() { cout << __FUNCTION__ << endl; }
};
struct E : D
{
virtual ~E() { cout << __FUNCTION__ << endl; }
};
int main()
{
C * o = new E();
delete o;
}
output:
输出:
~E
~D
~C
If the method in base class is marked as virtual
all the inherited methods are virtual as well so even if you don't mark the destructors in D
and E
as virtual
they will still be virtual
and they still get called in the same order.
如果在基类中的方法被标记为virtual
所有的继承方法是虚拟的,以及因此,即使您没有标记在析构函数D
,并E
为virtual
他们仍然会virtual
和他们仍然被调用的顺序相同。
回答by Igor Zevaka
This is by design. The destructor on the base class must be called in order for it to release its resources. Rule of thumb is that a derived class should only clean up its own resources and leave the base class to clean up itself.
这是设计使然。必须调用基类的析构函数才能释放其资源。经验法则是派生类应该只清理自己的资源,而让基类清理自己。
From C++ spec:
从C++ 规范:
After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X's direct members, the destructors for X's direct base classes and, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for X's virtual base classes. All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed in the reverse order of the completion of their constructor (see 12.6.2).
在执行析构函数的主体并销毁主体内分配的任何自动对象之后,类 X 的析构函数调用 X 的直接成员的析构函数、X 的直接基类的析构函数,如果 X 是最派生类的类型( 12.6.2),它的析构函数调用 X 的虚拟基类的析构函数。所有析构函数都被调用,就好像它们是用限定名引用的一样,也就是说,忽略更多派生类中任何可能的虚拟覆盖析构函数。基类和成员的销毁顺序与其构造函数完成的顺序相反(见 12.6.2)。
Also, because there is only one destructor, there is no ambiguity as to which destructor a class must call. This is not the case for constructors, where a programmer must pick which base class constructor should be called if there isn't an accessible default constructor.
此外,因为只有一个析构函数,所以类必须调用哪个析构函数没有歧义。这不是构造函数的情况,如果没有可访问的默认构造函数,程序员必须选择应该调用哪个基类构造函数。
回答by Jerry Coffin
Because that's how dtor's work. When you create an object, ctors are invoked starting from the base, and going all the way to the most derived. When you destroy objects (correctly) the reverse happens. The time that making a dtor virtual makes a difference is if/when you destroy an object via a pointer (or reference, though that's fairly unusual) to the base type. In that case, the alternative isn't really that only the derived dtor gets invoked -- rather, the alternative is simply undefined behavior. That make happen to take the form of invoking only the derived dtor, but it might take an entirely different form as well.
因为这就是 dtor 的工作方式。当你创建一个对象时,ctors 从基础开始调用,一直到最派生的。当您(正确)销毁对象时,情况正好相反。使 dtor 虚拟化的时间会有所不同,如果/何时通过指向基类型的指针(或引用,尽管这很不寻常)销毁对象。在这种情况下,替代方案并不是真的只有派生的 dtor 被调用——相反,替代方案只是未定义的行为。这会采取仅调用派生 dtor 的形式,但也可能采用完全不同的形式。
回答by Georg Fritzsche
As Igor says constructors must be called for base classes. Consider what would happen if it wouldn't be called:
正如 Igor 所说,必须为基类调用构造函数。考虑如果不调用它会发生什么:
struct A {
std::string s;
virtual ~A() {}
};
struct B : A {};
If the destructor for A
would not be called when deleting a B
instance, A
would never be cleaned up.
如果A
在删除B
实例时不会调用析构函数,则A
永远不会被清除。
回答by Amardeep AC9MF
A base class destructor may be responsible for cleaning up resources that were allocated by the base class constructor.
基类析构函数可能负责清理由基类构造函数分配的资源。
If your base class has a default constructor (one that doesn't take parameters or has defaults for all its parameters) that constructor is automatically called upon construction of a derived instance.
如果您的基类有一个默认构造函数(一个不带参数或所有参数都有默认值的构造函数),则在构造派生实例时会自动调用该构造函数。
If your base class has a constructor that requires parameters, you must call it manually in the initializer list of the derived class constructor.
如果你的基类有一个需要参数的构造函数,你必须在派生类构造函数的初始化列表中手动调用它。
Your base class destructor will always be automatically called upon deletion of the derived instance since destructors don't take parameters.
您的基类析构函数将始终在删除派生实例时自动调用,因为析构函数不接受参数。
If you're using polymorphism and your derived instance is pointed to by a base class pointer, then the derivedclass destructor is only called if the base destructor is virtual.
如果您使用多态并且您的派生实例由基类指针指向,则仅当基类析构函数是虚拟的时才调用派生类析构函数。
回答by Ben Voigt
When any object is destroyed, destructors run for all sub-objects. This includes both reuse by containment and reuse by inheritance.
当任何对象被销毁时,析构函数会为所有子对象运行。这包括通过包含重用和通过继承重用。