操作重载

所谓的操作重载实际上就是我们在自己创建类的时候,需要定义一些操作符的操作,比如说+ - < > = !=等等。在使用C++的标准库的时候,比如说string,我们习以为常的认为+就是连接,==就是比较,然而这些操作真正做什么是类中的操作符函数定义的,因此我们自己设计类的时候,也要对这些操作符进行重载,来达到我们的目的。

在c++中,所有的操作符都是由operator和其后要定义的运算符号组成。

输入输出运算符

对于输入输出运算符,它们必须是非成员函数。因为如果它们是成员函数,那么它们的左侧对象必然是类的一个对象。这样我们必须每次使用cout就要如下所示:

1
2
SalesData data;
data << cout;

而如果是非成员函数,我们就可以很自然的写成这种格式

1
cout << data;

同时如果<<成员函数,那么它也必须是ostream的成员函数,如下所示,那么我们就需要修改标准库ostream,这是非法的。

一个输出运算符的重载例子

1
2
3
4
ostream& operator<<(ostream& os, const SalesData& data) {
os << data.isbn() << " " << data.price();
return os;
}

第一个参数是ostream的引用,因为我们需要将内容写到os中,而返回值就是行参ostream

输入运算符的重载例子

1
2
3
4
5
6
7
8
9
10
istream& operator>>(istream& is, SalesData& data) {
double price;
is >> price;
if (is) {
data.revenue = price
} else {
data.revenue = 0.0;
}
return is;
}

算术运算符

算数运算符通常会计算两个对象并得到一个新的值,通常返回值是一个局部变量的副本。

operator+

operator+一般会使用复合赋值运算符operator+=来实现

1
2
3
4
5
SalesData operator+(const SalesData& lhs, const SalesData& rhs) {
SalesData sum = lhs;
sum += rhs;
return sum;
}

关系运算符

关系运算符一般有< > <= >= == !=这几种

operator==

1
2
3
bool operator==(const SalesData& lhs, const SalesData& rhs) {
return lhs.isbn() != rhs.isbn() && lhs.revenue == lhs.revenue;
}

operator!=

operator!=一般由operator==实现

1
2
bool operator!=(const SalesData& lhs, const SalesData& rhs) {
return !(lhs == rhs);

赋值运算符

1
2
3
4
SalesData& SalesData::operator=(const SalesData& rhs) {
price = rhs.price;
return *this;
}

复合赋值运算符

复合赋值运算符做好定义为类的成员函数,这样我们可以充分的使用this指针来实现

1
2
3
4
SalesData& SalesData::operator+=(const SalesData& rhs) {
price += rhs.price;
return *this;
}

下标运算符

下标运算符一般定义为operator[],而且一般而言,会定义下标运算符的const以及非const版本

1
2
3
4
5
6
7
class StrVec {
public:
string& operator[](size_t n) {return elements[n];}
const string& operator[](size_t n) const {return elements[n];}
private:
string* elements;
}

递增递减运算符

递增递减运算符会比较特殊,因为它们有前置和后置两种方式

首先看前置的

1
2
3
4
5
6
7
8
9
10
11
12
13
class StrBlobPtr {
public:
StrBlobPtr& operator++() {
++curr;
return *this;
}
StrBlobPtr& operator--() {
--curr;
return *this;
}
private:
int curr;
}

而后置为了与前置区分,特别的增加了一个不被使用的int形参,并且利用了前置的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class StrBlobPtr {
public:
StrBlobPtr operator++(int) {
StrBlobPtr ret = *this;
++*this;
return ret;
}
StrBlobPtr operator--(int) {
StrBlobPtr ret = *this;
--*this;
return ret;
}
private:
int curr;
}

因此在性能的场景下,也是推荐前置的方式。

成员访问运算符

成员访问运算符主要是指*和->

1
2
3
4
5
6
7
8
9
10
class StrBlobPtr {
public:
string& operator*() const {
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
string* operator->() const {
return & (this->operator*());
}
}

函数调用运算符

1
2
3
4
5
struct absInt {
int operator()(int val) const {
return val < 0 > -val : val;
}
}
显示 Gitment 评论