原文地址 Godam

起因

    在看了关于运算符重载的网课之后,还有很多疑惑,同学发来一起讨论:

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. class Point{
  5. int _x,_y;
  6. public:
  7. Point(int x=0,int y=0):_x(x),_y(y){}
  8. Point& operator++();//前置++
  9. Point operator++(int);//后置++
  10. Point& operator--();
  11. Point operator--(int);
  12. friend ostream& operator<<(ostream& o,Point& p);
  13. };
  14. Point& Point::operator++(){
  15. _x++;
  16. _y++;
  17. return *this;
  18. }
  19. //后缀式操作符接受一个额外的int型形参(不会使用它,仅作区分使用)
  20. Point Point::operator++(int){
  21. Point temp=*this;
  22. ++*this;//复用了 前缀++的重载
  23. return temp;
  24. //后缀式版本中,返回值是尚未自增的原值,但对象本身已经自增
  25. }
  26. Point& Point::operator--(){
  27. _x--;
  28. _y--;
  29. return *this;
  30. }
  31. Point Point::operator--(int){
  32. Point temp = *this;
  33. --*this;
  34. return temp;
  35. }
  36. ostream& operator<<(ostream& o,Point& p){//友元函数,返回值类型为ostream&,可以支持<<级练操作
  37. o<<"("<<p._x<<","<<p._y<<")"<<endl;
  38. return o;
  39. }
  40.  
  41. int main(int argc, const char * argv[]) {
  42. // insert code here...
  43. Point p(1,2);
  44. cout<<p<<endl;
  45. cout<<p++<<endl;
  46. cout<<++p<<endl;
  47. cout<<p--<<endl;
  48. cout<<--p<<endl;
  49. return 0;
  50. }

 

