重载运算符可以完成对象和对象之间的运算,同样也可以通过重载运算符运算实现对象和普通类型数据的运算。
(1)重载运算符举例
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
就以重载+运算符为例:
class Rectangle
{
public:
void setWidth(double width)
{
mWidth = width;
}
void setHeight(double height)
{
mHeight = height;
}
double getArea()
{
return mWidth * mHeight;
}
// 重载 + 运算符,用于把两个 Rectangle 对象相加
Rectangle operator+(const Rectangle& rect)
{
Rectangle rectangle;
rectangle.setWidth(mWidth + rect.mWidth);
rectangle.setHeight(mHeight + rect.mHeight);
return rectangle;
}
private:
double mWidth; // 宽度
double mHeight; // 长度
};
创建一个 Rectangle 类,Rectangle 类有4个函数
setWidth:设置矩形的宽度
setHeight:设置矩形的长度
getArea:设置矩形的面积
operator+:实现+运算符重载,两个Rectangle对象相加
调用方式如下:
int main()
{
Rectangle* rect1 = new Rectangle();
rect1->setWidth(10);
rect1->setHeight(10);
cout << "矩形的面积:" << rect1->getArea() << endl;
Rectangle* rect2 = new Rectangle();
rect2->setWidth(20);
rect2->setHeight(20);
cout << "矩形的面积:" << rect2->getArea() << endl;
Rectangle rect3 = *rect1 + *rect2;
cout << "矩形的面积:" << rect3.getArea() << endl;
return 0;
}
重载运算符一般用于两个对象运算,还可以是对象和基本数据类型的计算,演示代码如下:
#include <iostream>
using namespace std;
class Rectangle
{
public:
void setWidth(double width)
{
mWidth = width;
}
void setHeight(double height)
{
mHeight = height;
}
double getArea()
{
return mWidth * mHeight;
}
// 重载 + 运算符,用于把两个 Rectangle 对象相加
Rectangle operator+(const int change)
{
Rectangle rectangle;
rectangle.setWidth(mWidth + change);
rectangle.setHeight(mHeight + change);
return rectangle;
}
private:
double mWidth; // 宽度
double mHeight; // 长度
};
int main()
{
Rectangle* rect1 = new Rectangle();
rect1->setWidth(10);
rect1->setHeight(10);
cout << "矩形的面积:" << rect1->getArea() << endl;
Rectangle rect2 = *rect1 + 10;
cout << "矩形的面积:" << rect2.getArea() << endl;
return 0;
}
(2)可重载运算符和不可重载运算符
下面是可重载的运算符列表:
不可重载运算符有:
运算符 | 运算符类型 |
---|---|
.: | 成员访问运算符 |
., ->: | 成员指针访问运算符 |
::: | 域运算符 |
sizeof: | 长度运算符 |
?:: | 条件运算符 |
#: | 预处理符号 |
(3)一元运算符的重载
常见的一元运算符有:自增加(++)、自减少(--)、逻辑非(!)
自增加(++)和自减少(--)差不多,只举一个例子,就以自增加(++)举例,自增加(++)分为前置和后置,operator++() 表示前置, operator++(int) 表示后置,演示代码如下:
class Card
{
public:
void setCardCount(int count) // 获取卡片数量
{
mCount = count;
}
int getCardCount() // 获取卡片数量
{
return mCount;
}
void operator++() // ++card,前置形式
{
++mCount;
}
void operator++(int) // ++card,后置形式
{
mCount++;
}
private:
int mCount; // 卡片的数量
};
int main()
{
Card card;
card.setCardCount(10);
cout << "已有卡片数量:" << card.getCardCount() << endl;
++card;
cout << "前置自增1后的卡片数量:" << card.getCardCount() << endl;
card++;
cout << "后置自增1后的卡片数量:" << card.getCardCount() << endl;
return 0;
}
逻辑非的实例代码如下:
class Card
{
public:
void setIsCard(bool isCard)
{
this->isCard = isCard;
}
bool operator!()
{
return !isCard;
}
private:
bool isCard; // 是否是卡片
};
int main()
{
Card* card = new Card();
card->setIsCard(false);
if (!(*card)) {
cout << "this is a card" << endl;
}
else
{
cout << "this is not a card" << endl;
}
return 0;
}
(4)二元运算符的重载
常见的二元运算符就是 加(+)、减(-)、乘(*)、除(/)
文章一开始就有一个+运算符呀的例子,其它三个运算符和这个例子是一样的,所以就不重复举例了。
(5)关系运算符的重载
常见的关系运算符有:大于(>)、小于(<)、等于(==)、大于等于(>=)、小于等于(<=)。
就以等于为例:
class Line
{
public:
void setLength(int length)
{
this->length = length;
}
bool operator==(const Line line)
{
return length == line.length;
}
private:
int length; // 线条的长度
};
int main()
{
Line* line1 = new Line();
line1->setLength(10);
Line* line2 = new Line();
line2->setLength(10);
if (*line1 == *line2)
{
cout << "line1 == line2" << endl;
}
else
{
cout << "line1 != line2" << endl;
}
return 0;
}
(6)<< 和 >> 运算符
在 C++ 中,标准库本身已经对左移运算符 <<
和右移运算符 >>
分别进行了重载,使其能够用于不同数据的输入输出,但是输入输出的对象只能是 C++ 内置的数据类型(例如 bool、int、double 等)和标准库所包含的类类型(例如 string、complex、ofstream、ifstream 等)。
如果我们自己定义了一种新的数据类型,需要用输入输出运算符去处理,那么就必须对它们进行重载,代码如下:
class Name
{
public:
void setName(string name)
{
this->name = name;
}
string getName()
{
return name;
}
friend istream& operator>>(istream& intput, Name& n)
{
intput >> n.name;
return intput;
}
friend ostream& operator<<(ostream& output, const Name& n)
{
output << n.name;
return output;
}
private:
string name;
};
int main()
{
cout << "please input name: "<< endl;
Name* name = new Name();
cin >> *name;
cout << "my name is " << *name << endl;
return 0;
}
定义一个Name类,使用
Name* name = new Name();
对Name建立实例,直接输入或者输出对象。
在Name类中,对 << 和 >> 进行重载可以实现直接输出对象。
在Name类中重载运算符 >>:
friend istream& operator>>(istream& intput, Name& n)
{
intput >> n.name;
return intput;
}
由于operator>>的形式参数个数大于一个,所以operator>>必须以全局的函数存在(非成员函数),即需要在函数前面添加关键字 friend
。
另外,这个运算符主要是输入使用的,Name的name变量是要被赋值的,所以形式参数 n 不能是常量。
在Name类中重载运算符 <<:
friend ostream& operator<<(ostream& output, const Name& n)
{
output << n.name;
return output;
}
当前形式参数的 n 设置为常量,因为此函数的目的是将 n 输出,所以 n 不会被赋值(n 不会成为左值)。
和 operator>> 函数一样,operator<<函数的形式参数也是2个,所以必须将 operator<< 函数作为全局函数。
(7)赋值运算符的重载
演示代码如下:
class DengOp
{
public:
void setParam(int param)
{
this->param = param;
}
int getParam()
{
return param;
}
void operator= (const DengOp& op)
{
param = op.param * 2;
}
private:
int param;
};
int main()
{
DengOp dOp, dOpBak;
dOp.setParam(10);
dOpBak = dOp;
cout << dOpBak.getParam() << endl;
return 0;
}
以上代码是同类型的对象相互赋值,不过已经将 param 值扩大的2倍。
其中,operator= 不能做为全局函数,也就是说,此函数前面不支持添加 friend 关键字,且 operator= 的形式参数的数量只能是1。
operator= 也可以做成不同类型对象相互赋值,代码如下:
class DengOp2
{
public:
void setParam(int param)
{
this->param = param;
}
int getParam()
{
return param;
}
private:
int param;
};
class DengOp1
{
public:
void setParam(int param)
{
this->param = param;
}
int getParam()
{
return param;
}
void operator= (DengOp2& op2)
{
param = op2.getParam();
}
private:
int param;
};
以上两个类分别是:DengOp1 和 DengOp2
DengOp1 有 operator= 函数,DengOp2 中没有,调用代码如下:
int main()
{
DengOp1 dOp1;
dOp1.setParam(10);
DengOp2 dOp2;
dOp2.setParam(20);
dOp1 = dOp2;
cout << dOp1.getParam() << endl;
return 0;
}
打印结果是:20。
注意,不可以将 dOp1 赋值给 dOp2,如:
dOp2 = dOp1;
因为 DengOp2 中没有对赋值运算符重载。
(8)类型强制转换运算符的重载
类型强制转换运算符
又称 函数调用运算符
,类型强制转换运算符就是 ()
,它的重载有两种玩法。
【第一种玩法】
class D
{
public:
void setA(int a)
{
mA = a;
}
int getA()
{
return mA;
}
D operator()(int a)
{
D d;
d.setA(a);
return d;
}
private:
int mA;
};
int main()
{
D d1, d2;
d1.setA(10);
d2 = d1(20);
cout << d2.getA() << endl;
return 0;
}
【第二种玩法】
class D
{
public:
void setA(int a)
{
mA = a;
}
int getA()
{
return mA;
}
operator double()
{
return mA;
}
private:
int mA;
};
int main()
{
D d;
d.setA(10);
double n = (double)d;
cout << n << endl;
return 0;
}
(9)下标运算符 [] 重载
下标操作符 [] 通常用于访问数组元素。重载该运算符用于增强操作 C++ 数组的功能。
演示代码如下:
class D
{
public:
void setArr()
{
for (size_t i = 0; i < 10; i++)
{
arr[i] = i;
}
}
int& operator[](int i)
{
if (i >= 10) // 如果索引大于10,则返回第一个元素
{
return arr[0];
}
return arr[i];
}
private:
int arr[10];
};
int main()
{
D d;
d.setArr();
cout << d[2] << endl;
cout << d[5] << endl;
cout << d[12] << endl;
return 0;
}
(10)总结
还有一些运算符也可以实现重载,这里就不一一举例了。
在 C++ 中进行运算符重载时,有以下问题需要注意:
- 重载后运算符的含义应该符合原有用法习惯。例如重载
+
运算符,完成的功能就应该类似于做加法,在重载的+
运算符中做减法是不合适的。此外,重载应尽量保留运算符原有的特性。 - C++ 规定,运算符重载不改变运算符的优先级。
- 以下运算符不能被重载:
.
、.*
、::
、? :
、sizeof
。 - 重载运算符
()
、[]
、->
、或者赋值运算符=
时,只能将它们重载为成员函数,不能重载为全局函数。
运算符重载的实质是将运算符重载为一个函数,使用运算符的表达式就被解释为对重载函数的调用。
运算符可以重载为全局(friend)函数。此时函数的参数个数就是运算符的操作数个数,运算符的操作数就成为函数的实参。
运算符也可以重载为成员函数。此时函数的参数个数就是运算符的操作数个数减一,运算符的操作数有一个成为函数作用的对象,其余的成为函数的实参。
必要时需要重载赋值运算符=,以避免两个对象内部的指针指向同一片存储空间。
运算符可以重载为全局函数,然后声明为类的友元。
<< 和 >> 是在 iostream 中被重载,才成为所谓的“流插入运算符”和“流提取运算符”的。
类型的名字可以作为强制类型转换运算符,也可以被重载为类的成员函数。它能使得对象被自动转换为某种类型。
自增、自减运算符各有两种重载方式,用于区别前置用法和后置用法。
运算符重载不改变运算符的优先级。重载运算符时,应该尽量保留运算符原本的特性。
[本章完...]
网友评论