题目描述
使用c++实现一个笛卡尔坐标系中点的类,要求重载加法,自加(两种),and,右移赋值运算符, 并测试以下样例
(5,5)++ == (5, 5)
++(5,5) == (6,6)
(5,5) & (6,6) == (4,4)
(5,5) >> 2 == (1,1)
题目分析
笛卡尔坐标系
- 1D笛卡尔坐标系为数轴,用单个数表示一个值。
- 在2D笛卡尔坐标系中定位点:两个数(x,y)就可以定位一个点,且2D坐标的标准表示法就是(x,y),x分量表示该点到y轴的有符号距离,同样y分量表示该点到x轴的有符号距离。有符号距离是指某个方向上距离为正,而在相反的方向上为负。
- 在3D中定位一个点需要3个数,x,y和z,分别代表该点到yz,xz和xy平面的有符号距离。对于任意的3D坐标系,通过旋转我们只能使两个轴和目标相同,第三个轴总是和目标方向相反。存在两种完全不同的3D坐标系:左手坐标系和右手坐标系。
本题主要涉及2D笛卡尔坐标系的相关知识。
C++重载运算符
参考博文:https://www.cnblogs.com/sexybear/p/4551742.html
运算符重载:运算符与类结合,产生新的含义。
引入重载运算符可以实现类的多态性(多态是指一个函数名有多种含义)。
可以用类的成员函数或友元函数实现运算符的重载。
友元函数和成员函数的使用场合:一般情况下,建议一元运算符使用成员函数,二元运算符使用友元函数
- 运算符的操作需要修改类对象的状态,则使用成员函数。如需要做左值操作数的运算符(如=,+=,++)
- 运算时,有数和对象的混合运算时,必须使用友元
- 二元运算符中,第一个操作数为非对象时,必须使用友元函数。如输入输出运算符«和»
具体规则如下:
运算符 | 建议使用 |
所有一元运算符 | 成员函数 |
= ( ) [ ] -> | 必须是成员函数 |
+= -= /= *= ^= &= != %= »= «= | 成员函数 |
所有其它二元运算符, 例如: –,+,*,/ | 友元函数 |
« » | 必须是友元函数 |
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
Box operator+(const Box&);
声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:
Box operator+(const Box&, const Box&);
参数和返回值
当参数不会被改变,一般按const引用来传递(若是使用成员函数重载,函数也为const。
对于返回数值的决定:
- 如果返回值可能出现在=号左边, 则只能作为左值, 返回非const引用。
- 如果返回值只能出现在=号右边, 则只需作为右值, 返回const型引用或者const型值。
- 如果返回值既可能出现在=号左边或者右边, 则其返回值须作为左值, 返回非const引用。
代码示例
示例1:重载+运算符
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
示例2:重载« 和»运算符(部分代码)
class Point
{
private:
int x;
public:
Point(int x1)
{ x=x1;}
friend ostream& operator<<(ostream& cout,const Point& p);//使用友元函数重载<<输出运算符
friend istream& operator>>(istream& cin,Point& p);//使用友元函数重载>>输出运算符
};
ostream& operator<<(ostream& cout,const Point& p)
{
cout<<p.x<<endl;
return cout;
}
istream& operator>>(istream& cin,Point& p)
{
cin>>p.x;
return cin;
}
调用: Point a(1); Point b(2); cin»a»b; cout«a«b«endl;
语法:
重载方式:只能使用友元函数重载 且 使用三个引用&
函数名:
- 输出流: operator«(参数表)
- 输入流:operator»(参数表)
参数表:固定(容易出错啊),两个参数均用引用&
- 输出流: 必须是两个参数:对输出流ostream& 和 对象。第一个操作数cout,定义在文件iostream中,是标准类类型ostream的对象的引用。如:ostream& cout,const Point& p
- 输入流:必须是两个参数:对输入流ostream& 和 对象。第一个操作数是cin,定义在文件iostream,实际上是标准类类型istream的对象的引用。如:instream& cin,const Point& p
函数调用:
- 输出流: 显式调用:cout«对象。隐式调用: operator«(cout,对象)
- 输入流:显式调用:cin»对象。隐式调用: operator»(cin,对象)
返回类型:返回类型固定 + 使用返回函数引用(满足连续输出)
- 输出流: 返回ostream&。如:ostream& operator«(ostream& cout,const Point& p)
- 输入流:返回:istream&。如:istream& operator»(istream& cin,Point& p)
注意: «和»操作符的重载必须使用友元函数,因为成员函数要求是有对象调用,则第一个参数必须是类的对象,但是«和»第一个参数是流的对象引用。故不能使用成员函数。
示例3:重载++和- -
class Point
{
private:
int x;
public:
Point(int x1)
{ x=x1;}
Point operator++();//成员函数定义自增
const Point operator++(int x); //后缀可以返回一个const类型的值
friend Point operator--(Point& p);//友元函数定义--
friend const Point operator--(Point& p,int x);//后缀可以返回一个const类型的值
};
Point Point::operator++()//++obj
{
x++;
return *this;
}
const Point Point::operator++(int x)//obj++
{
Point temp = *this;
this->x++;
return temp;
}
Point operator--(Point& p)//--obj
{
p.x--;
return p;
//前缀形式(--obj)重载的时候没有虚参,通过引用返回*this 或 自身引用,也就是返回变化之后的数值
}
const Point operator--(Point& p,int x)//obj--
{
Point temp = p;
p.x--;
return temp;
// 后缀形式obj--重载的时候有一个int类型的虚参, 返回原状态的拷贝
}
函数调用:
Point b(2);
a++;//隐式调用成员函数operator++(0),后缀表达式
++a;//隐式调用成员函数operator++(),前缀表达式
b--;//隐式调用友元函数operator--(0),后缀表达式
--b;//隐式调用友元函数operator--(),前缀表达式
cout<<a.operator ++(2);//显式调用成员函数operator ++(2),后缀表达式
cout<<a.operator ++();//显式调用成员函数operator ++(),前缀表达式
cout<<operator --(b,2);//显式调用友元函数operator --(2),后缀表达式
cout<<operator --(b);//显式调用友元函数operator --(),前缀表达式 </pre>
备注:
1、a++
函数返回:temp(临时变量)
函数返回是否是const类型:返回是一个拷贝后的临时变量),不能出现在等号的左边(临时变量不能做左值),函数的结果只能做右值,则要返回一个const类型的值。
++a
函数返回:*this 函数返回是否是const类型:返回原状态的本身,返回值可以做左值,即函数的结果可以做左值,则要返回一个非const类型的值。
2、前后缀仅从函数名(operator++)无法区分,只能有参数区分,这里引入一个虚参数int x,x可以是任意整数。
3、单目运算符的重载:
重载运算符函数名:operator@(参数表)
隐式调用形式:obj1@ 或 @obj1
显式调用形式:
- 成员函数: ```c++ obj1.operator@( )//前缀
obj1.operator@(0)//后缀
- 友元函数:
```c++
operator@(OBJ obj)//前缀
operator@(OBJ obj,int x)//后缀
执行时,隐式调用形式和显式调用形式都会调用函数operator@()
可重载运算符
- 双目算术运算符 + (加),-(减),*(乘),/(除),% (取模)
- 关系运算符 ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于)
- 逻辑运算符 ||(逻辑或),&&(逻辑与),!(逻辑非)
- 单目运算符 + (正),-(负),*(指针),&(取地址)
- 自增自减运算符 ++(自增),–(自减)
- 位运算符 (按位或),& (按位与),~(按位取反),^(按位异或),,« (左移),»(右移)
- 赋值运算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, «=, »=
- 空间申请与释放 new, delete, new[ ] , delete[]
- 其他运算符 ()(函数调用),->(成员访问),,(逗号),
不可重载的运算符
- .:成员访问运算符
- ., ->:成员指针访问运算符
- :::域运算符
- sizeof:长度运算符
- ?::条件运算符
-
#: 预处理符号
注意: 1. 运算重载符不可以改变语法结构。 2. 运算重载符不可以改变操作数的个数。 3. 运算重载符不可以改变优先级。 4. 运算重载符不可以改变结合性。
完整代码
#include<iostream>
using namespace std;
class Cartesian {
public:
Cartesian(int xx = 0, int yy = 0) {
x = xx;
y = yy;
}//构造函数
int getX();
int getY();
void print();
Cartesian operator ++();//自加前置
Cartesian operator ++(int);//自加后置
Cartesian operator +(Cartesian &a);
Cartesian operator &(Cartesian &a);
Cartesian operator >>(int a);
private:
int x;
int y;
};
int Cartesian :: getX() {
return x;
}
int Cartesian::getY() {
return y;
}
void Cartesian:: print() {
cout << "(" << getX() << "," << getY() << ")";
}
Cartesian Cartesian::operator++() {
return *this;
x++;
y++;
}
Cartesian Cartesian::operator++(int) {
Cartesian temp(*this);
x++;
y++;
return temp;
}
Cartesian Cartesian::operator +(Cartesian &a) {
x += a.x;
y += a.y;
return Cartesian(x, y);
}
Cartesian Cartesian::operator &(Cartesian &a) {
x &= a.x;
y &= a.y;
return Cartesian(x,y);
}
Cartesian Cartesian:: operator >>(int a){
x = x >> a;
y = y >> a;
return Cartesian(x, y);
}
void displayMenu() {
cout << endl<<"*********************" << endl;
cout << " 1.(x,y)++" << endl;
cout << " 2.++(x,y)" << endl;
cout << " 3.(x1,y1)&(x2,y2)" << endl;
cout << " 4.(x,y)>>a" << endl;
cout << " 0.exit"<<endl;
cout << "*********************" << endl;
cout << endl;
}//显示主菜单
int main() {
int x1, y1, x2, y2, flag=1,a;
cout<< "请输入一个坐标:";
cin >> x1>>y1;
Cartesian c1(x1, y1);
cout << "请输入另一个点:";
cin >> x2 >> y2;
Cartesian c2(x2, y2);
displayMenu();
cin >> flag;
switch (flag) {
case 1:
c1.print();
cout << "++==";
c1++;
c1.print();
break;
case 2:
cout << "++";
c1.print();
++c1;
cout << "==";
c1.print();
break;
case 3:
c1.print();
cout << "&";
c2.print();
cout << "==";
c1.operator&(c2);
c1.print();
break;
case 4:
cout << "请输入右移数";
cin >> a;
cout << ">>" << a << "==";
c1.operator>>(a);
c1.print();
break;
case 0:
exit;
break;
default:
cout << "输入错误,请重新输入";
break;
}
return 0;
}