memory leak问题?
各位朋友,我有一个问题,想请教大家,下面是一个示范程序,没有任何实际意义。
class foo
{
public:
foo():a(0) { pi = new int(3);}
private:
int a;
int *pi;
};
void test()
{
foo *pf = new foo();
//do something with pf
delete pf;
}
大家知道,“foo *pf = new foo(4);”这条语句的执行分为两个方面:
1.调用operator new()分配sizeof(foo)大小的raw memory;
2.调用foo类的构造函数对1中分配的内存进行初始化。
那么,如果在第2步中出现异常,即调用constructor时出现异常,从而退出test()函数,便会导致内存泄漏问题。有人可能会想到,利用try...catch进行异常捕获:
void test()
{
try{
foo *pf = new foo();
//do omething with pf
}
catch(...){
delete pf;
throw;
}
delete pf;
}
可问题是,如果调用constructor时出现异常,即对象没有被构造完全,pf是不会被正确赋值的(即pf不会被赋为1中分配的内存的起始地址),所以上述方法也不适用。
那我想请教各位高手,像这种内存泄漏问题,应该怎么解决呢?( 用所谓的"placement delete"方法?---好像此方法不是普遍使用的呀:( )
问题点数:50、回复次数:20Top
1 楼ericqxg007(还有很多东西要学(卡卡一米阳光))回复于 2006-02-03 18:10:09 得分 0
那你就在constructor里面写一个异常处理
另外提示一下:
在构造函数里面写了new 别忘了在析构函数里面写个deleteTop
2 楼hu_vane(边学边发现自己的无知)回复于 2006-02-03 18:49:13 得分 0
constructor中抛个异常就行了Top
3 楼cpp_wq(天天C++)回复于 2006-02-03 20:47:40 得分 0
to 楼上二位:
你们好像没看懂我的问题。
“在constructor里面写上一个异常处理”只能够释放“本对象的指针成员”所指向的内存区域,却无法释放“当前对象”所占用的内存(在上面的程序中,当前对象即位pf指针所指向的foo对象,它也分配在heap上,而不是在stack上)。
Top
4 楼ericqxg007(还有很多东西要学(卡卡一米阳光))回复于 2006-02-03 21:05:08 得分 0
try{
foo *pf = new foo();
//do omething with pf
}
catch(...){
delete pf;
throw;
}
你本身代码都有问题,既然没有new成功你delete干什么?
一般我不用异常 直接判断指针是不是空就可以了
new 异常在编译器会有默认的异常处理Top
5 楼cpp_wq(天天C++)回复于 2006-02-04 13:12:01 得分 0
to ericqxg007(一笑而过):
的确,你说的没错,是“没有new成功”,但是那是new expression的第二步(调用构造函数时)出现的问题,而第一步(调用operator new分配raw memory)已经成功完成,所以我的意思是,如何将这个已经分配的raw memory还给系统。难道你所说的“new 异常在编译器会有默认的异常处理”便能释放所分配的raw memory?Top
6 楼ox_thedarkness()回复于 2006-02-04 13:34:45 得分 0
to 一笑而过
判断指针为空是不符合现在C++标准的。参考 Effect C++ 条款8
在1993年以前,C++要求new失败时返回0;但是现在C++中 new 只会抛出 std::bad_alloc异常。
所以在现在的编译器上,if( pf ){...}测试要么成立,要么处在流程之外(异常抛出)
如果要使用旧式返回0的异常,则应该使用nothrow(头文件<new>中)形式的new
A* pa = new( nothrow ) A;
if( pa ) // Ok
Top
7 楼ox_thedarkness()回复于 2006-02-04 14:08:24 得分 0
对于楼主的问题,答案是必须用很麻烦的办法。
一般来说,C++有一个规则就是不能在constructor中抛出异常(虽然这是允许的,但是会诱发mem leak,如楼主所言)
一个常见的解决方案是提供 init 函数,把所有要求资源申请都集中到init 函数中。这样 constructor就不会抛出异常了。类似:
foo* pf = 0;
try{
pf = new foo(); // 只有foo的 new会抛出std::bad_alloc 异常。
pf->init(); // 其他异常在这里抛出
}catch( ... ){
if( !pf ){ // foo的 new失败。
} else { // pf->init()异常
}
}Top
8 楼vollin(林尚义)回复于 2006-02-04 14:59:37 得分 0
ox_thedarkness() ( ) 信誉:100
同意,在构造中使用异常是件非常麻烦的事。Top
9 楼zmjsx(风向)回复于 2006-02-04 15:47:58 得分 0
我有个办法你看看可行不?你用汇编写个内存占用控制,某命令调用申请了内存地址后,如果一段时间内未写入,就将此次调用视作误操作。Top
10 楼rigel2001(大宝)回复于 2006-02-04 16:26:57 得分 0
所以我的意思是,如何将这个已经分配的raw memory还给系统。难道你所说的“new 异常在编译器会有默认的异常处理”便能释放所分配的raw memory?
--------------------------------------------------
这点lz确实不用操心.
1.调用operator new()分配sizeof(foo)大小的raw memory;
--------------------------------------------------
如果出错的话,没有内存被分配,所以不用处理.
2.调用foo类的构造函数对1中分配的内存进行初始化。
--------------------------------------------------
如果出错的话,运行时系统会调用delete的.
直接判断指针是不是空就可以了
--------------------------------------------------
这种方法比较土,而且对于新标准来说是一种倒退.
一个常见的解决方案是提供 init 函数,把所有要求资源申请都集中到init 函数中。这样 constructor就不会抛出异常了。
--------------------------------------------------
事实上,catch到bad_alloc,是无法区分具体在第一步还是第二步发生了错误,这个解决方案就是强制bad_alloc只有在第一步才能抛出,把constructor里面的语句(就使new的第二步)都搬到init函数里面去,保留一个空的constructor,这样第二步就是空的就不会出错了
总之,楼主多虑了,reclaim内存的事情,您就别殚精竭虑了.
当然要管理内存分配情况,包括重载new,delete等等要考虑的东西还是比较多的.Top
11 楼cpp_wq(天天C++)回复于 2006-02-04 16:38:36 得分 0
to zmjsx(风向):
你的方法很有诱惑力,但是能不能给出源码,让大家参考参考。Top
12 楼cpp_wq(天天C++)回复于 2006-02-04 16:43:52 得分 0
to rigel2001(大宝):
“
2.调用foo类的构造函数对1中分配的内存进行初始化。
--------------------------------------------------
如果出错的话,运行时系统会调用delete的.
”
真是这样么?如果真是如此,那我真是多想了!但希望人兄给出此观点的可靠论据(别误会,我不是不相信你,只是想了解的多一些)Top
13 楼ox_thedarkness()回复于 2006-02-04 17:26:17 得分 0
阿哈,果然如 rigel2001(大宝) 所言呢,下面代码在VC7 / DevC++4.9 下执行
都显示系统自动delete了之前分配给foo的内存呢
不晓得C++标准是如何规定的?
//////////////////////////////////////////////////////
#include <iostream>
#include <new>
#include <cstdlib>
using namespace std;
void* operator new( size_t n ){ // 正常的重载new
void* p = malloc( n );
printf( "new (%d) #%p\n", n, p );
return p;
}
void* operator new( size_t n, bool badAlloc ){ // new(true)调用这个版本的new,抛出bad_alloc
printf( "new (%d) fail\n", n );
throw bad_alloc();
}
void operator delete( void* p ){
printf( "delete #%p\n", p );
free( p );
}
class foo{
public:
foo():a(0) { pi = new(false) int(3); }
private:
int a;
int *pi;
};
int main(){
foo *pf = 0;
try{
pf = new foo();
}catch(...){}
delete pf;
cin.get();
}Top
14 楼rigel2001(大宝)回复于 2006-02-05 15:24:38 得分 0
实际上,
C* p = new C;
这句话调用的是类似下面__new(基于new之上)的东西
void __new() throw (bad_alloc)
{
C * p = reinterpret_cast<C*> (new char[size]);
try
{
new (p) C;
}
catch(...)
{
delete[] p;
throw;
}
}
Top
15 楼rigel2001(大宝)回复于 2006-02-05 15:28:52 得分 0
真是这样么?
-----------
真是这样Top
16 楼lovedna(有间道)回复于 2006-02-05 18:18:48 得分 0
foo():a(0) { pi = new int(3);}
问题出在这条语句中,
int(3)
楼主说这条语句出错是吧! int 类是系统定义的,错误处理系统当然会处理,
如果推广到自定义类:
MyClass (3)
出错的话,出错处理也只能在MyClass类中.
Top
17 楼xlsue(小林)回复于 2006-02-05 18:38:59 得分 0
foo *pf = new foo();
//在foo构造函数中发生异常,这里并不会发生momery leak。因为new operator中已经有个异常处理。。。Top
18 楼xlsue(小林)回复于 2006-02-05 18:49:28 得分 0
如大宝所说的,大宝挺好的,我天天用:)Top
19 楼ox_thedarkness()回复于 2006-02-05 21:54:54 得分 0
哦~~ 原来如此~~
顶一个大宝~~Top
20 楼rigel2001(大宝)回复于 2006-02-05 22:32:49 得分 0
hehe~Top




