首先我们从最简单的整型运算来理解前++和后++:
将a=10再赋值20意味着(a=10)返回的是a的空间,又把这个20赋值给这个空间的内存,最后a=20;
++(++a)意味着++a返回的是空间(引用),可以继续作++的调用,引用使得++a是可修改的左值;
又由于后置++的优先级大于前置++,因此20行和21行的本质一样,会出现报错。因为a++操作通过临时量返回其值,该值是一个常量,因此不能被修改(不是左值),而后缀++需要对左值进行操作,所以会引起编译错误。
所谓的左值,说通俗一点就是可以被修改和引用的值,左值可以取地址。与之相对的就是右值。在使用时,左值可以作为右值,但右值不能作为左值。
以两道例题为例:
题目一:X的放大与缩小(运算符重载)
题目描述:
X字母可以放大和缩小,变为n行X(n=1,3,5,7,9,...,21)。例如,3行x图案如下:
现假设一个n行(n>0,奇数)X图案,遥控器可以控制X图案的放大与缩小。遥控器有5个按键,1)show,显示当前X图案;2)show++, 显示当前X图案,再放大图案,n+2;3)++show,先放大图案,n+2,再显示图案;4)show--,显示当前X图案,再缩小图案,n-2;5)--show,先缩小图案,n-2,再显示图案。假设X图案的放大和缩小在1-21之间。n=1时,缩小不起作用,n=21时,放大不起作用。
用类CXGraph表示X图案及其放大、缩小、显示。主函数模拟遥控器,代码如下,不可修改。请补充CXGraph类的定义和实现。
输入要求:
第一行n,大于0的奇数,X图案的初始大小。
第二行,操作次数
每个操作一行,为show、show++、show--、--show、++show之一,具体操作含义见题目。
输出要求:
对每个操作,输出对应的X图案。
输入样例:
3
5
show
show++
show++
++show
--show
输出样例:
XXX
X
XXX
XXX
X
XXX
XXXXX
XXX
X
XXX
XXXXX
XXXXXXXXX
XXXXXXX
XXXXX
XXX
X
XXX
XXXXX
XXXXXXX
XXXXXXXXX
XXXXXXX
XXXXX
XXX
X
XXX
XXXXX
XXXXXXX
代码示例:
#include<iostream>
#include<iomanip>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
class XGraph
{
private:
int n;
public:
XGraph(int nn) :n(nn) {}
XGraph(XGraph& rhs)
{
n = rhs.n;
}
XGraph& operator++()//前置++,返回的是引用;前置用引用则是为了不产生临时变量,可以减少内存的消耗。
{
if (this->n <= 19)
{
this->n += 2;
}
return *this;
}
XGraph operator++(int)//后置++,返回的是临时对象,因为后置的时候原来的对象已经被++改变了。
{ //后置的函数的声明里面参数列表中比前置多了一个int,这里的int其实是作为区分前后置的标志,在函数体里面并没有直接地用到。
XGraph temp = *this;
if (this->n <= 19)
{
this->n += 2;
}
return temp;
}
XGraph& operator--()//原理与++或--相同
{
if (this->n >= 3)
{
this->n -= 2;
}
return *this;
}
XGraph operator--(int)
{
XGraph temp = *this;
if (this->n >= 3)
{
this->n -= 2;
}
return temp;
}
friend ostream& operator<<(ostream& out, const XGraph& rhs);//输出运算符重载,记住特定的格式
};
ostream& operator<<(ostream& out, const XGraph& rhs)
{
for (int i = 0; i < (rhs.n + 1) / 2; i++)
{
for (int j = 0; j < i; j++)
{
cout << " ";
}
for (int j = i; j < rhs.n - i; j++)
{
cout << "X";
}
cout << endl;
}
for (int i = (rhs.n + 1) / 2; i < rhs.n; i++)
{
for (int j = rhs.n - 1 - i; j > 0; j--)
{
cout << " ";
}
for (int j = rhs.n - 1 - i; j <= i; j++)
{
cout << "X";
}
cout << endl;
}
return out;
}
int main()
{
int t, n;
string command;
cin >> n;
XGraph xGraph(n);
cin >> t;
while (t--)
{
cin >> command;
if (command == "show++")
{
cout << xGraph++ << endl;
}
else if (command == "++show")
{
cout << ++xGraph << endl;
}
else if (command == "show--")
{
cout << xGraph-- << endl;
}
else if (command == "--show")
{
cout << --xGraph << endl;
}
else if (command == "show")
cout << xGraph << endl;
}
return 0;
}
前置++,返回的是引用;前置用引用则是为了不产生临时变量,可以减少内存的消耗。
后置++,返回的是临时对象,因为后置的时候原来的对象已经被++改变了。
后置的函数的声明里面参数列表中比前置多了一个int,这里的int其实是作为区分前后置的标志,在函数体里面并没有直接地用到。
再以整数运算为例,如果是 cout << a++; 的话,是先输出a的值,a再加1。当运行后置++的时候,我们先备份当前对象的值,然后在返回前把它做了加一的动作,然后返回备份的值。因为备份的值是局部对象,因此不能返回局部对象的引用,引用的话就是说你出了这个函数,空间必须还在,但局部对象在函数结束后就被析构了,所以只能返回对象。
题目二:三维点坐标平移(增量运算符重载)
题目描述:
定义一个三维点Point类,利用友元函数重载"++"和"--"运算符,并区分这两种运算符的前置和后置运算。
++表示x\y\z坐标都+1,--表示x\y\z坐标都-1
请完成以下程序填空
输入要求:
只有一行输入,输入三个整数,表示点的x/y/z坐标
输出要求:
由主函数自行输出
输入样例:
10 20 30
输出样例:
x=11 y=21 z=31
x=10 y=20 z=30
x=11 y=21 z=31
x=11 y=21 z=31
x=9 y=19 z=29
x=10 y=20 z=30
x=9 y=19 z=29
x=9 y=19 z=29
代码框架:
#include <iostream>
using namespace std;
class Point;
Point operator -- (Point & );
Point operator -- (Point &, int);
class Point {
private:
int x, y, z;
public:
Point(int tx=0, int ty=0, int tz=0 )
{ x = tx, y = ty, z = tz; }
Point operator ++ ();
Point operator ++ (int);
friend Point operator -- (Point & );
friend Point operator -- (Point &, int);
void print();
};
//完成以下填空
/********** Write your code here! **********/
/*******************************************/
int main()
{ int tx, ty, tz;
cin>>tx>>ty>>tz;
Point p0(tx, ty, tz); //原值保存在p0
Point p1, p2; //临时赋值进行增量运算
//第1行输出
p1 = p0;
p1++;;
p1.print();
//第2行输出
p1 = p0;
p2 = p1++;
p2.print();
//第3、4行输出,前置++
p1 = p0;
(++p1).print();
p1.print();
//第5、6行输出,后置--
p1 = p0;
p1--;
p1.print();
p0.print();
//第7、8行输出,前置--
p1 = p0;
(--p1).print();
p1.print();
return 0;
}
代码示例:
#include <iostream>
using namespace std;
class Point;
Point operator -- (Point & );
Point operator -- (Point &, int);
class Point {
private:
int x, y, z;
public:
Point(int tx=0, int ty=0, int tz=0 )
{ x = tx, y = ty, z = tz; }
Point operator ++ ();
Point operator ++ (int);
friend Point operator -- (Point & );
friend Point operator -- (Point &, int);
void print();
};
//完成以下填空
Point Point::operator++()
{
x++;
y++;
z++;
return *this;
}
Point Point::operator++(int)
{
Point temp = *this;
x++;
y++;
z++;
return temp;
}
Point operator -- (Point& rhs)
{
rhs.x--;
rhs.y--;
rhs.z--;
return rhs;
}
Point operator -- (Point& rhs, int n)
{
Point temp = rhs;
rhs.x--;
rhs.y--;
rhs.z--;
return temp;
}
void Point::print()
{
cout << "x=" << x << " y=" << y << " z=" << z << endl;
}
int main()
{ int tx, ty, tz;
cin>>tx>>ty>>tz;
Point p0(tx, ty, tz); //原值保存在p0
Point p1, p2; //临时赋值进行增量运算
//第1行输出
p1 = p0;
p1++;;
p1.print();
//第2行输出
p1 = p0;
p2 = p1++;
p2.print();
//第3、4行输出,前置++
p1 = p0;
(++p1).print();
p1.print();
//第5、6行输出,后置--
p1 = p0;
p1--;
p1.print();
p0.print();
//第7、8行输出,前置--
p1 = p0;
(--p1).print();
p1.print();
return 0;
}
我们按照要求完成代码,同时用另一种方法完成代码,分别成为代码一、代码二。
代码二:
#include <iostream>
using namespace std;
class Point;
class Point {
private:
int x, y, z;
public:
Point(int tx = 0, int ty = 0, int tz = 0)
{
x = tx, y = ty, z = tz;
}
Point& operator ++ ();
Point operator ++ (int);
Point& operator -- ();
Point operator -- (int);
void print();
};
//完成以下填空
Point& Point::operator++()
{
x++;
y++;
z++;
return *this;
}
Point Point::operator++(int)
{
Point temp = *this;
x++;
y++;
z++;
return temp;
}
Point& Point::operator -- ()
{
x--;
y--;
z--;
return *this;
}
Point Point::operator -- (int)
{
Point temp = *this;
x--;
y--;
z--;
return temp;
}
void Point::print()
{
cout << "x=" << x << " y=" << y << " z=" << z << endl;
}
int main()
{
int tx, ty, tz;
cin >> tx >> ty >> tz;
Point p0(tx, ty, tz); //原值保存在p0
Point p1, p2; //临时赋值进行增量运算
//第1行输出
p1 = p0;
p1++;;
p1.print();
//第2行输出
p1 = p0;
p2 = p1++;
p2.print();
//第3、4行输出,前置++
p1 = p0;
(++p1).print();
p1.print();
//第5、6行输出,后置--
p1 = p0;
p1--;
p1.print();
p0.print();
//第7、8行输出,前置--
p1 = p0;
(--p1).print();
p1.print();
return 0;
}
我们会发现对代码一,有这样的错误:
而代码二则不会产生问题:
其本质原因是:代码一第一个前--返回的是对象,再次前--,是在临时对象上减而不是在原对象,返回对象做对象的备份和原对象不是一个空间。