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

时间:2021-11-08 21:11:11
最近在研究智能指针时,随手写了一段应该崩的代码,结果却让我崩了,先上代码给各位高手过目:

#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();应该出错才对,可是结果却是调用成功,没有任何异常,请教各位高手这是为什么呢?

23 个解决方案

#1


看样子,你的scoped_refptr类重载了赋值操作,当a = NULL;时,内部的指针做了一定的处理..没仔细看,下班走人了

#2


a->Afun();//你输出结果是不是NULL:0呢。如果是,那么就是正常的啊

#3


对一个指向NULL的指针进行delete不会有什么不良影响

#4


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

#5


但是不建议这么用,如果你的A类里面有指针,而且Afun()中有使用这些指针进行读写的话,就很容易出问题。

#6


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

不是这样的吧?我不用模板类,定义一个普通的类这样操作后,调用的函数中仍然是个输出语句,但是程序一定会崩的,所以您的解释不成立

#7


引用 2 楼 pengzhixi 的回复:
a->Afun();//你输出结果是不是NULL:0呢。如果是,那么就是正常的啊

按理来讲对象都析构掉了,还怎么可能调用一个不存在的方法呢?我在不用模板的情况话定义一个类,然后进行这样的操作结果一定是崩溃

#8


引用 3 楼 pengzhixi 的回复:
对一个指向NULL的指针进行delete不会有什么不良影响

您好像有点文不对题啊?一个空指针正常来讲您觉得能调用任何方法吗?况且所调用的方法也已经被析构掉了

#9


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

不是这样的吧?我不用模板类,定义一个普通的类这样操作后,调用的函数中仍然是个输出语句,但是程序一定会崩的,所以您的解释不成立

你试过了? 求高人看看这段诡异的代码

#10


学习!!!

#11


给你个例子:
struct Test{
    void fun(){
         cout<<"Test"<<endl;
    }
};

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

system("pause");
return 0;
}

#12


没自己写过这么长的智能指针……,还好代码看得懂,说下我的理解。
4楼的解释应该差不多。
a->Afun();
a里边的ptr是NULL,所以:NULL->Afun();
别忘了,这里的Afun()不是虚函数,所以编译器在生成指令的时候是直接生成对函数Afun()的调用。它在生成指令的时候不知道ptr会等于NULL,因为是成员函数,所以还会传递一个this指针,这个this指针就是ptr的值,是NULL(也就是0)。所以才会有NULL:0的输出。
就像4楼说的,在成员函数里边没有用到成员变量、或者虚函数(这两个都需要用到this指针来寻址)。那就不会崩溃。

至于你6楼所说的,没代码,就不知道了。

#13


引用 9 楼 hastings 的回复:
引用 6 楼 zmzmyun 的回复:

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

不是这样的吧?我不用模板类,定义一个普通的类这样操作后,调用的函数中仍然是个输出语句,但是程序……

当然试过啦,就是因为一般来说都会崩溃,而这里没有崩溃我才发帖请教的

#14


没看懂,呵呵

#15


引用 13 楼 zmzmyun 的回复:
当然试过啦,就是因为一般来说都会崩溃,而这里没有崩溃我才发帖请教的
编译下我11楼给的代码

#16


访问空指针的后果为“未定义”,崩溃只是其中一种可能。你现在真学到的就是第二种可能。
其实不存在“一般XXXX”这种说法,你必须修正自己的错误认识。

#17


楼主:

实际上,当你写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来读写所指内存,因而没问题,反之必崩!)

#18


引用 17 楼 gules 的回复:
楼主:

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

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

所以0->Afun()实际上就是A_Afun(this); (this为NULL)
而你的代码有保护检查,如下:
    void Afun()
    {
……

我觉得您分析的很有道理,但是还有一点我一直想不通,难道说一个类被析构了,它的成员函数还存在于内存中吗?我的成员函数又不是静态的,我的理解是它应该随着这个类一起销毁掉了才对呀,还请赐教

#19


其一,将a=NULL并不会析构a;
其二,类的成员函数的定义是(函数体的代码)与类的某个对象的生命周期不是一回事,无论你产生多少个对象,其成员函数的二进制代码也只有一份拷贝。其实与静态成员函数代码是一样的,只不过静态成员函数内没有this指针。

#20


⊙﹏⊙b汗。我12楼的回复被忽略了。
看你18楼的回复,觉得你对对象和类的关系搞不清,先学习基础再说吧。
如果,了解了编译器是怎么处理对非虚成员函数的调用的、了解了类跟对象的关系,就不会有那么多的疑问。

#21


delete 只是给内存至乐个标志 只要不访问里面的数据成员 就没得问题

#22


引用 20 楼 wuxupeng999 的回复:
⊙﹏⊙b汗。我12楼的回复被忽略了。
看你18楼的回复,觉得你对对象和类的关系搞不清,先学习基础再说吧。
如果,了解了编译器是怎么处理对非虚成员函数的调用的、了解了类跟对象的关系,就不会有那么多的疑问。

不好意思,不是忽略您的回复,的确如您所说,我这边基础不太好,对象和类有点混乱了,今天我用同样的代码跑了一次我前面说的必崩的代码,居然没有崩,看来确实如16楼所说的那样,崩溃只是一种可能,最后感谢您的回复,小弟受教了

#23


引用 19 楼 gules 的回复:
其一,将a=NULL并不会析构a;
其二,类的成员函数的定义是(函数体的代码)与类的某个对象的生命周期不是一回事,无论你产生多少个对象,其成员函数的二进制代码也只有一份拷贝。其实与静态成员函数代码是一样的,只不过静态成员函数内没有this指针。

您可能没有仔细看代码,这个是智能指针,在赋空值时其对象同时也被析构了
您说的第二点我没有意见,现在基本搞清楚原因了,在这里谢谢您啦

#1


看样子,你的scoped_refptr类重载了赋值操作,当a = NULL;时,内部的指针做了一定的处理..没仔细看,下班走人了

#2


a->Afun();//你输出结果是不是NULL:0呢。如果是,那么就是正常的啊

#3


对一个指向NULL的指针进行delete不会有什么不良影响

#4


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

#5


但是不建议这么用,如果你的A类里面有指针,而且Afun()中有使用这些指针进行读写的话,就很容易出问题。

#6


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

不是这样的吧?我不用模板类,定义一个普通的类这样操作后,调用的函数中仍然是个输出语句,但是程序一定会崩的,所以您的解释不成立

#7


引用 2 楼 pengzhixi 的回复:
a->Afun();//你输出结果是不是NULL:0呢。如果是,那么就是正常的啊

按理来讲对象都析构掉了,还怎么可能调用一个不存在的方法呢?我在不用模板的情况话定义一个类,然后进行这样的操作结果一定是崩溃

#8


引用 3 楼 pengzhixi 的回复:
对一个指向NULL的指针进行delete不会有什么不良影响

您好像有点文不对题啊?一个空指针正常来讲您觉得能调用任何方法吗?况且所调用的方法也已经被析构掉了

#9


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

不是这样的吧?我不用模板类,定义一个普通的类这样操作后,调用的函数中仍然是个输出语句,但是程序一定会崩的,所以您的解释不成立

你试过了? 求高人看看这段诡异的代码

#10


学习!!!

#11


给你个例子:
struct Test{
    void fun(){
         cout<<"Test"<<endl;
    }
};

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

system("pause");
return 0;
}

