引用容易犯的错误。

view plaincopy to clipboardprint?

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150

class A   

{   

public:   

    A(int i){x=i;}   

    int get(){return x;}   

private:   

    int x;   

};   

A & func()   

{   

    A a(23);   

    return a;   

}   

int main()   

{   

    A&r=func();   

    cout<<r.get()<<endl;   

    return 0;   

}  

class A

{

public:

A(int i){x=i;}

int get(){return x;}

private:

int x;

};

A & func()

{

A a(23);

return a;

}

int main()

{

A&r=func();

cout<<r.get()<<endl;

return 0;


由于对象a是个局部对象,因此当函数对象func结束后,局部对象a也就被删除了。由于对象a消失了,所以Func()函数返回的其实是一个并不存在的对象的别名。

用这个不存在的对象来调用该对象的函数get()。该函数会返回一个并不存在的对象的x成员。因此输出一个随机数。


view plaincopy to clipboardprint?

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150

class A   

{   

public:   

    A(int i){x=i;}   

    int get(){return x;}   

private:   

    int x;   

};   

A  func()   

{   

    A a(23);   

    return a;   

}   

int main()   

{   

    A&r=func();   

    cout<<r.get()<<endl;   

    return 0;   

}  

class A

{

public:

A(int i){x=i;}

int get(){return x;}

private:

int x;

};

A  func()

{

A a(23);

return a;

}

int main()

{

A&r=func();

cout<<r.get()<<endl;

return 0;


如果这样,那输出就是23,为什么?


因为去掉引用符号后,返回方式变成了按值返回,而按值返回又会调用复制构造函数,复制一个对象a的副本,然后将这个副本再赋给r,r就是这个对象a的副本的别名。


下面举个例子说明这个:


view plaincopy to clipboardprint?

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150

class A   

{   

public:   

    A(int i){cout<<"调用构造函数创建一个对象\n";x=i;}   

    A(A&a){cout<<"执行复制构造函数创建一个对象\n";x=a.x;}   

    ~A(){cout<<"执行析构函数!\n";}   

    int get(){return x;}   

private:   

    int x;   

};   

A  func()   

{   

    cout<<"跳转到func函数中!\n";   

    A a(23);   

    cout<<"对象a的地址"<<&a<<endl;   

    return a;   

}   

int main()   

{   

    A&r=func();   

    cout<<"对象a的副本的地址:"<<&r<<endl;   

    cout<<r.get()<<endl;   

    return 0;   

}  

class A

{

public:

A(int i){cout<<"调用构造函数创建一个对象\n";x=i;}

A(A&a){cout<<"执行复制构造函数创建一个对象\n";x=a.x;}

~A(){cout<<"执行析构函数!\n";}

int get(){return x;}

private:

int x;

};

A  func()

{

cout<<"跳转到func函数中!\n";

A a(23);

cout<<"对象a的地址"<<&a<<endl;

return a;

}

int main()

{

A&r=func();

cout<<"对象a的副本的地址:"<<&r<<endl;

cout<<r.get()<<endl;

return 0;


VC6.0下输出结果:


跳转到func函数中!


调用构造函数创建一个对象


对象a的地址0012FF00


执行复制构造函数创建一个对象


执行析构函数


对象a的副本的地址:0012FF6C


23


执行析构函数!


看到这个结果相信会明白不少东西。但是与此同时,这里有个问题:


为什么对象a的副本的生命会一直持续到main函数结束呢?(上面最后一句输出:执行析构函数!就是对象a的副本的析构,所以有必要怀疑一下他的生命周期!)


这是因为对于引用而言,如果引用的是一个临时变量,那么这个临时变量的生存期会不少于这个引用的生存期。

(参见c++标准[class.temporary])


也就是说,直到main函数结束时,引用r的生存期结束,r所引用的临时变量的生存期才结束,由于r所引用的是一个对象,因此这时才会调用它的析构函数来释放内存。


PS;


顺便说一下,指针是没有这个特性的,假如将对象a的副本的地址赋给一个指针,那么在func函数返回对象a的副本的时候,就可以析构这个对象a的副本。


view plaincopy to clipboardprint?

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150

class A   

{   

public:   

    A(int i){cout<<"调用构造函数创建一个对象\n";x=i;}   

    A(A&a){cout<<"执行复制构造函数创建一个对象\n";x=a.x;}   

    ~A(){cout<<"执行析构函数!\n";}   

    int get(){return x;}   

private:   

    int x;   

};   

A  func()   

{   

    cout<<"跳转到func函数中!\n";   

    A a(23);   

    cout<<"对象a的地址"<<&a<<endl;   

    return a;   

}   

int main()   

{   

    A*r=&func();   

    cout<<"对象a的副本的地址:"<<r<<endl;   

    cout<<r->get()<<endl;   

    return 0;   

}  

class A

{

public:

A(int i){cout<<"调用构造函数创建一个对象\n";x=i;}

A(A&a){cout<<"执行复制构造函数创建一个对象\n";x=a.x;}

~A(){cout<<"执行析构函数!\n";}

int get(){return x;}

private:

int x;

};

A  func()

{

cout<<"跳转到func函数中!\n";

A a(23);

cout<<"对象a的地址"<<&a<<endl;

return a;

}

int main()

{

A*r=&func();

cout<<"对象a的副本的地址:"<<r<<endl;

cout<<r->get()<<endl;

return 0;


输出结果如下:


跳转到func函数中!


调用构造函数创建一个对象


对象a的地址0012FF0C


执行复制构造函数创建一个对象


执行析构函数


执行析构函数


对象a的副本的地址:0012FF78


23


分析:连续执行了两次析构函数,将对象a和他的副本所占用的内存全部释放掉了。


但是我们确实看到了get函数输出了对象a的副本的x成员为23


于此我们也知道对象a的副本已经被删除了啊,已经析构过了啊。为什么这里又输出了他的成员x的值呢?


::这是因为析构函数调用并析构某个对象后,只是告诉编译器这一块内存不再为某个对象独占了,你可以访问它,别的对象或者变量也可以访问它并使用该内存区域存储他们的数据,但是在他们使用之前,存放在该内存区域中的数据并没有删除,因此使用指针来访问该区域仍然能够得到未被修改的x的值。




本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yiruirui0507/archive/2010/10/30/5976786.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值