求高人看看这段诡异的代码

zmzmyun 2010-09-13 05:46:05
最近在研究智能指针时,随手写了一段应该崩的代码,结果却让我崩了,先上代码给各位高手过目:

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)


class RefCountedBase {
protected:

~RefCountedBase() {
printf("~RefCountedBase\n");
}
RefCountedBase() : ref_count_(0) {
printf("RefCountedBase:%x\n", this);
}

void AddRef() {
++ref_count_;
}

bool Release() {
if (--ref_count_ == 0) {
return true;
}
return false;
}
private:
int ref_count_;
DISALLOW_COPY_AND_ASSIGN(RefCountedBase);
};


template <class T>
class RefCounted : public RefCountedBase {
public:
~RefCounted() {
printf("~RefCounted\n");
}
RefCounted() {
printf("RefCounted:%x\n", this);
}

void AddRef() {
RefCountedBase::AddRef();
}

void Release() {
if (RefCountedBase::Release()) {
delete static_cast<T*>(this);
}
}

private:
DISALLOW_COPY_AND_ASSIGN(RefCounted<T>);
};


template <class T>
class scoped_refptr {
public:
scoped_refptr() : ptr_(NULL) {
printf("scoped_refptr:%x\n", this);
}

scoped_refptr(T* p) : ptr_(p) {
if (ptr_)
ptr_->AddRef();
}

scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
if (ptr_)
ptr_->AddRef();
}

~scoped_refptr() {
if (ptr_)
ptr_->Release();
}

T* get() const { return ptr_; }
operator T*() const { return ptr_; }
T* operator->() const { return ptr_; }

scoped_refptr<T>& operator=(T* p) {
// AddRef first so that self assignment should work
if (p)
p->AddRef();
if (ptr_ )
ptr_ ->Release();
ptr_ = p;

return *this;
}

scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
return *this = r.ptr_;
}

void swap(T** pp) {
T* p = ptr_;
ptr_ = *pp;
*pp = p;
}

void swap(scoped_refptr<T>& r) {
swap(&r.ptr_);
}

private:
T* ptr_;
};

class A : public RefCounted<A>
{
public:
A(){
printf("A:%x\n", this);
}
~A(){
printf("~A\n");
}
void Afun()
{
if (this == NULL)
{
printf("NULL:%x\n", this);
return;
}
printf("afun:%x\n", this);
}
private:
DISALLOW_COPY_AND_ASSIGN(A);

};

void f()
{
scoped_refptr<A> a = new A();
a->Afun();
a = NULL;
a->Afun();
}
int _tmain(int argc, _TCHAR* argv[])
{
f();
return 0;
}

注意看f()函数中第二次调用a->Afun();应该出错才对,可是结果却是调用成功,没有任何异常,请教各位高手这是为什么呢?
...全文
324 点赞 收藏 23
写回复
23 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
pengzhixi 2010-09-14
[Quote=引用 13 楼 zmzmyun 的回复:]
当然试过啦,就是因为一般来说都会崩溃,而这里没有崩溃我才发帖请教的[/Quote]编译下我11楼给的代码
回复
cswuyg 2010-09-14
⊙﹏⊙b汗。我12楼的回复被忽略了。
看你18楼的回复,觉得你对对象和类的关系搞不清,先学习基础再说吧。
如果,了解了编译器是怎么处理对非虚成员函数的调用的、了解了类跟对象的关系,就不会有那么多的疑问。
回复
cengshilieren 2010-09-14
没看懂,呵呵
回复
gules 2010-09-14
其一,将a=NULL并不会析构a;
其二,类的成员函数的定义是(函数体的代码)与类的某个对象的生命周期不是一回事,无论你产生多少个对象,其成员函数的二进制代码也只有一份拷贝。其实与静态成员函数代码是一样的,只不过静态成员函数内没有this指针。
回复
zmzmyun 2010-09-14
[Quote=引用 17 楼 gules 的回复:]

楼主:

实际上,当你写a=NULL; a->Afun();时,表面上的确是0->Afun(),但别忘了编译后的成员函数的样子:

void A_Afun(A* a); (函数名是乱编的,看编译器怎么进行名称重整而定)

