我正在尝试使用虚拟析构函数进行审核 - 想知道是否有人对以下内容有一个简单的解释(使用vs 2010):
我定义类层次结构A-B-C-D,D继承C,C继承B,B继承A,A是Base;
进行了2次实验:
首次实验 -
A有一个虚拟的析构函数。
B有一个非虚拟析构函数
C有一个虚拟的析构函数
D有一个非虚拟的析构函数
// ----------------------------
在D类堆上分配4个对象 - 在第3个点指向A *,B *和C *的指针 - 将第4个作为完整性的D *。 删除所有4个指针。
正如我所料,在所有4个实例中,完整的析构函数链以从D到A的相反顺序执行,释放所有内存。
第二次实验 -
A具有非虚拟析构函数**将A更改为非虚拟
B有一个非虚拟析构函数
C有一个虚拟的析构函数
D有一个非虚拟的Distructor
在D类堆上分配4个对象 - 在前3个指向A *,B *和C *的指针 - 将第4个作为完整性的D *。
删除C *和D *指针: 完整的析构函数链以从D到A的相反顺序执行,释放所有内存。
删除B *: B然后运行析构函数(泄漏)
删除A *: 只运行一个析构函数(泄漏)
任何人都可以解释为什么会这样?
当在实验2中分配D类型opject时,它的直接基类(C)有一个虚拟析构函数 - 不能告诉编译器用Vptr跟踪它并知道内存类型?无论参考?
由于 迈克
答案 0 :(得分:6)
当在实验2中分配D类型opject时,它的直接基类(C)有一个虚拟析构函数 - 不能告诉编译器用Vptr跟踪它并知道内存类型?无论参考?
没有
在您的第二个测试用例中,A
和B
没有vptrs / vtables。 (即使他们这样做了,非虚拟成员函数仍然会静态解决,而不是动态解决。)
换句话说,基类不会从派生类“继承”信息(例如函数是否为虚函数)。
答案 1 :(得分:2)
当您删除没有虚析构函数的A *时,编译时编译器不知道它会在运行时指向具有虚析构函数的对象。删除可能是具有虚拟析构函数的对象 - 或者不是。动态绑定不会发生。
答案 2 :(得分:1)
您实际的问题是关于为什么会使用虚拟和非虚拟析构函数?具有非虚拟析构函数的基类的Cos是不好的。见the faq
答案 3 :(得分:1)
我写了几乎相同的问题,所以我想分享一下。
请注意,我还在不同的Ctor中添加了一些虚函数用法,以说明它是如何工作的(很快,在每个Ctor中,V表只更新“直到它”,这意味着虚拟函数的实现将被调用的是在继承链的“这一点”之前得到的最多。)
我的一个注释:在运行给定类的示例代码中,我还在STACK上添加了“Derived”对象(B和D)的创建 - >为了强调当我们对类实例使用指针(无论什么类型)时,所有关于Dtor的“虚拟”的考虑都是适用的。
class A;
void callBack(A const& a);
class A
{
public:
A() { std::cout << "A Ctor " << std::endl; f1(); callBack(*this); /* f3();*/ }
~A() { std::cout << "A Dtor " << std::endl; }
void f1() { std::cout << "A : f1 " << std::endl; }
virtual void f2() const { std::cout << "A : f2 " << std::endl; }
virtual void f3() = 0;
};
class B : public A
{
public:
B() { std::cout << "B Ctor " << std::endl; f1(); callBack(*this); f3(); }
~B() { std::cout << "B Dtor " << std::endl; }
void f1 () { std::cout << "B : f1 " << std::endl;}
void f2() const { std::cout << "B : f2 " << std::endl; }
virtual void f3() { std::cout << "B : f3 " << std::endl; }
};
class C : public A
{
public:
C() { std::cout << "C Ctor " << std::endl; f1(); callBack(*this); f3(); }
virtual ~C() { std::cout << "C Dtor " << std::endl; }
void f1() { std::cout << "C : f1" << std::endl;}
void f2() const { std::cout << "C : f2" << std::endl; }
virtual void f3() const { std::cout << "C : f3" << std::endl; }
};
class D : public C
{
public:
D() { std::cout << "D Ctor " << std::endl; f1(); callBack(*this); }
~D() { std::cout << "D Dtor " << std::endl; }
void f1() { std::cout << "D : f1" << std::endl; }
void f2() const { std::cout << "D : f2 " << std::endl; }
virtual void f3() { std::cout << "D : f3 " << std::endl; }
};
void callBack(A const& a) { a.f2(); }
// =================================================================================================================================
int main()
{
std::cout << "Start of main program" << std::endl;
std::cout << "Creating a D object on the heap" << std::endl;
D* pd = new D;
C* pc = new D;
A* pa = new D;
if (true)
{
std::cout << "Entering Dummy scope # 1 and creating B object on the stack" << std::endl;
B b;
std::cout << "Leaving Dummy scope # 1 with B object within it" << std::endl;
}
if (true)
{
std::cout << "Entering Dummy scope # 2 and creating D object on the stack" << std::endl;
D d;
std::cout << "Leaving Dummy scope # 2 with D object within it" << std::endl;
}
std::cout << "Calling delete on pd (D*) which points on a D object" << std::endl;
delete pd;
std::cout << "Calling delete on pc (C*) which points on a D object" << std::endl;
delete pc;
std::cout << "Calling delete on pa (A*) which points on a D object" << std::endl;
delete pa;
std::cout << "End of main program" << std::endl;
return 0;
}