分类:
2008-11-11 10:33:34
今天看到网上大拿写的一个很有意思的写法,在一个类成员里有delete this
例如如下例子类
class VSCLS
{
public:
void stop()
{
delete this;
return;
};
~VSCLS()
{
printf("ENDING...\n");
};
};
如果我既调stop又delete,就会打印两遍ENDING...
这样写法安全吗?是用来防止内存泄漏吗?或者根本就是不对的
问题点数:50、回复次数:28
目的是调用STOP就会自动释放当前对象!
如果既调stop又delete,当然就会打印两遍ENDING...
不过他的目的是只调STOP。
这样写很不安全,但是严格遵循额外的规则,可以保持正确:
所有实例必须用new生成,不能用delete删除,只能用stop来删除,stop之后必须把指针置为NULL。
如果stop后delete会导致内存访问错误。绝对不行。
楼上所言。
调用了stop以后,还能delete它?真是奇怪啊
有必要吗?
调用stop后在delete在不同的编译系统中可能会有不同的表现,非常危险.因为stop之后对象已经自杀,再调用delete去删除不存在的对象,显然是错误的.
一般来说,这样的对象是堆中的对象,stop栈内的对象就会出问题.要禁止对象被分配在栈中,类的构造函数将被设为private,同时添加一个public函数,如 Type *Create(){ new Type;}
关注!
Too simple, sometimes naive.
The
destructure should be private, so that the object must
be performed by new, and you must destroy it by
stop instead of delete.
英语这么烂!
至于你打印两遍..那是stop() 中的delete this引发了dtor
然后你又delete对象的话,意思是对一个指针调用两次delete.
这样的行为用标准C++来说,就是未定义。
未定义的后果就是一般时候或许都好好的。。但某个关键时候它爆发了。
这个程序在STOP中DELETE了自己,但是如果不是用NEW来产生的对象,
为什么还要用DELETE来删除呢(在很多经典的书中都说,NEW要与DELETE
配对使用).如果是用NEW来产生的对象,那么在外部DELETE和调用STOP()
有什么区别呢?这样做不是很好吧,而且很容易产生上面有人说的那种错误.
也许我还是个菜鸟,没有理解其中有什么奥妙.关注高手来解答.
More Effective C++上,
有一个条款,教你把dtor做成private(这样就只能用new来产生object,不能用作local
object)。要销毁这样的对象,需要有一个类似上面stop()的member function,其中就有delete this;这句。
实际上stop也应该是被隐藏的,暴露的应该只是对象的一系列行为规则,在对象失去作用后,有判断的机制自动调用stop,这样就形成对象的自动生命周期,完全透明的。
study!
两次删除其实是不一样的。以下程序可以证明:
#include
class VSCLS
{
public:
VSCLS(){i=9;}
void stop()
{
delete this;
return;
};
~VSCLS()
{
std::printf("ending%d",i);
};
private:
int i;
};
main(){
VSCLS* a=new VSCLS;
a->stop();
delete a;
}
看来这次我拿到分了
cow!好没气氛啊,怎么都不说话啦?
this和a好像都是指向的同一地址,该地址在heap中,保存的是new生成的对象!
如果是这样,那么,调用a->stop后,由delete删除了该块内存,其结果是造成a指向的区域未有定义(已经删除了,释放回堆中,随时可供其它new使用)!
这样,如果在接下来的代码中调用delete a,则是删除了不明区域,其结果同样未有定义!
在c++中删除一个null的指针是安全的,但c++同样不允许我们修改this的值!
所以,这样做,个人认为是很不安全的!
up
错啦
第一次仅仅删除了member data而已。而function还是存在地
我上面那段程序用BCC和GCC都编译通过的。
我说的不是标准,而是事实。虽然编译器没有按照标准去做,但是我们要承认事实
/*这是非常危险的,因为在你调用了一次方法stop以后,对象指针相当于已经没有初始化
下面的程序是能够编译通过,但是程序在调用第二次的stop时候,由于stop函数不是static,就是没有实例化的对象是没有权利调用非static成员函数的,
所以程序出错了*/
#include
#include
class VSCLS
{
public:
void stop()
{
delete this;
return;
};
~VSCLS()
{
printf("ENDING...\n");
};
};
void main()
{
VSCLS *p=new VSCLS();
int i=0;
for( i = 0; i<2;i++)
{
i++;
p->stop();
cout<<"it's ok when i = "<
}
delete p;
}
程序有个地方写错了
#include
#include
class VSCLS
{
public:
void stop()
{
delete this;
return;
};
~VSCLS()
{
printf("ENDING...\n");
};
};
void main()
{
VSCLS *p=new VSCLS();
int i=0;
for( i = 0; i<2;i++)
{
p->stop();
cout<<"it's ok when i = "<
}
delete p;
}
编译是可以通过的,不过运行时就有问题啦!
毕竟这里试图删除一块包含未知区域的内存区,而编译器当然不会负责check这件事情!
试着把delete a 和 a->stop换个位置,效果几乎是相同的!
本来两次就都是delete嘛
这种写法是有特殊用途的,肯定还有一些其它措施以保证它的正确使用。比如楼上说的用private的构造函数防止用户直接delete,用其它方法保证对象一定在堆上创建等等,只有这样才能保证这个类不被误用。
to julyclyde(从ASP转向CPP,到处都是P) :
第一次仅仅删除了member data而已。而function还是存在地
我上面那段程序用BCC和GCC都编译通过的。
我说的不是标准,而是事实。虽然编译器没有按照标准去做,但是我们要承认事实
===================
这不是什么标准不标准的事,删除两次绝对是错误的,但是,在编译阶段发现不了这个问题,编译器怎么知道在a.stop()后,a对象就已经释放了呢?所以编译肯定可以通过的。
删除了对象,不等于对象原来占用的空间就会清除,对象原来的空间上还是会保留原来的内容,直到再次分配出去,在其它地方被修改,所以释放了的对象在很多时候操作起来好像还是正确的。
这样的写法在某些地方有特殊用途,比如我需要实现一个基于引用计数的智能指针,那么我会要求对象在计数达到0的时候用delete this删除自身。这种用法的实例在许多地方都可以见到。比如VC中的_com_ptr_t以及CORBA中的var类型。
In order to understand "delete this" :
First Step------dive into "delete p"
delete p 执行了哪两个步骤?
delete p 是一个两步的过程:调用析构函数,然后释放内存。delete p产生的代码看上去是这样的(假设是Fred*类型的):
// 原始码:delete p;
if (p != NULL)
{
p->~Fred();
operator delete(p);
}
p->~Fred() 语句调用 p 指向的Fred 对象的析构函数。
operator delete(p) 语句调用内存释放原语 void operator delete(void* p)。
Second Step-------"delete this"
成员函数调用delete this合法吗?
只要你小心,一个对象请求自杀(delete this),是可以的。
以下是我对“小心”的定义:
你必须100%的确定,this对象是用 new分配的(不是用new[],也不是用定位放置 new,也不是一个栈上的局部对象,也不是全局的,也不是另一个对象的成员,而是明白的普通的new)。
你必须100%的确定,该成员函数是this对象最后调用的的成员函数。
你必须100%的确定,剩下的成员函数(delete this之后的)不接触到 this对象任何一块(包括调用任何其他成员函数或访问任何数据成员)。
你必须 100%的确定,在delete this之后不再去访问this指针。换句话说,你不能去检查它,将它和其他指针比较,和 NULL比较,打印它,转换它,对它做任何事。
自然,对于这种情况还要习惯性地告诫:当你的指针是一个指向基类类型的指针,而没有虚析构函数时(也不可以 delete this)。
注意:因为是在类成员函数里面delete this的,所以在此语句以后,不能访问任何的成员变量及虚函数,否则一定非法。