递增运算符重载
作用: 通过重载递增运算符,实现自己的整型数据
重载前置递增运算符
classMyInteger {
friendostream&operator<<(ostream&out, MyIntegermyint);
public:
MyInteger() {
m_Num=0;
}
//前置++
MyInteger&operator++() {//注意&
//先++
m_Num++;
//再返回
return*this;
}
private:
intm_Num;
};
//左移运算符重载
ostream&operator<<(ostream&out, MyIntegermyint) {//加上&就是引用传递。
out<<myint.m_Num;
returnout;
}
//前置++ 先++ 再返回
voidtest01() {
MyIntegermyInt;
cout<<++myInt<<endl;//先++,后输出
cout<<myInt<<endl;
}
intmain() {
test01();//输出结果:1 1
system("pause");
return0;
}
注意:cout << ++myInt << endl;//先++,后输出
先++,后输出是指先运行成员函数前置递增运算符重载,再运行函数左移运算符重载。所以先++完成后再传入左移运算符重载函数中,要么是引用传递,要么是拷贝传递,上图使用的是拷贝传递,都可。
PS:为什么MyInteger& operator++() {}
处要使用&
//预期目的:两次递增运算都是作用在同一个对象上
inta=0;
cout<<++(++a) <<endl;//2
//如果返回值是引用,那么返回值就是本身,如果返回值是一个值,实际上返回的是一个值的副本,拷贝构造。
//若是去除了引用,拷贝构造函数被调用,创建了临时对象,没有在原对象上进行操作,所以输出的不一样。
//引用是为了对一个数据进行递增操作
MyInteger&operator++() {
m_Num++;
return*this;
}
重载后置递增运算符
#include<iostream>
usingnamespacestd;
classMyInteger {
friendostream&operator<<(ostream&out, MyIntegermyint);//注意&
public:
MyInteger() {
m_Num=0;
}
//后置++ ,int代表占位参数,可以用于区分前置和后置递增。
MyIntegeroperator++(int) {
//先返回
MyIntegertemp=*this; //记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++;
m_Num++;
returntemp;//拷贝构造
}
private:
intm_Num;
};
//左移运算符重载
ostream&operator<<(ostream&out, MyIntegermyint) {
out<<myint.m_Num;
returnout;
}
//后置++ 先返回 再++
voidtest02() {
MyIntegermyInt;
cout<<myInt++<<endl;//先输出后++
cout<<myInt<<endl;
}
intmain() {
test02();//输出结果:0 1
system("pause");
return0;
}
PS:后置递增返回值的原因:
MyIntegeroperator++(int) {
//先返回
MyIntegertemp=*this; //记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++;
m_Num++;
returntemp;
}
如果返回值是引用,局部对象在当前成员函数执行完后释放,再返回局部对象的引用就是非法操作。如果返回值是一个值,实际上返回的是一个值的副本,因为返回是一个拷贝构造过程,原来的释放了,但是拷贝了一份新的,不受成员函数释放的影响。(详细见前面文章回顾)
这里可以直接 new 一个类,然后就可以返回引用了。
#include<iostream>
usingnamespacestd;
classMyInteger {
friendostream&operator<<(ostream&out, MyInteger&myint);
public:
MyInteger() {
m_Num=0;
}
//后置++ ,int代表占位参数,可以用于区分前置和后置递增。
//在堆区创建
MyInteger&operator++(int) {
temp=newMyInteger(*this);
m_Num++;
return*temp;
}
~MyInteger() {//析构时释放堆区
if (temp!=NULL)
{
deletetemp;
temp=NULL;
}
}
public:
MyInteger*temp ;
private:
intm_Num;
};
//左移运算符重载
ostream&operator<<(ostream&out, MyInteger&myint) {//注意此处是取址符
out<<myint.m_Num;
returnout;
}
//后置++ 先返回 再++
voidtest02() {
MyIntegermyInt;
cout<<myInt++<<endl;//先输出,后++
cout<<myInt<<endl;
}
intmain() {
test02();//输出结果:0 1
system("pause");
return0;
}
注意1:cout << myInt++ << endl;//先输出,后++
虽然是先输出后++,但是运行时同前置递增重载运行顺序,先运行后置递增重载成员函数,再运行左移运算符重载全局函数。
cout << myInt++ << endl;//先输出,后++
先调用 左移运算符重载全局函数 输出开辟到堆区的*temp再对栈区的myInt做后置++操作
cout << myInt << endl;
之后再执行第二次输出,再次调用 左移运算符重载全局函数 引用传入后置递增后的myInt,注意易错点:为什么使用引用?
使用引用的原因:解决浅拷贝问题!
如果不加&符号operator<<(ostream& out, MyInteger myint)
传入的是对myInt的拷贝,在这个左移运算符重载全局函数运行完输出之后会对这个拷贝对象进行释放,从而运行了这个拷贝对象中的析构函数,提前释放了堆区数据。当test02()
运行完成后会对myInt进行释放,从而会再一次运行析构函数去释放堆区的数据从而报错。
注意2:后置递增因为一直是在对temp进行增加,因此无法使用(myint++)++
,返回的temp的值,再被<<输入时,只能是值的状态。因为执行完++时temp已经被释放没有内存空间,也就不能产生同地址的引用。
注意3:就算是正常的(a++)++
这样的语句也会报错。
后置++操作正常是先引用后递增,所以这里用了一个temp来记录递增之前的值,而不是直接返回原来的数的引用,但这里确实不可以进行链式操作了,因为返回回来的对象不是原来的对象,返回的对象是temp。
总结: 前置递增返回引用,后置递增返回值