所以0->Afun()实际上就是A_Afun(this); (this为NULL)
而你的代码有保护检查,如下:
void Afun()
{
……
[/Quote]
我觉得您分析的很有道理,但是还有一点我一直想不通,难道说一个类被析构了,它的成员函数还存在于内存中吗?我的成员函数又不是静态的,我的理解是它应该随着这个类一起销毁掉了才对呀,还请赐教
回复
gules 2010-09-14
楼主:

实际上,当你写a=NULL; a->Afun();时,表面上的确是0->Afun(),但别忘了编译后的成员函数的样子:

void A_Afun(A* a); (函数名是乱编的,看编译器怎么进行名称重整而定)

所以0->Afun()实际上就是A_Afun(this); (this为NULL)
而你的代码有保护检查,如下:
void Afun()
{
if (this == NULL)
{
printf("NULL:%x\n", this);
return;
}
printf("afun:%x\n", this);
}

所以不会崩溃。(即使没有,你也没有通过NULL来读写所指内存,因而没问题,反之必崩!)
回复
taodm 2010-09-14
访问空指针的后果为“未定义”,崩溃只是其中一种可能。你现在真学到的就是第二种可能。
其实不存在“一般XXXX”这种说法,你必须修正自己的错误认识。
回复
zmzmyun 2010-09-14
[Quote=引用 19 楼 gules 的回复:]

其一,将a=NULL并不会析构a;
其二,类的成员函数的定义是(函数体的代码)与类的某个对象的生命周期不是一回事,无论你产生多少个对象,其成员函数的二进制代码也只有一份拷贝。其实与静态成员函数代码是一样的,只不过静态成员函数内没有this指针。
[/Quote]
您可能没有仔细看代码,这个是智能指针,在赋空值时其对象同时也被析构了
您说的第二点我没有意见,现在基本搞清楚原因了,在这里谢谢您啦
回复
zmzmyun 2010-09-14
[Quote=引用 20 楼 wuxupeng999 的回复:]

⊙﹏⊙b汗。我12楼的回复被忽略了。
看你18楼的回复,觉得你对对象和类的关系搞不清,先学习基础再说吧。
如果,了解了编译器是怎么处理对非虚成员函数的调用的、了解了类跟对象的关系,就不会有那么多的疑问。
[/Quote]
不好意思,不是忽略您的回复,的确如您所说,我这边基础不太好,对象和类有点混乱了,今天我用同样的代码跑了一次我前面说的必崩的代码,居然没有崩,看来确实如16楼所说的那样,崩溃只是一种可能,最后感谢您的回复,小弟受教了
回复
yangyanglei 2010-09-14
delete 只是给内存至乐个标志 只要不访问里面的数据成员 就没得问题
回复
zmzmyun 2010-09-13
[Quote=引用 9 楼 hastings 的回复:]
引用 6 楼 zmzmyun 的回复:

引用 4 楼 hastings 的回复:
a = NULL;
//这里的时候,a已经析构delete掉了~~
a->Afun();//此时a->返回的是0;
然后0->Afun();
而Afun中没有读写内存操作,是可以运行通过而不崩溃的~~

不是这样的吧?我不用模板类,定义一个普通的类这样操作后,调用的函数中仍然是个输出语句,但是程序……
[/Quote]
当然试过啦,就是因为一般来说都会崩溃,而这里没有崩溃我才发帖请教的
回复
cswuyg 2010-09-13
没自己写过这么长的智能指针……,还好代码看得懂,说下我的理解。
4楼的解释应该差不多。
a->Afun();
a里边的ptr是NULL,所以:NULL->Afun();
别忘了,这里的Afun()不是虚函数,所以编译器在生成指令的时候是直接生成对函数Afun()的调用。它在生成指令的时候不知道ptr会等于NULL,因为是成员函数,所以还会传递一个this指针,这个this指针就是ptr的值,是NULL(也就是0)。所以才会有NULL:0的输出。
就像4楼说的,在成员函数里边没有用到成员变量、或者虚函数(这两个都需要用到this指针来寻址)。那就不会崩溃。

至于你6楼所说的,没代码,就不知道了。
回复
pengzhixi 2010-09-13
给你个例子:
struct Test{
void fun(){
cout<<"Test"<<endl;
}
};

int main()
{
((Test*)0)->fun();

system("pause");
return 0;
}
回复
willabc 2010-09-13
学习!!!
回复
hastings 2010-09-13
[Quote=引用 6 楼 zmzmyun 的回复:]

引用 4 楼 hastings 的回复:
a = NULL;
//这里的时候,a已经析构delete掉了~~
a->Afun();//此时a->返回的是0;
然后0->Afun();
而Afun中没有读写内存操作,是可以运行通过而不崩溃的~~

不是这样的吧?我不用模板类,定义一个普通的类这样操作后,调用的函数中仍然是个输出语句,但是程序一定会崩的,所以您的解释不成立
[/Quote]
你试过了?
回复
zmzmyun 2010-09-13
[Quote=引用 3 楼 pengzhixi 的回复:]
对一个指向NULL的指针进行delete不会有什么不良影响
[/Quote]
您好像有点文不对题啊?一个空指针正常来讲您觉得能调用任何方法吗?况且所调用的方法也已经被析构掉了
回复
zmzmyun 2010-09-13
[Quote=引用 2 楼 pengzhixi 的回复:]
a->Afun();//你输出结果是不是NULL:0呢。如果是,那么就是正常的啊
[/Quote]
按理来讲对象都析构掉了,还怎么可能调用一个不存在的方法呢?我在不用模板的情况话定义一个类,然后进行这样的操作结果一定是崩溃
回复
zmzmyun 2010-09-13
[Quote=引用 4 楼 hastings 的回复:]
a = NULL;
//这里的时候,a已经析构delete掉了~~
a->Afun();//此时a->返回的是0;
然后0->Afun();
而Afun中没有读写内存操作,是可以运行通过而不崩溃的~~
[/Quote]
不是这样的吧?我不用模板类,定义一个普通的类这样操作后,调用的函数中仍然是个输出语句,但是程序一定会崩的,所以您的解释不成立
回复
pengzhixi 2010-09-13
但是不建议这么用,如果你的A类里面有指针,而且Afun()中有使用这些指针进行读写的话,就很容易出问题。
回复
hastings 2010-09-13
a = NULL;
//这里的时候,a已经析构delete掉了~~
a->Afun();//此时a->返回的是0;
然后0->Afun();
而Afun中没有读写内存操作,是可以运行通过而不崩溃的~~
回复
加载更多回复
相关推荐
发帖
C++ 语言
创建于2007-09-28

6.0w+

社区成员

C++ 语言相关问题讨论,技术干货分享,前沿动态等
申请成为版主
帖子事件
创建了帖子
2010-09-13 05:46
社区公告
暂无公告