c++ virtual 虚析构函数 资源释放的讨论

关于c++ virtual,析构函数的讨论已经挺多了,参见

http://zxjgoodboy.blog.sohu.com/61482463.html

http://blog.csdn.net/zoukh/article/details/16624

http://blog.csdn.net/han_348154920/article/details/5944351

http://hi.baidu.com/bystander1983/blog/item/d61627553e3afe52d10906d2.html

http://www.cnblogs.com/powersun/archive/2007/08/13/853015.html

http://www.cnblogs.com/xd502djj/archive/2010/09/22/1832912.html

但是这些文章大多在讨论:通过虚析构函数,可以利用多态顺利释放资源,防止内存泄露

但是我在使用时却恰恰发生了相反的事情,不是内存泄漏,而是内存重复删除。这也是一个需要深思的问题。

我们还是以传统的animal和dog为例。

首先是基类 animal

#pragma once
class animal
{
public:
animal(
void);
virtual ~animal(void);

int * type;

virtual void allocate(int n);
virtual void release();

};

实现

#include "animal.h"
#include
"stdio.h"
animal::animal(
void):type(0)
{
}
animal::
~animal(void)
{
release();
}

void animal::allocate( int n )
{
release();
puts(
"allocate from Animal start!");
type
=new int[n];
puts(
"allocate from Animal end!");
}

void animal::release()
{
puts(
"release from Animal start!");
if(type)
{
delete [] type;
}
puts(
"release from Animal end!");
}
.

.

派生类dog

#pragma once
#include
"animal.h"
class dog :
public animal
{
public:
dog(
void);
virtual ~dog(void);

int * country;

virtual void allocate(int n);
virtual void release();
};

.

实现

#include "dog.h"
#include
"stdio.h"

dog::dog(
void):country(0)
{}
dog::
~dog(void)
{}
void dog::allocate(int n)
{
puts(
"allocate from Dog start!");
animal::allocate(n);
country
=new int[n];
puts(
"allocate from Dog end!");
}
void dog::release()
{
puts(
"release from Dog start!");
animal::release();
if(country)
{
delete []country;
}
puts(
"release from Dog end!");
}

.

.

main里考察分别使用基类和派生类指针的情形

#include "dog.h"
#include
"stdio.h"
int main()
{
animal
* a=new dog();
a
->allocate(12);
delete a;
puts(
"\na finished!\n");

dog
*b=new dog();
b
->allocate(12);
delete b;
puts(
"\nb finished!\n");
}
.

运行结果是

allocate from Dog start!
release from Dog start
!
release from Animal start
!
release from Animal end
!
release from Dog end
!
allocate from Animal start
!
allocate from Animal end
!
allocate from Dog end
!
release from Animal start
!
release from Animal end
!

a finished
!

allocate from Dog start
!
release from Dog start
!
release from Animal start
!
release from Animal end
!
release from Dog end
!
allocate from Animal start
!
allocate from Animal end
!
allocate from Dog end
!
release from Animal start
!
release from Animal end
!

b finished
!

.

两种情形是一致的,但是要注意对于单一情形,在allocate和析构函数中,都是在基类里调用了release(),但表现的结果确完全不同:

allocate成功使用了多态,析构函数却只是调用了animal::release()

产生这种问题的根源在于:派生类的普通成员函数会对基类的同签名函数产生隐藏,调用派生类的函数不会触发基类的函数;但是析构函数就恰恰相反,调用派生类的析构函数后会紧接着调用基类的析构函数

也就是说,我在dog::allocate()中显式的调用animal::allocate(),也就顺次显式调用release(),实际上使用派生类指针调用基类函数,恰好满足多态条件,因此运行正常。

但是在dog::~dog()中,我没用显示调用基类析构函数,而是系统隐式调用的;但是系统隐式调用时,派生类已经析构了,它是按照animal::~animal()来调用的,那么release()也就只是有基类指针使用,因此没有使用多态,animal发生了内存泄露。

那么,我如果在dog::~dog()中显式的调用release()行不行呢?

我们试试看

dog::~dog(void)
{
release();
}

现在的程序输出是

allocate from Dog start!
release from Dog start
!
release from Animal start
!
release from Animal end
!
release from Dog end
!
allocate from Animal start
!
allocate from Animal end
!
allocate from Dog end
!
release from Dog start
!
release from Animal start
!
release from Animal end
!
release from Dog end
!
release from Animal start
!

没错,就是这些!程序就在这卡着。(我的开发环境是 vs2010+win7)

尝试调试,说程序陷入死锁状态。仔细观察最后5句,发现 dog::~dog()里的release()顺利执行,在调用基类的析构函数时卡住了。类似的情况我在另一个大点的项目中则是直接崩溃了。

分析一下这里原因也好理解,animal::release()函数被调用了两次,第二次调用时产生死锁或者崩溃。

那么是我程序哪里实现的有问题么?

allocate与release都应该是virtual的吧?

dog::release()中必须要调用 animal::release()才合理吧?

那么现在陷入了两难困境:dog::~dog()中到底应不应该调用release()呢?调用,程序崩溃;不调用,内存泄露。

.

.

很让人头疼的问题。

无奈,我只好曲线救国,把release拆分成了两部分来实现,总算可以实现目标,但这样确实太丑了。

animal.h

#pragma once
class animal
{
public:
	animal(void);
	virtual ~animal(void);

	int * type;

	virtual void allocate(int n);
	virtual void release();
	void releaseMyData();

};

animal.cpp

#include "animal.h"
#include
"stdio.h"
animal::animal(
void):type(0)
{
}
animal::
~animal(void)
{
release();
}

void animal::allocate( int n )
{
release();
puts(
"allocate from Animal start!");
type
=new int[n];
puts(
"allocate from Animal end!");
}

void animal::release()
{
releaseMyData();
}

void animal::releaseMyData()
{
puts(
"release from Animal start!");
if(type)
{
delete [] type;
}
puts(
"release from Animal end!");
}

dog.h

#pragma once
#include
"animal.h"
class dog :
public animal
{
public:
dog(
void);
virtual ~dog(void);

int * country;

virtual void allocate(int n);
virtual void release();
void releaseMyData();
};

dog.cpp

#include "dog.h"
#include
"stdio.h"

dog::dog(
void):country(0)
{}


dog::
~dog(void)
{
releaseMyData();
}

void dog::allocate(int n)
{
puts(
"allocate from Dog start!");
animal::allocate(n);
country
=new int[n];
puts(
"allocate from Dog end!");
}
void dog::release()
{
animal::release();
releaseMyData();
}

void dog::releaseMyData()
{
puts(
"release from Dog start!");
if(country)
{
delete []country;
}
puts(
"release from Dog end!");
}

你聪明的,告诉我,我哪里错了?有什么解决方案啊?

posted on 2011-07-05 00:42  大宝pku  阅读(2354)  评论(13编辑  收藏  举报

导航