函数的按引用返回与按地址返回

时间:2022-09-30 15:43:06

函数的按引用返回与按地址返回

函数值按引用返回常见的一个错误:

先来看一个错误代码:

[cpp] view plaincopyprint?
  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. private:
  6. int a;
  7. public:
  8. A(int num):a(num){cout<<"构造函数!"<<endl;}
  9. A(A &b)
  10. {
  11. a=b.a;
  12. cout<<"复制构造函数!"<<endl;
  13. }
  14. ~A()
  15. {
  16. cout<<"析构函数"<<endl;
  17. }
  18. void Setkey(int num)
  19. {
  20. a=num;
  21. }
  22. int GetKey()
  23. {
  24. return this->a;
  25. }
  26. };
  27. //按引用返回,但是a是局部变量,因此超出fun函数作用域之后
  28. //a就消失了,所以b是一个不存在的对象的别名,所以程序输出随机数
  29. A&fun()
  30. {
  31. A a(3);
  32. return a;
  33. }
  34. int main()
  35. {
  36. A &b=fun();
  37. cout<<b.GetKey()<<endl;
  38. return 0;
  39. }

该代码输出随机数字,而不是3,原因如代码中注释所说,按引用返回,但是a是局部变量,因此超出fun函数作用域之后a就消失了,所以b是一个不存在的对象的别名,所以程序输出随机数
我们可以做如下修改:去掉A&fun()中的&: 【成都IT培训

[cpp] view plaincopyprint?
  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. private:
  6. int a;
  7. public:
  8. A(int num):a(num){cout<<"构造函数!"<<endl;}
  9. A(A &b)
  10. {
  11. a=b.a;
  12. cout<<"复制构造函数!"<<endl;
  13. }
  14. ~A()
  15. {
  16. cout<<"析构函数"<<endl;
  17. }
  18. void Setkey(int num)
  19. {
  20. a=num;
  21. }
  22. int GetKey()
  23. {
  24. return this->a;
  25. }
  26. };
  27. //函数按值返回,必定会调用类的复制构造函数构造一个对象a的副本
  28. //在main函数中定义一个对象作为副本的别名,而对于引用而言,如
  29. //果引用的是一个临时变量,那么这个临时变量的生存期不少于引用的
  30. //生存期。也就是说main函数中的副本会在b这个别名生存期结束
  31. //之后才消失。
  32. A fun()
  33. {
  34. A a(3);
  35. return a;
  36. }
  37. int main()
  38. {
  39. A &b=fun();
  40. cout<<b.GetKey()<<endl;
  41. return 0;
  42. }
函数的输出结果是:

构造函数!
复制构造函数!
析构函数
3
析构函数
请按任意键继续. . .

正如代码中的注释所说,

[cpp] view plaincopyprint?
  1. 函数按值返回,必定会调用类的复制构造函数构造一个对象a的副本在main函数中定义一个对象作为副本的别名,而对于引用而言,如引用的是一个临时变量,那么这个临时变量的生存期不少于引用的生存期。也就是说main函数中的副本会在b这个别名生存期结束之后才消失。

下面看一个利用按引用返回和按地址返回来解决内存泄露的问题:

[cpp] view plaincopyprint?
  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. private:
  6. int a;
  7. public:
  8. A(int num):a(num){cout<<"构造函数!"<<endl;}
  9. A(A &b)
  10. {
  11. a=b.a;
  12. cout<<"复制构造函数!"<<endl;
  13. }
  14. ~A()
  15. {
  16. cout<<"析构函数"<<endl;
  17. }
  18. void Setkey(int num)
  19. {
  20. a=num;
  21. }
  22. int GetKey()
  23. {
  24. return this->a;
  25. }
  26. };
  27. A fun()
  28. {
  29. A *a=new A(3);
  30. return *a;
  31. }
  32. int main()
  33. {
  34. A &b=fun();
  35. cout<<b.GetKey()<<endl;
  36. return 0;
  37. }

该段代码虽然能够正确的输出结果但是却会导致内存泄露。在fun函数中在堆中开辟空间,用指针a来指向该空间,但是指针式临时变量,超出fun函数后就消失,所以开辟的空间无法消除。解决的方法是按引用传递或按地址传递。 【成都IT培训

按 引用传递的方法代码如下:

[cpp] view plaincopyprint?
  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. private:
  6. int a;
  7. public:
  8. A(int num):a(num){cout<<"构造函数!"<<endl;}
  9. A(A &b)
  10. {
  11. a=b.a;
  12. cout<<"复制构造函数!"<<endl;
  13. }
  14. ~A()
  15. {
  16. cout<<"析构函数"<<endl;
  17. }
  18. void Setkey(int num)
  19. {
  20. a=num;
  21. }
  22. int GetKey()
  23. {
  24. return this->a;
  25. }
  26. };
  27. A &fun()
  28. {
  29. A *a=new A(3);
  30. return *a;
  31. }
  32. int main()
  33. {
  34. A &b=fun();
  35. cout<<b.GetKey()<<endl;
  36. delete &b;
  37. return 0;
  38. }
但是按引用传递会出现一个问题,就是出现空引用,因为b指向的内存已经被删除。

按地址传递的方法如下:

[cpp] view plaincopyprint?
  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. private:
  6. int a;
  7. public:
  8. A(int num):a(num){cout<<"构造函数!"<<endl;}
  9. A(A &b)
  10. {
  11. a=b.a;
  12. cout<<"复制构造函数!"<<endl;
  13. }
  14. ~A()
  15. {
  16. cout<<"析构函数"<<endl;
  17. }
  18. void Setkey(int num)
  19. {
  20. a=num;
  21. }
  22. int GetKey()
  23. {
  24. return this->a;
  25. }
  26. };
  27. A *fun()
  28. {
  29. A *a=new A(3);
  30. return a;
  31. }
  32. int main()
  33. {
  34. A *b=fun();
  35. cout<<b->GetKey()<<endl;
  36. delete b;
  37. return 0;

这两种做法都不合理,容易忽略堆中的内存删除,所以最好是在哪里开辟空间在哪里撤销空间。对本代码来说,可以在main函数中申请空间,然后通过传递参数的方式传到fun函数中,在main的末尾进行delete。代码如下:


[cpp] view plaincopyprint?
  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. private:
  6. int a;
  7. public:
  8. A(int num):a(num){cout<<"构造函数!"<<endl;}
  9. A(A &b)
  10. {
  11. a=b.a;
  12. cout<<"复制构造函数!"<<endl;
  13. }
  14. ~A()
  15. {
  16. cout<<"析构函数"<<endl;
  17. }
  18. void Setkey(int num)
  19. {
  20. a=num;
  21. }
  22. int GetKey()
  23. {
  24. return this->a;
  25. }
  26. };
  27. void fun(A &a)
  28. {
  29. a.Setkey(99);
  30. }
  31. int main()
  32. {
  33. A *b=new A(23);
  34. fun(*b);
  35. cout<<b->GetKey()<<endl;
  36. delete b;
  37. return 0;
  38. }