会飞的鱼

2020
Godam
首页 » 学习笔记 » C++ 右值引用问题

C++ 右值引用问题

起因

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

#include <iostream>
using namespace std;

class Point{
    int _x,_y;
public:
    Point(int x=0,int y=0):_x(x),_y(y){}
    Point& operator++();//前置++
    Point operator++(int);//后置++
    Point& operator--();
    Point operator--(int);
    friend ostream& operator<<(ostream&  o,Point& p);
};
Point& Point::operator++(){
    _x++;
    _y++;
    return *this;
}
//后缀式操作符接受一个额外的int型形参(不会使用它,仅作区分使用)
Point Point::operator++(int){
    Point temp=*this;
    ++*this;//复用了 前缀++的重载
    return temp;
    //后缀式版本中,返回值是尚未自增的原值,但对象本身已经自增
}
Point& Point::operator--(){
    _x--;
    _y--;
    return *this;
}
Point Point::operator--(int){
    Point temp = *this;
    --*this;
    return temp;
}
ostream& operator<<(ostream& o,Point& p){//友元函数,返回值类型为ostream&,可以支持<<级练操作
    o<<"("<<p._x<<","<<p._y<<")"<<endl;
    return o;
}

int main(int argc, const char * argv[]) {
    // insert code here...
    Point p(1,2);
    cout<<p<<endl;
    cout<<p++<<endl;
    cout<<++p<<endl;
    cout<<p--<<endl;
    cout<<--p<<endl;
    return 0;
}


经过

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

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

#include <iostream>
using namespace std;


class Point {
	int _x, _y;
public:
	Point(int x = 0, int y = 0) :_x(x), _y(y) {}
	Point& operator++();//前置++
	Point& operator++(int);//后置++
	Point& operator--();
	Point& operator--(int);
	friend ostream& operator<<(ostream&  o, Point& p);
};

Point temp;

Point& Point::operator++() {
	_x++;
	_y++;
	return *this;
}
//后缀式操作符接受一个额外的int型形参(不会使用它,仅作区分使用)
Point& Point::operator++(int) {
	 temp = *this;
	++*this;//复用了 前缀++的重载
	return temp;
	//后缀式版本中,返回值是尚未自增的原值,但对象本身已经自增
}
Point& Point::operator--() {
	_x--;
	_y--;
	return *this;
}
Point& Point::operator--(int) {
 temp = *this;
	--*this;
	return temp;
}
ostream& operator<<(ostream& o, Point& p) {//友元函数,返回值类型为ostream&,可以支持<<级练操作
	//p._x = p._x + 1;
	o << "(" << p._x << "," << p._y << ")" << endl;
	return o;
}

int main(int argc, const char * argv[]) {
	// insert code here...
	Point p(1, 2);
	cout << p << endl;
	cout << p++ << endl;
	cout << ++p << endl;
	cout << p-- << endl;
	cout << --p << endl;
	return 0;
}


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

ostream& operator<<(ostream& o, Point p) {//友元函数,返回值类型为ostream&,可以支持<<级练操作
	//p._x = p._x + 1;
	o << "(" << p._x << "," << p._y << ")" << endl;
	return o;
}
     确实是可以了,但是为什么呢?为什么加上&就不可以了呢?


    emmm,补上完整代码:

#include <iostream>
using namespace std;


class Point {
	int _x, _y;
public:
	Point(int x = 0, int y = 0) :_x(x), _y(y) {}
	Point& operator++();//前置++
	Point operator++(int);//后置++
	Point& operator--();
	Point operator--(int);
	friend ostream& operator<<(ostream&  o, Point p);
};


Point& Point::operator++() {
	_x++;
	_y++;
	return *this;
}
//后缀式操作符接受一个额外的int型形参(不会使用它,仅作区分使用)
Point Point::operator++(int) {
	 Point temp = *this;
	++*this;//复用了 前缀++的重载
	return temp;
	//后缀式版本中,返回值是尚未自增的原值,但对象本身已经自增
}
Point& Point::operator--() {
	_x--;
	_y--;
	return *this;
}
Point Point::operator--(int) {
 Point temp = *this;
	--*this;
	return temp;
}
ostream& operator<<(ostream& o, Point p) {//友元函数,返回值类型为ostream&,可以支持<<级练操作
	//p._x = p._x + 1;
	o << "(" << p._x << "," << p._y << ")" << endl;
	return o;
}

int main(int argc, const char * argv[]) {
	// insert code here...
	Point p(1, 2);
	cout << p << endl;
	cout << p++ << endl;
	cout << ++p << endl;
	cout << p-- << endl;
	cout << --p << endl;
	return 0;
}
     暂且称之为第②解决方案。


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

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

#include <iostream>
using namespace std;


class Point {
	int _x, _y;
public:
	Point(int x = 0, int y = 0) :_x(x), _y(y) {}
	Point& operator++();//前置++
	Point operator++(int);//后置++
	Point& operator--();
	Point operator--(int);
	friend ostream& operator<<(ostream&  o, Point& p);
};


Point& Point::operator++() {
	_x++;
	_y++;
	return *this;
}
//后缀式操作符接受一个额外的int型形参(不会使用它,仅作区分使用)
Point Point::operator++(int) {
	 Point temp = *this;
	++*this;//复用了 前缀++的重载
	return temp;
	//后缀式版本中,返回值是尚未自增的原值,但对象本身已经自增
}
Point& Point::operator--() {
	_x--;
	_y--;
	return *this;
}
Point Point::operator--(int) {
 Point temp = *this;
	--*this;
	return temp;
}
ostream& operator<<(ostream& o, Point& p) {//友元函数,返回值类型为ostream&,可以支持<<级练操作
	//p._x = p._x + 1;
	o << "(" << p._x << "," << p._y << ")" << endl;
	return o;
}

int main(int argc, const char * argv[]) {
	// insert code here...
	Point p(1, 2);
	cout << p << endl;
	Point c = p++;
	cout << c << endl;
	cout << ++p << endl;
	Point d = p--;
	cout << d << endl;
	cout << --p << endl;
	return 0;
}
     这样将右值转化为左值就可以正常输出了。


拓展

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

#include <iostream>
using namespace std;

int& sum(int c) {
	return c;
}
int co(int b) {
	b = b + 1;
	return b;
}
int main() {
	int a = 3;
	co(sum(a));
	cout << a;
}
     对于这段代码,刚开始以为是输出4,但运行之后不是,这就是类似于刚一开始出现的问题了,甚至于问题更严重。


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

结语

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


文章如无特别注明均为原创! 作者: 果果, 转载或复制请以 超链接形式 并注明出处 GODAM|博客|godam
原文地址《 C++ 右值引用问题》发布于2020-5-6

分享到:
打赏

评论

游客

切换注册

登录

您也可以使用第三方帐号快捷登录

切换登录

注册

sitemap