经过

    这段代码中关于后置++输出时的<<均显示没有与操作数匹配的运算符。前置++可以成功输出,错误应该出在后置++(--)的返回上,对比发现前置的运算符重载函数返回值均为引用类型,注意到这点之后我把函数都改成了引用类型,发现在输出的时候出现了乱码。

    分布运行查看,意识到应该是关于右值的问题,便将所有函数中出现的变量声明为了全局变量,如下所示:

  1. #include <iostream>
  2. using namespace std;
  3.  
  4.  
  5. class Point {
  6. int _x, _y;
  7. public:
  8. Point(int x = 0, int y = 0) :_x(x), _y(y) {}
  9. Point& operator++();//前置++
  10. Point& operator++(int);//后置++
  11. Point& operator--();
  12. Point& operator--(int);
  13. friend ostream& operator<<(ostream& o, Point& p);
  14. };
  15.  
  16. Point temp;
  17.  
  18. Point& Point::operator++() {
  19. _x++;
  20. _y++;
  21. return *this;
  22. }
  23. //后缀式操作符接受一个额外的int型形参(不会使用它,仅作区分使用)
  24. Point& Point::operator++(int) {
  25. temp = *this;
  26. ++*this;//复用了 前缀++的重载
  27. return temp;
  28. //后缀式版本中,返回值是尚未自增的原值,但对象本身已经自增
  29. }
  30. Point& Point::operator--() {
  31. _x--;
  32. _y--;
  33. return *this;
  34. }
  35. Point& Point::operator--(int) {
  36. temp = *this;
  37. --*this;
  38. return temp;
  39. }
  40. ostream& operator<<(ostream& o, Point& p) {//友元函数,返回值类型为ostream&,可以支持<<级练操作
  41. //p._x = p._x + 1;
  42. o << "(" << p._x << "," << p._y << ")" << endl;
  43. return o;
  44. }
  45.  
  46. int main(int argc, const char * argv[]) {
  47. // insert code here...
  48. Point p(1, 2);
  49. cout << p << endl;
  50. cout << p++ << endl;
  51. cout << ++p << endl;
  52. cout << p-- << endl;
  53. cout << --p << endl;
  54. return 0;
  55. }

 

    姑且将之称为解决方案①吧,但这样声明为全局变量之后会变得很不安全(咱也不知道为啥,大家都这么说),之后问了学长,发现直接删去<<重载函数中的参数列表里的&就可以了。修改如下(别忘记修改友元处):

  1. ostream& operator<<(ostream& o, Point p) {//友元函数,返回值类型为ostream&,可以支持<<级练操作
  2. //p._x = p._x + 1;
  3. o << "(" << p._x << "," << p._y << ")" << endl;
  4. return o;
  5. }

     确实是可以了,但是为什么呢?为什么加上&就不可以了呢?

    emmm,补上完整代码:

  1. #include <iostream>
  2. using namespace std;
  3.  
  4.  
  5. class Point {
  6. int _x, _y;
  7. public:
  8. Point(int x = 0, int y = 0) :_x(x), _y(y) {}
  9. Point& operator++();//前置++
  10. Point operator++(int);//后置++
  11. Point& operator--();
  12. Point operator--(int);
  13. friend ostream& operator<<(ostream& o, Point p);
  14. };
  15.  
  16.  
  17. Point& Point::operator++() {
  18. _x++;
  19. _y++;
  20. return *this;
  21. }
  22. //后缀式操作符接受一个额外的int型形参(不会使用它,仅作区分使用)
  23. Point Point::operator++(int) {
  24. Point temp = *this;
  25. ++*this;//复用了 前缀++的重载
  26. return temp;
  27. //后缀式版本中,返回值是尚未自增的原值,但对象本身已经自增
  28. }
  29. Point& Point::operator--() {
  30. _x--;
  31. _y--;
  32. return *this;
  33. }
  34. Point Point::operator--(int) {
  35. Point temp = *this;
  36. --*this;
  37. return temp;
  38. }
  39. ostream& operator<<(ostream& o, Point p) {//友元函数,返回值类型为ostream&,可以支持<<级练操作
  40. //p._x = p._x + 1;
  41. o << "(" << p._x << "," << p._y << ")" << endl;
  42. return o;
  43. }
  44.  
  45. int main(int argc, const char * argv[]) {
  46. // insert code here...
  47. Point p(1, 2);
  48. cout << p << endl;
  49. cout << p++ << endl;
  50. cout << ++p << endl;
  51. cout << p-- << endl;
  52. cout << --p << endl;
  53. return 0;
  54. }

     暂且称之为第②解决方案。

 

    为什么加上&就不可以了呢?想起了之前关于右值左值的内容,大家可以到这里看一下https://www.godreams.cn/?post=89 ,思考之后才想起来,在重载函数返回值时,其值是作为一个右值返回的,既然是右值,就不存在引用了,这样就能够解释为什么没有与之匹配的<<重载函数了,所以,这里只需要将右值找一个中间变量接受,之后作为左值再调用<<重载函数就可以使用了。

    代码如下(只修改了主函数部分,为方便复制我直接全部发出来吧):

  1. #include <iostream>
  2. using namespace std;
  3.  
  4.  
  5. class Point {
  6. int _x, _y;
  7. public:
  8. Point(int x = 0, int y = 0) :_x(x), _y(y) {}
  9. Point& operator++();//前置++
  10. Point operator++(int);//后置++
  11. Point& operator--();
  12. Point operator--(int);
  13. friend ostream& operator<<(ostream& o, Point& p);
  14. };
  15.  
  16.  
  17. Point& Point::operator++() {
  18. _x++;
  19. _y++;
  20. return *this;
  21. }
  22. //后缀式操作符接受一个额外的int型形参(不会使用它,仅作区分使用)
  23. Point Point::operator++(int) {
  24. Point temp = *this;
  25. ++*this;//复用了 前缀++的重载
  26. return temp;
  27. //后缀式版本中,返回值是尚未自增的原值,但对象本身已经自增
  28. }
  29. Point& Point::operator--() {
  30. _x--;
  31. _y--;
  32. return *this;
  33. }
  34. Point Point::operator--(int) {
  35. Point temp = *this;
  36. --*this;
  37. return temp;
  38. }
  39. ostream& operator<<(ostream& o, Point& p) {//友元函数,返回值类型为ostream&,可以支持<<级练操作
  40. //p._x = p._x + 1;
  41. o << "(" << p._x << "," << p._y << ")" << endl;
  42. return o;
  43. }
  44.  
  45. int main(int argc, const char * argv[]) {
  46. // insert code here...
  47. Point p(1, 2);
  48. cout << p << endl;
  49. Point c = p++;
  50. cout << c << endl;
  51. cout << ++p << endl;
  52. Point d = p--;
  53. cout << d << endl;
  54. cout << --p << endl;
  55. return 0;
  56. }

     这样将右值转化为左值就可以正常输出了。

 

拓展

    为了研究函数类型是引用的情况,我又写了一个简单的代码进行分析。

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. int& sum(int c) {
  5. return c;
  6. }
  7. int co(int b) {
  8. b = b + 1;
  9. return b;
  10. }
  11. int main() {
  12. int a = 3;
  13. co(sum(a));
  14. cout << a;
  15. }

     对于这段代码,刚开始以为是输出4,但运行之后不是,这就是类似于刚一开始出现的问题了,甚至于问题更严重。

 

    首先在第四行,对于函数sum参数列表并没有使用引用,而且在主函数中传递参数时也没有使用引用,所以造成一个严重的问题:试图使用函数去返回一个局部变量的引用。局部变量的生存周期仅在函数的生存周期内,一旦出函数,就要消亡。只能作为右值传递出去,而右值是不可能有引用的,所以这里将函数定义为返回值是引用的情况是毫无意义的,最终得出结论:返回值是引用的一定是一个左值,可以使用this、全局变量等等,千万不能是局部变量。。这应该是我这样的新手才会遇到的问题吧。

结语

    之后参照前部分内容介绍的方法修改了代码,输出了地址,这样简化了前一部分的代码,其中的具体内容原理算是大致理解了,也是在写完关于右值的笔记之后第一次遇到右值问题。之后具体步骤就不写了。。。。。。。整个过程很菜,但还是决定把这篇笔记写下来

C++ 右值引用问题

相关文章: