关于重载左移和递增/递减运算符,左移重载函数的形参有无加引用而引发的问题

  最近在看视频学习C++运算符重载部分时,遇到了一个问题,其表现是前置递增( cout << ++myint <<endl;//myint是一个对象)可以正常输出但是后置递增(cout << myint++ << end)一直报错。网上找的博文解释也相对晦涩。后面自己对此问题着重思考,结合了前面的知识和网上的一些解释,也算是对自己所学知识的巩固,在这里做一个记录。

  本文所测试的内容是重载左移运算符(<<)和递增运算符(++)组合运用。采用全局函数重载左移运算符,成员函数重载递增运算符。

1. 写左移运算符重载函数

  首先回顾一下左移运算符的重载函数的构造(其中MyInteger是一个类,内的成员属性只含私有的m_num),当时本着不浪费内存的原则,想统统都用引用传递(此函数的第二个形参的引用是问题的伏笔):

//全局函数重载左移运算符
ostream& operator<<(ostream& cout, MyInteger &myint)
{
	cout << myint.m_num;
	return cout;
}

  注意最后需要让此全局函数作为测试类MyInteger的一个友元,才能访问对象myint中的私有属性m_num。

  此时能够正常使用对象的输出功能:cout << myint << endl;

2. 写递增运算符重载函数(包括前置和后置)
   利用成员函重载前置递增运算符和后置递增运算符:

MyInteger &operator++()//前置递增重载函数
	{
		this->m_num ++;
		return *this;
	}
	
MyInteger operator++(int)//后置递增重载函数,用int进行重载
	{
		MyInteger temp = *this;
		m_num++;
		return temp;
	}

  此处解释为什么前置递增重载函数返回的是一个引用(*this此时返回的是对象本身),而后置递增重载函数反回的是一个值。
  前置:为什么返回引用,不返回值??

  如果返回值,cout << ++(++myint) << endl;

  此代码在执行一次递增之后再执行递增会拷贝复制出新对象,后续便是对一个新的对象进行操作,无法链式操作。返回引用是为了一直对一个数据进行递增操作。

  后置:为什么返回值,不返回引用??

  temp是局部变量,在当前函数执行完之后,局部变量就会被释放,返回引用就是非法操作。(此时编译器比较人性会做一次值保留,但是后续使用便不会保留,因此禁止如此非法操作)

3. 通过测试函数测试结果

  现在有了左移运算符的重载全局函数,以及递增运算符的重载成员函数。下面进行测试输出;

//测试函数
void test1()
{
	MyInteger myint;//创建一个测试对象
	cout << ++myint <<endl;//实现前置递增并输出
	cout << myint++ << endl;//实现后置递增并输出
	cout << myint << endl;
}

此处,问题便出现了,代码:

  cout << ++myint <<endl;

  cout << myint << endl;
  都能正常输出,但是加上:cout << myint++ << endl;
  程序便报错。

4. 解决方法

  后面照着其他人的说法以及自己的测试,有两种修改方法
  1、在左移运算符的重载函数ostream& operator<<(ostream& cout, MyInteger & myint)的第二个形参的前面加一个const变成:
ostream& operator<<(ostream& cout,const MyInteger & myint)
  2、去掉ostream& operator<<(ostream& cout, MyInteger & myint)的第二个形参前面的引用(&)符号。

5. 本质解释
   自己对此两种方法进行了思考,发现就是一个简单的常量引用的问题

  首先解释一下代码的执行顺序:不论<<++myint还是<<myint++,虽然其显示结果也和默认的递增符一样(前置先递增再输出,后置先输出再递增),但其本质上却都是先调用++重载,再调用<<重载(这一点可能与默认数据类型的前置后置执行顺序不一样)。

  接下来解释左移重载写ostream &operator<<(ostream &cout , MyInteger &myint)时,为什么前置递增(cout<<++myint)可以,而后置递增(cout<<myint++)会报错:

  1、前置递增(++myint)时,重载返回的是对象myint,因此可以采用引用的方式传左移重载函数(<<)中进行重载;

  2、但后置递增(myint++)时,对象myint虽然加了1,但是返回的却是局部变量temp记录的值(常量)而不是对象myint,此时需要拿个变量去接收,但是左移重载函数并没有实现此功能,而是直接传进去引用了,相当于MyInteger &myint = 10(直接给常量起别名myint,但这是非法的)。

  因此问题就变成了解决常量引用的问题。有两种修改方法来修改左移重载函数的函数头(回到了前面所学知识):
  1、……,MyInteger myint) 将常数0传入第二个形参,相当于MyInteger myint = 0(隐式转换法),即MyInteger myint=MyInteger(0)(定义一个新形参对象myint,然后将0传进去) ;
  2、……,const MyInteger &myint) 将常数0传入第二个形参,相当于const MyInteger &myint = 0(开辟一片新内存,并用新的引用名myint操作这块内存;此时即使不知道此内存变量的具体名字,但是可以用此引用名对内存进行操作)

递减同理。

整个测试源码如下:

#include<iostream>
using namespace std;


//定义一个测试类
class MyInteger 
{
	friend ostream& operator<<(ostream& cout, const MyInteger  &myint); //左移运算符重载函数作友元

public:
	MyInteger() //无参构造函数初始化对象  
	{
		m_num = 0;
	}

	MyInteger &operator++()//前置递增重载函数
	{
		this->m_num ++;
		return *this;
	}
	MyInteger operator++(int)//后置递增重载函数
	{
		MyInteger temp = *this;
		m_num++;
		return temp;
	}
private:
	int m_num;
};

//全局函数重载左移运算符
ostream& operator<<(ostream& cout, const MyInteger & myint)
{
	cout << myint.m_num;
	return cout;
}


//测试函数
void test1()
{
	MyInteger myint;//创建一个测试对象
	cout << ++myint <<endl;//实现前置递增并输出
	cout << myint++ << endl;//实现后置递增并输出
	cout << myint << endl;
}


int main()
{
	test1(); //调用测试函数
	system("pause");
	return 0;
}

如有错误,敬请指正。

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值