C++ 右值引用问题

原文地址 Godam

起因

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

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

经过

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

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

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

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

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

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

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

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

拓展

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

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

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

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

结语

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C++11中,右值引用的作用之一是实现移动语义,即对象的资源所有权的转移。在C++11之前,移动语义的缺失是C++所面临的一个问题右值引用也可以看作是一块空间的别名,只能引用右值。通过使用右值引用,我们可以对右值进行引用,并且可以实现对移动语义的支持。右值引用的语法是在类型后面加上两个&&。在函数返回值为临时变量的情况下,可以使用右值引用来接收该临时变量。另外,右值引用还可以引用经过move操作后的左值,通过使用move函数,可以改变左值的属性,使其变成右值。总之,右值引用C++中的作用主要是支持移动语义,提高程序的性能和效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [c++右值引用具体用法](https://download.csdn.net/download/weixin_38734492/14887141)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++11——右值引用](https://blog.csdn.net/weixin_57023347/article/details/120957689)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Godams

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值