我有两个班,而B
派生A
。
我创建了一个指向A
的指针,名为a2
。请查看我的main
功能。
这条线做什么?
a2 = new B();
为什么当我删除a2
时,只激活了A的析构函数?
#include <iostream>
using namespace std;
class A {
int num;
public:
A() { cout << "constructor of A\n"; }
void set_num(int new_num) { cout << "set_num of A" << endl; num = new_num; }
void print() { cout << "print of A: "; cout << num << endl; }
~A() { cout << "destructor of A\n"; }
};
class B: public A {
int num;
public:
B() { cout << "constructor of B\n"; }
void set_num(int new_num) { cout << "set_num of B" << endl; num = new_num; }
void print() { cout << "print of B: "; cout << num << endl; }
~B() { cout << "destructor of B\n"; }
};
void main() {
A *a2;
a2 = new B();
delete a2;
}
这是输出:
constructor of A
constructor of B
destructor of A
每个帮助表示赞赏!
答案 0 :(得分:1)
答案 1 :(得分:1)
如果要通过指向基础子对象的指针删除对象,则基类的析构函数必须虚拟:
class A
{
public:
virtual ~A() { /* ... */ }
// ...
};
(这会自动使所有派生类析构函数都是虚拟的,所以你不必拼写它。)
原因是C ++ 11标准中的第5.3.5 / 3条:
如果要删除的对象的静态类型与其不同 动态类型,静态类型应该是要删除的对象的动态类型的基类 静态类型应具有虚拟析构函数或行为未定义。
我确信你可以想象为什么虚拟析构函数是,但是对于一个简单的例子,想象指针到基础(就像你的a2
)甚至不需要在数字上与指向acutal对象的指针 - 但是你需要确切的指针来释放内存。很明显,在找出正确的地址的过程中需要一些魔力。虚拟析构函数融入了这种魔力。
答案 2 :(得分:1)
所以这里最简单和最方便的解决方案是使用虚拟析构函数,但是让我展示另一个解决方案:帮助编译器。类型推断在这里很有用 - 如果你声明对象是作为特定类型,您将获得预期的行为:
int main()
{
B *a2;
a2 = new B();
delete a2;
}
输出
constructor of A
constructor of B
destructor of B
destructor of A
此外,main()
应该返回int
,而不是void
。