【菜鸟C++学习笔记】24. 常见的引用注意问题

来源:互联网 发布:用记事本写c语言 编辑:程序博客网 时间:2024/05/16 19:13

1、引用不存在的对象

例如:

#include <iostream>using namespace std;class A{public:void set(int i){x=i;}int get(){return x;}private:int x;};A &fun(){A a;a.set(20);return a;}int main(){A &r=fun();cout<<r.get()<<endl;return 0;}
输出结果:1245056

分析:在函数fun的定义中创建了一个局部变量对象a,函数返回这个对象的外号,但是此时注意a的生命已经随着fun的结束而结束了,因此此时再为这个空对象创建一个外号时,输出的结果将是一个随机数。

讨论:如果把fun函数定义那一行改成按值返回,则输出的值将是20。这是因为如果饮用的是一个临时变量,那么这个临时变量的生命周期将不小于这个饮用的周期,因此直到main函数结束时,这个r的饮用和其饮用的临时变量的值才会结束。但是注意!指针就没有这个特性,如果改成A *r=fun();的话,那么输出的仍然是上面的随机数。

2、引用按值返回的堆中对象

问题:如果在一个函数中创建一个堆中对象a,并按值返回这个对象,那么返回的这个对象是不是堆中的对象呢?

看下面的程序:

#include <iostream>using namespace std;class A{public:A(int i){x=i;cout<<"构造函数执行"<<endl;}A(A&){cout<<"复制构造函数执行"<<endl;}~A(){cout<<"析构函数执行"<<endl;}int get(){return x;}private:int x;};A fun(){A *p=new A(20);cout<<p<<endl;return *p;}int main(){A &r=fun();cout<<r.get()<<endl;return 0;}
输出结果:

分析:注意函数中创建一个堆中对象并用指针p指向它,返回的是*p,即堆中的数据的值,因此属于按值传递,将调用复制构造函数创建副本,但这个过程是存放在栈中的,因此这个副本不是堆中的对象,在主函数中,为这个副本创建了一个外号,用外号可以访问其数据成员的值。

另外,这个程序只调用了一次析构函数,说明在堆中空间那个没有被析构,将出现内存泄露。

如果尝试为这个外号再创建一个指针*p1,指向这个复制构造函数创建的副本,而这个副本是在栈中的,因此当删除delete *p1时,会出现程序崩溃的情况。

 

3、引用按别名返回的堆中对象

为了避免出现内存泄露的现象,因此不能按值返回堆中的对象,而应该按址或外号返回,因此在第13行改成返回一个地址即可:

#include <iostream>using namespace std;class A{public:A(int i){x=i;cout<<"构造函数执行"<<endl;}A(A&){cout<<"复制构造函数执行"<<endl;}~A(){cout<<"析构函数执行"<<endl;}int get(){return x;}private:int x;};A &fun(){A *p=new A(20);cout<<"p:"<<p<<endl;return *p;}int main(){A &r=fun();cout<<"&r:"<<&r<<endl;cout<<"r的值:"<<r.get()<<endl;A *p1=&r;delete p1;cout<<"r的值:"<<r.get()<<endl;return 0;}

输出结果:

分析:这一次,按地址返回后,该地址指向的是堆中空间的值,因此定义一个新的指针p1后,通过第25行的代码即可删除这块堆中空间。

问题:我们看到第26行再次调用r时,输出了一个随机数,这是因为不能引用空引用,因此我们对此程序做下面的修改:

#include <iostream>using namespace std;class A{public:A(int i){x=i;cout<<"构造函数执行"<<endl;}A(A&){cout<<"复制构造函数执行"<<endl;}~A(){cout<<"析构函数执行"<<endl;}void set(int i){x=i;}int get(){return x;}private:int x;};A &fun(A&r){r.set(30);return r;}int main(){A *p=new A(20);cout<<"p指向的值:"<<p->get()<<endl;fun(*p);cout<<"p指向的值:"<<p->get()<<endl;delete p;return 0;}

输出结果:

分析:

本程序中,在堆中创建空间放到main函数中,将其空间用一个指针p指向,第一次输出时输出的是p指向空间初始化的值。在第23行调用fun函数,将*p作为参数传递给fun函数,fun函数以一个外号接收,而返回r的引用,因此在fun函数中把r指向的值做修改后,也把对空间中原来的值给修改了,因为是外号,修改就会同时修改原值。故输出结果如上面所示。而r这个别名在fun函数结束后生命周期就结束了,因此不会出现引用混乱的情况。

总的来说,总原则是在哪里创建就要记得在哪里释放。