析构函数的浅谈《原创》
2011-06-03 10:34 Rollen Holt 阅读(590) 评论(3) 编辑 收藏 举报显式的调用析构函数是一件非常危险的事情,,我们自己所谓的显式调用析构函数,实际上只是调用了一个成员函数,并没有真正意义上的让对象“析构”。
为了理解这个问题,我们必须首先弄明白“堆区”和“栈区”的概念。《具体的区别参加我的文章-《堆区和栈区浅谈》 》
堆区(heap)
举个例子说明一下:
Fred *p=new Fred();
delete p;//自动调用p->~Fred
但是如果我们将上一条语句改为:p->~Fred();呢。会出现什么情况呢?
因为显示调用析构函数不会释放Fred对象本身的内存,也就是栈内存,所以不要这么做,记住delete做了2件事情:调用析构函数和回收内存。
编译器隐式调用析构函数,如分配了堆内存,显式调用析构的话引起重复释放堆内存的异常
需要显式调用的析构函数,这实在是很少见的情况。
Line #1本质上只是调用了构造函数
建议:万不得已时才使用“placement
但是这个是很危险的:你要独自承担这样的责任,传递给“placement
你还有析构放置的对象的责任。这通过显式调用析构函数来完成:
void
这是显式调用析构函数的唯一时机。
下面给出一些我的建议哈。大家参考一下,欢迎拍砖
但是也会有一些时候,我们想要将一个局部对象在被创建的代码块 } 之前被析构的话,举个例子吧。比如假设析构
Void function
{
}
这个时候我们可以人为的添加一个{…}.将局部对象的生命周期长度包裹于这个人为的{…}中就可以了。这样在我们人为的 } 的后面析构函数将会被自动调用。绝大多数的时候我们是可以人为的将局部对象包裹于这个人为的{…}中的,来限制其生命周期。但是也有一些特殊的情况使得我们不能人为的用{…}去包裹一些代码,这个时候我们可以通过增加一个类似于析构函数的成员函数来实现类似的功能。但是我们千万不要调用析构函数本身。
注意其他的
并且我们在编写析构函数的时候,并不需要显示的调用成员对象的析构函数(排除place new情况)。因为类的析构函数(不论你是否显式地定义了)自动调用成员对象的析构函数。它们以出现在类声明中的顺序的反序被析构。
class
我们在书写派生类的析构函数的时候,也不需要显示的调用基类的析构函数。派生类的析构函数(不论你是否显式地定义了)自动调用基类子对象的析构函数。基类在成员对象之后被析构。在多重继承的情况下,直接基类以出现在继承列表中的顺序的反序被析构。
class
注意:虚拟继承的顺序相关性是多变的。如果你在一个虚拟继承层次中依赖于其顺序相关性,那你应该去别的地方查一下资料
比如我们有下面的语句:
void Creat
{
//other code…
}
此处我们创建了一个对象数组,则他最后的析构顺序为A[9],A[8]…A[0]
所以,结论是,一般不要自作聪明的去调用析构函数。或者要是你不嫌麻烦的话,析构之前最好先看看堆上的数据是不是已经被释放过了。放出一个演示的例子,大家可以参考一下哈。
class aaa
{
public:
private:
};
void main()
{
aaa a;
a.~aaa();
a.disp();
}
这样的话,显式两次deconstructor,第一次析构相当于调用一个普通的成员函数,执行函数内语句,显示
第二次析构是编译器隐式的调用,增加了释放栈内存的动作,这个类未申请堆内存,所以对象干净地摧毁了,
显式+对象摧毁;
class aaa
{
public:
private:
};
void main()
{
aaa a;
a.~aaa();
a.disp();
}
这样的话,第一次显式调用析构函数,相当于调用一个普通成员函数,执行函数语句,释放了堆内存,但是并未释放栈内存,对象还存在(但已残缺,存在不安全因素);
第二次调用析构函数,再次释放堆内存(此时报异常),然后释放栈内存,对象销毁
==============================================================================
本博客已经废弃,不在维护。新博客地址:http://wenchao.ren
我喜欢程序员,他们单纯、固执、容易体会到成就感;面对压力,能够挑灯夜战不眠不休;面对困难,能够迎难而上挑战自我。他
们也会感到困惑与傍徨,但每个程序员的心中都有一个比尔盖茨或是乔布斯的梦想“用智慧开创属于自己的事业”。我想说的是,其
实我是一个程序员
==============================================================================