不迎不送,来去自便,无茶无酒,谈笑随缘

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

         在编写一个PocketPC应用程序的时候遇到一个奇怪的错误,当运行到某一函数中两个CString对象相赋值的时候,编译器就会抛出地址保护性异常,仔细地检查了一下这个函数,其中并没有直接使用指针对内存进行操作,也没有两个自定义对象之间采用等号的方式进行赋值的运算,对这个函数进行了一下完整的单步执行追踪,并没有发现该函数中有任何内存上的漏洞,因此可以断定内存漏洞并不是发生在该函数中的。于是花了2个小时的时间对程序进行了走查,在关键的地方添加断点单步运行,查看内存中的变化,发现某一个对象的析构函数被多次调用,而对象所对应的类的析构函数中有使用delete释放指针的情况,于是恍然大悟,原来自认为对什么时候该使用拷贝构造函数了如指掌的我,又一次被拷贝构造函数戏弄了。那么究竟什么情况下才需要使用拷贝构造函数,下面做一个简述。

 

最常见的使用拷贝构造函数的情况应该是为对象提供一个利用现有对象来Clone出一个一模一样的新对象的功能,这是拷贝构造函数最基本的用法,这里就不再说了。

另一种使用情况是当对象需要使用等号进行赋值的时候使用的。定义一个类,类中指定义了一个指向int类型的指针,该指针在构造函数中分配内存,再析构函数中释放内存 ,类定义如下

class A

{

public:

     int *pN;

 

     A()

     {

         pN=new int[2];

         pN[0]=1;

         pN[1]=2;

     }

     ~A()

     {

         delete [] pN;

     }

};

 

当我们采用赋值=对两个对象进行赋值的时候,由于没有重载=号,系统会默认重载一个等号运算符,该运算符将一个对象的引用赋值给另一个对象。这就出现了一个问题,如果其中一个对象被卸载,那么另一个对象在被卸载时,pN会在析构函数中再次被delete ,由于pN已经在第一个对象被delete时被释放了,这次释放就会抛出一个异常,导致程序崩溃。演示代码如下:

 

void fun()

{

                  A a,b;

          a=b

}

函数fun在运行结束后会发生二次析构的错误。

解决二次析构问题有两种方式,其一是重载赋值运算符,在重载的赋值运算符中为指针pN做一次深层拷贝,保证当前对象和原对象中的pN指向的是不同的内存地址。代码如下:

A &operator = (A &a)

     {

         memcpy(this->pN,a.pN,2);

         return *this;

}

这里之所以没有new一个新pN是因为这个操作在构造函数中作了。

第二种方式就是建立一个拷贝构造函数,并添加代码:

     A(A &a)

     {

         pN=new int[2];

         memcpy(this->pN,a.pN,2);

}

调用方式改为

A a;

     A b(a);

 

最后还有一种必须要添加拷贝构造函数的情况,就是对象要做为参数传递给一个函数的时候,比如

void fun(A a)

{

         }

         这种错误是由于系统会为自定义类类型添加一个默认的拷贝构造函数,而这个拷贝构造函数所作的工作与默认的赋值运算符相同,都是仅仅对对象的引用进行赋值,因此也会引起二次析构的问题。

 

         总而言之,当类中有指针,并且在析构函数中对指针进行了delete操作的情况下,必须要重载拷贝构造函数,否则有可能引起重复释放的问题。

QQ:819543772

EMail:wuchunlei@163.com

 

 

posted on 2009-01-21 19:43  wude  阅读(154)  评论(0编辑  收藏  举报