原文地址 Godam
起因
在看了关于运算符重载的网课之后,还有很多疑惑,同学发来一起讨论:
- #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;
- }
暂且称之为第②解决方案。
为什么加上&就不可以了呢?想起了之前关于右值左值的内容,大家可以到这里看一下Godam ,思考之后才想起来,在重载函数返回值时,其值是作为一个右值返回的,既然是右值,就不存在引用了,这样就能够解释为什么没有与之匹配的<<重载函数了,所以,这里只需要将右值找一个中间变量接受,之后作为左值再调用<<重载函数就可以使用了。
代码如下(只修改了主函数部分,为方便复制我直接全部发出来吧):
- #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、全局变量等等,千万不能是局部变量。。这应该是我这样的新手才会遇到的问题吧。
结语
之后参照前部分内容介绍的方法修改了代码,输出了地址,这样简化了前一部分的代码,其中的具体内容原理算是大致理解了,也是在写完关于右值的笔记之后第一次遇到右值问题。之后具体步骤就不写了。。。。。。。整个过程很菜,但还是决定把这篇笔记写下来