随笔 - 96  文章 - 255  trackbacks - 0
<2008年4月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

E-mail:zbln426@163.com QQ:85132383 长期寻找对战略游戏感兴趣的合作伙伴。

常用链接

留言簿(21)

随笔分类

随笔档案

SDL相关网站

我的个人网页

我的小游戏

资源下载

搜索

  •  

积分与排名

  • 积分 - 485247
  • 排名 - 37

最新评论

阅读排行榜

评论排行榜

        几乎在大部分时候,我们是不需要显式的调用析构函数的。显式的调用析构函数是一件非常危险的事情,因为如果系统会调用析构函数,无论我们自己是否已经调用过,仍然会再次调用。换句话说,我们自己所谓的显式调用析构函数,实际上只是调用了一个成员函数,并没有真正意义上的让对象“析构”。
        为了理解这个问题,我们必须首先弄明白“堆”和“栈”的概念。
堆区(heap) —— 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
栈区(stack)—— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
        我们构造对象,往往都是在一段语句体中,比如函数,判断,循环,还有就直接被一对“{}”包含的语句体。这个对象在语句体中被创建,在语句体结束的时候被销毁。问题就在于,这样的对象在生命周期中是存在于栈上的。也就是说,如何管理,是系统完成而程序员不能控制的。所以,即使我们调用了析构,在对象生命周期结束后,系统仍然会再调用一次析构函数,将其在栈上销毁,实现真正的析构。
        所以,如果我们在析构函数中有清除堆数据的语句,调用两次意味着第二次会试图清理已经被清理过了的,根本不再存在的数据!这是件会导致运行时错误的问题,并且在编译的时候不会告诉你!
在网上找到这几句话,说得很好啊:
//显式调用的时候,析构函数相当于的一个普通的成员函数
//编译器隐式调用析构函数,如分配了对内存,显式调用析构的话引起重复释放堆内存的异常
//把一个对象看作占用了部分栈内存,占用了部分堆内存(如果申请了的话),这样便于理解这个问题
//系统隐式调用析构函数的时候,会加入释放栈内存的动作(而堆内存则由用户手工的释放)
//用户显式调用析构函数的时候,只是单纯执行析构函数内的语句,不会释放栈内存,摧毁对象

        系统在什么情况下不会自动调用析构函数呢?显然,如果对象被建立在堆上,系统就不会自动调用。一个常见的例子是new...delete组合。但是好在调用delete的时候,析构函数还是被自动调用了。很罕见的例外在于使用布局new的时候,在delete设置的缓存之前,需要显式调用的析构函数,这实在是很少见的情况。
        所以,结论是,一般不要自作聪明的去调用析构函数。或者要是你不嫌麻烦的话,析构之前最好先看看堆上的数据是不是已经被释放过了。放出一个演示的例子,大家可以参考一下哈。

#ifndef A_HPP
#define A_HPP

#include 
<iostream>
using namespace std;

class A
{
private:
    
int a;
    
int* temp;
    
bool heap_deleted;
public:
    A(
int _a);
    A(
const A& _a);
    
~A();
    
void change(int x);
    
void show() const;
};

#endif


 

#include "a.hpp"

A::A(
int _a): heap_deleted(false)
{
    temp 
= new int;
    
*temp = _a;
    a 
= *temp;
    cout
<< "A Constructor!" << endl; 
}

A::A(
const A& _a): heap_deleted(false)
{
    temp 
= new int;
    
*temp = _a.a;
    a 
= *temp;
    cout 
<< "A Copy Constructor" << endl;
}

A::
~A()
{
    
if ( heap_deleted == false){
        cout 
<< "temp at: " << temp << endl;
        delete temp;
        heap_deleted 
= true;
        cout 
<< "Heap Deleted!\n";
    }
    
else {
        cout 
<< "Heap  already Deleted!\n";
    }

    cout 
<< "A Destroyed!" << endl; 
}

void A::change(int x)
{
    a 
= x;
}

void A::show() const
{
    cout 
<< "a = " << a << endl;
}

#include "a.hpp"

int main(int argc, char* argv[])
{

    A a(
1);
    a.
~A();
    a.show();
    cout 
<< "main() end\n";
    a.change(
2);
    a.show();

    
return 0;
}
posted on 2008-04-12 14:29 lf426 阅读(2454) 评论(1)  编辑 收藏 引用 所属分类: 语言基础、数据结构与算法

FeedBack:
# re: 显式析构函数的陷阱 2008-10-24 20:54 林楚涛
当用placement new 创建对象的时候就必须自己显式调用析构函数  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理