明明白白c++ 基类的析构函数

前言

在effective c++中有这么一句话:确定基类有虚析构函数。

记得早期学c++的时候,很多书上会把基类的析构函数声明为虚函数,并且这样介绍虚函数,但是一直没有深入理解基类为什么要用虚函数作为析构函数。


构造函数和析构函数

定义就不讲了,很容易理解。很多时候在构造函数里面申请空间,在析构函数里面释放空间。

书上讲的例子都很简单。


我们这里写一个例子,来讲一下。

#include <iostream>
using namespace std;
class A
{
public:
 A();
 ~A();
};
A::A()
{
 cout<<"A construct"<<endl;
}
A::~A()
{
cout<<"A destruct"<<endl;
}
class B:public A
{
public:
 B();
 ~B();
};
B::B()
{
 cout<<"B construct"<<endl;
}

B::~B()
{
 cout<<"B destcuct"<<endl;
}

class C
{
public:
   C();
   virtual ~C();
   virtual void print();
};
C::C()
{
 cout<<"C construct"<<endl;
}
C::~C()
{
  cout << "C destruct"<<endl;
}
void C::print()
{
   cout <<" C print"<<endl;
}

class D:public C
{
public:
   D();
   virtual ~D();
   virtual void print();
};

D::D()
{
 cout<<"D construct"<<endl;
}
D::~D()
{
  cout << "D destruct"<<endl;
}

void D::print()
{
  cout<<"D print"<<endl;
}



int main()
{
   cout<<"----------a---------"<<endl;
   A a;
   cout<<"----------b---------"<<endl;
   B b;
   cout<<"----------p---------"<<endl;
   A *p = new B;
   delete p;
   cout<<"----------q---------"<<endl;
   C *q = new D;
   q->print();
   delete q;
   cout<<"---------end--------"<<endl;
}


可以看到输出的结果如下:


----------a---------
A construct
----------b---------
A construct
B construct
----------p---------
A construct
B construct
A destruct
----------q---------
C construct
D construct
D print
D destruct
C destruct
---------end--------
B destcuct
A destruct
A destruct


我们分析一下:

A a;这个很好理解,调用A的构造函数。

所以输出的是

A construct

B b 呢,就是先调用A的,在调用B的构造函数

得到的是

A construct

B construct

然后在函数执行完,a,b的生命期就结束了。

系统会自己调用它们的析构函数,因为是栈的关系,先进后出。

所以先调用B的,析构也是和构造相反。

B destruct

A destruct

下来调用A 的

A destruct


虚析构函数

那么虚析构函数有什么用呢?
这种情况下有用,就是基类的指针指向派生类的对象的时候。
上文中我们用
 A *p = new B;
这个时候会生成一个B的对象,ps:写成new B 和new B();都一样。
然后我们用
delete p;
我们的本意肯定是要删除B的对象,先调用B的析构,在调用A的析构,但是结果呢?
我们这里打印的是只调用了A的析构。
实际上,c++语言标准关于这个问题的阐述非常清楚:当通过基类的指针去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的。
所以其实这种行为很危险,导致的结果是未知的。
就像上面一样,得不到我们想要的结果。

但是如果用了虚析构函数就不存在这样的问题了。
你可以看到D *q = new C;
然后delete q;
就可以得到我们想要的结果了。

然后D d;呢 和上面的一样,不受任何影响。

那么为什么不把c++的析构函数默认为虚函数呢?
第一,历史原因,兼容c语言,
第二,虚函数本身要占空间,4个字节。
第三,如果该类不存在子类,也没有必要声明生虚函数。
所以很多人说java比c++简单,因为它没有那么多过去。

另外,我上面例子有个print,来说明普通虚函数和虚析构函数的区别。
普通虚函数只会调用自己对应的那个函数, 
而虚析构函数,两重特性,虚函数的特性找到自己对应的;析构函数的特性调用父类的析构函数。

最后,希望大家能自己写写代码,这样才能真正理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值