函数的按引用返回与按地址返回
函数值按引用返回常见的一个错误:
先来看一个错误代码:
- #include<iostream>
- using namespace std;
- class A
- {
- private:
- int a;
- public:
- A(int num):a(num){cout<<"构造函数!"<<endl;}
- A(A &b)
- {
- a=b.a;
- cout<<"复制构造函数!"<<endl;
- }
- ~A()
- {
- cout<<"析构函数"<<endl;
- }
- void Setkey(int num)
- {
- a=num;
- }
- int GetKey()
- {
- return this->a;
- }
- };
- //按引用返回,但是a是局部变量,因此超出fun函数作用域之后
- //a就消失了,所以b是一个不存在的对象的别名,所以程序输出随机数
- A&fun()
- {
- A a(3);
- return a;
- }
- int main()
- {
- A &b=fun();
- cout<<b.GetKey()<<endl;
- return 0;
- }
该代码输出随机数字,而不是3,原因如代码中注释所说,按引用返回,但是a是局部变量,因此超出fun函数作用域之后a就消失了,所以b是一个不存在的对象的别名,所以程序输出随机数
我们可以做如下修改:去掉A&fun()中的&: 【成都IT培训】
- #include<iostream>
- using namespace std;
- class A
- {
- private:
- int a;
- public:
- A(int num):a(num){cout<<"构造函数!"<<endl;}
- A(A &b)
- {
- a=b.a;
- cout<<"复制构造函数!"<<endl;
- }
- ~A()
- {
- cout<<"析构函数"<<endl;
- }
- void Setkey(int num)
- {
- a=num;
- }
- int GetKey()
- {
- return this->a;
- }
- };
- //函数按值返回,必定会调用类的复制构造函数构造一个对象a的副本
- //在main函数中定义一个对象作为副本的别名,而对于引用而言,如
- //果引用的是一个临时变量,那么这个临时变量的生存期不少于引用的
- //生存期。也就是说main函数中的副本会在b这个别名生存期结束
- //之后才消失。
- A fun()
- {
- A a(3);
- return a;
- }
- int main()
- {
- A &b=fun();
- cout<<b.GetKey()<<endl;
- return 0;
- }
构造函数!
复制构造函数!
析构函数
3
析构函数
请按任意键继续. . .
正如代码中的注释所说,
- 函数按值返回,必定会调用类的复制构造函数构造一个对象a的副本在main函数中定义一个对象作为副本的别名,而对于引用而言,如引用的是一个临时变量,那么这个临时变量的生存期不少于引用的生存期。也就是说main函数中的副本会在b这个别名生存期结束之后才消失。
下面看一个利用按引用返回和按地址返回来解决内存泄露的问题:
- #include<iostream>
- using namespace std;
- class A
- {
- private:
- int a;
- public:
- A(int num):a(num){cout<<"构造函数!"<<endl;}
- A(A &b)
- {
- a=b.a;
- cout<<"复制构造函数!"<<endl;
- }
- ~A()
- {
- cout<<"析构函数"<<endl;
- }
- void Setkey(int num)
- {
- a=num;
- }
- int GetKey()
- {
- return this->a;
- }
- };
- A fun()
- {
- A *a=new A(3);
- return *a;
- }
- int main()
- {
- A &b=fun();
- cout<<b.GetKey()<<endl;
- return 0;
- }
该段代码虽然能够正确的输出结果但是却会导致内存泄露。在fun函数中在堆中开辟空间,用指针a来指向该空间,但是指针式临时变量,超出fun函数后就消失,所以开辟的空间无法消除。解决的方法是按引用传递或按地址传递。 【成都IT培训】
按 引用传递的方法代码如下:
- #include<iostream>
- using namespace std;
- class A
- {
- private:
- int a;
- public:
- A(int num):a(num){cout<<"构造函数!"<<endl;}
- A(A &b)
- {
- a=b.a;
- cout<<"复制构造函数!"<<endl;
- }
- ~A()
- {
- cout<<"析构函数"<<endl;
- }
- void Setkey(int num)
- {
- a=num;
- }
- int GetKey()
- {
- return this->a;
- }
- };
- A &fun()
- {
- A *a=new A(3);
- return *a;
- }
- int main()
- {
- A &b=fun();
- cout<<b.GetKey()<<endl;
- delete &b;
- return 0;
- }
按地址传递的方法如下:
- #include<iostream>
- using namespace std;
- class A
- {
- private:
- int a;
- public:
- A(int num):a(num){cout<<"构造函数!"<<endl;}
- A(A &b)
- {
- a=b.a;
- cout<<"复制构造函数!"<<endl;
- }
- ~A()
- {
- cout<<"析构函数"<<endl;
- }
- void Setkey(int num)
- {
- a=num;
- }
- int GetKey()
- {
- return this->a;
- }
- };
- A *fun()
- {
- A *a=new A(3);
- return a;
- }
- int main()
- {
- A *b=fun();
- cout<<b->GetKey()<<endl;
- delete b;
- return 0;
这两种做法都不合理,容易忽略堆中的内存删除,所以最好是在哪里开辟空间在哪里撤销空间。对本代码来说,可以在main函数中申请空间,然后通过传递参数的方式传到fun函数中,在main的末尾进行delete。代码如下:
- #include<iostream>
- using namespace std;
- class A
- {
- private:
- int a;
- public:
- A(int num):a(num){cout<<"构造函数!"<<endl;}
- A(A &b)
- {
- a=b.a;
- cout<<"复制构造函数!"<<endl;
- }
- ~A()
- {
- cout<<"析构函数"<<endl;
- }
- void Setkey(int num)
- {
- a=num;
- }
- int GetKey()
- {
- return this->a;
- }
- };
- void fun(A &a)
- {
- a.Setkey(99);
- }
- int main()
- {
- A *b=new A(23);
- fun(*b);
- cout<<b->GetKey()<<endl;
- delete b;
- return 0;
- }