#12


没自己写过这么长的智能指针……,还好代码看得懂,说下我的理解。
4楼的解释应该差不多。
a->Afun();
a里边的ptr是NULL,所以:NULL->Afun();
别忘了,这里的Afun()不是虚函数,所以编译器在生成指令的时候是直接生成对函数Afun()的调用。它在生成指令的时候不知道ptr会等于NULL,因为是成员函数,所以还会传递一个this指针,这个this指针就是ptr的值,是NULL(也就是0)。所以才会有NULL:0的输出。
就像4楼说的,在成员函数里边没有用到成员变量、或者虚函数(这两个都需要用到this指针来寻址)。那就不会崩溃。

至于你6楼所说的,没代码,就不知道了。

#13


引用 9 楼 hastings 的回复:
引用 6 楼 zmzmyun 的回复:

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

不是这样的吧?我不用模板类,定义一个普通的类这样操作后,调用的函数中仍然是个输出语句,但是程序……

当然试过啦,就是因为一般来说都会崩溃,而这里没有崩溃我才发帖请教的

#14


没看懂,呵呵

#15


引用 13 楼 zmzmyun 的回复:
当然试过啦,就是因为一般来说都会崩溃,而这里没有崩溃我才发帖请教的
编译下我11楼给的代码

#16


访问空指针的后果为“未定义”,崩溃只是其中一种可能。你现在真学到的就是第二种可能。
其实不存在“一般XXXX”这种说法,你必须修正自己的错误认识。

#17


楼主:

实际上,当你写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来读写所指内存,因而没问题,反之必崩!)

#18


引用 17 楼 gules 的回复:
楼主:

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

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

所以0->Afun()实际上就是A_Afun(this); (this为NULL)
而你的代码有保护检查,如下:
    void Afun()
    {
……

我觉得您分析的很有道理,但是还有一点我一直想不通,难道说一个类被析构了,它的成员函数还存在于内存中吗?我的成员函数又不是静态的,我的理解是它应该随着这个类一起销毁掉了才对呀,还请赐教

#19


其一,将a=NULL并不会析构a;
其二,类的成员函数的定义是(函数体的代码)与类的某个对象的生命周期不是一回事,无论你产生多少个对象,其成员函数的二进制代码也只有一份拷贝。其实与静态成员函数代码是一样的,只不过静态成员函数内没有this指针。

#20


⊙﹏⊙b汗。我12楼的回复被忽略了。
看你18楼的回复,觉得你对对象和类的关系搞不清,先学习基础再说吧。
如果,了解了编译器是怎么处理对非虚成员函数的调用的、了解了类跟对象的关系,就不会有那么多的疑问。

#21


delete 只是给内存至乐个标志 只要不访问里面的数据成员 就没得问题

#22


引用 20 楼 wuxupeng999 的回复:
⊙﹏⊙b汗。我12楼的回复被忽略了。
看你18楼的回复,觉得你对对象和类的关系搞不清,先学习基础再说吧。
如果,了解了编译器是怎么处理对非虚成员函数的调用的、了解了类跟对象的关系,就不会有那么多的疑问。

不好意思,不是忽略您的回复,的确如您所说,我这边基础不太好,对象和类有点混乱了,今天我用同样的代码跑了一次我前面说的必崩的代码,居然没有崩,看来确实如16楼所说的那样,崩溃只是一种可能,最后感谢您的回复,小弟受教了

#23


引用 19 楼 gules 的回复:
其一,将a=NULL并不会析构a;
其二,类的成员函数的定义是(函数体的代码)与类的某个对象的生命周期不是一回事,无论你产生多少个对象,其成员函数的二进制代码也只有一份拷贝。其实与静态成员函数代码是一样的,只不过静态成员函数内没有this指针。

您可能没有仔细看代码,这个是智能指针,在赋空值时其对象同时也被析构了
您说的第二点我没有意见,现在基本搞清楚原因了,在这里谢谢您啦