这一篇介绍C++中非常重要的一部分知识——运算符重载
如有侵权,请联系删除,如有错误,欢迎大家指正,谢谢
运算符重载
- 运算符重载的作用是赋予运算符自定义功能,如 ‘ + ’ 号可以实现 ‘常数+常数’ 等常规的加法运算,但是对于 ‘对象+对象’ 、‘对象+常数’ 、‘常数+对象’ 等操作在默认情况下不被允许,但是可以通过重载 ‘ + ’ 实现这些运算
- 形式为:返回值类型 operator运算符(参数列表);
- 参数列表中必须有一个类类型的参数(最好是引用,避免进行拷贝构造)
- 在类内进行运算符重载,参数列表中默认左边有一个所在类类型的参数,这个也比较好理解,类中的非静态成员函数左边都默认有一个所在类类型的参数(this指针,上一篇有介绍),运算符重载是一种特殊的函数,又是在类内进行重载的,所以它也有一个所在类类型的参数(this指针)
- 不是所有的运算符都支持重载,支持重载的运算符又分为支持类内和类外重载(有些运算符既可以在类内重载也可以在类外重载,但有些运算符只可以在类内(类外)重载)
重载规则
《C++ Primer(第5版)》中给出的可重载和不可重载运算符
- 赋值" = "、下标" [ ] "、调用"( )"和成员访问"->"运算符必须是成员
- 复合赋值运算符(如:+= 等)一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同
- 改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用运算符,通常应该是成员
- 具有对称性的运算符可能转换为任一端的运算对象,例如算术、相等性、关系和位运算符等,因此它们通常应该是普通的非成员函数
一般运算符重载
#include <iostream>
using namespace std;
class Test {
public:
Test(int n) : m(n) {
}
// ========= 类内和类外都可以重载,并且可以同时存在 ========
// 类内重载"+(加)"运算符(类内类外可以同时存在)
// "-(减)"、"*(乘)"、"/(除)"、"%(取余)"、"^(异或)"、"&(按位与)"、"|(按位或)"、
// ">(大于)"、"<(小于)"、">=(大于等于)"、"<=(小于等于)"、"==(等于)"、"!=(不等于)"
// "&&(与)"、"||(或)"与"+(加)"、
// """"类似
Test* operator+(int n) {
Test* tmp = new Test{ *this };
tmp->m += n;
cout << tmp->m << endl;
return tmp;
}
// ======== 类内类外都可以重载,不允许同时存在 ========
// 类内重载"~(按位取反)"运算符(类内类外不能同时存在)
// "+(正)"、"-(负)"、"!(非)"、"%(取余)"、"^(异或)"、"&(按位与)"、"|(按位或)"
// "+=(加等于)"、"-=(减等于)"、"*=(乘等于)"、"/=(除等于)"、"%=(取余等于)"、"^=(异或等于)"、"&=(按位与等于)"、"|=(按位或等于)"
// "<<=(左移等于)"、">>=(右移等于)"与"~(按位取反)"类似
Test& operator~() {
this->m = ~this->m;
cout << this->m;
return *this;
}
// ======== 只允许类内重载 ========
// 类内重载"=(赋值)"运算符,只允许类内重载(这是拷贝赋值)
// "[](下标运算符)"、"()(函数调用运算符,STL中的仿函数便是通过类内重载"()"实现的)"、"->(成员访问运算符)"
Test& operator=(const Test& test) {
this->m = test.m;
return *this;
}
// "new"、"new[]"、"new()"、"delete"、"delete[]" 详细的重载方式在《C/C++内存申请和释放(二)》
friend Test* operator+(const Test& test, int n); // 将类外重载"+(加)"运算符的函数声明为友元函数
//friend Test& operator~(Test& test); // 将类外重载"~(按位取反)"运算符的函数声明为友元函数
private:
int m;
};
// 类外重载"+(加)"运算符("-(减)"、"*(乘)"、"/(除)"、"%(取余)"、"^(异或)"与"+(加)"类似)
Test* operator+(const Test& test, int n) {
Test* tmp = new Test{ test };
tmp->m += n;
cout << tmp->m << endl;
return tmp;
}
//Test& operator~(Test& test) {
// test.m = ~test.m;
// cout << (test.m) << endl;
// return test;
//}
int main() {
Test test(2);
Test* t1 = test + 2; // 调用重载的"+"
delete t1;
Test& t2 = ~test; // 调用重载的"~"
Test t3(3);
t3 = test; // 调用重载的"=",传说中的拷贝赋值
system("pause");
return 0;
}
特殊运算符重载
- cout 是 class ostream 的对象,cin 是 class istream 的对象,这两个只能在类外重载,因为 << 作用在左边的 cout 上,>> 作用在左边的 cin 上,类内重载函数默认最左边有一个参数是当前类的指针,将 cout 和 cin 传入类内重载的函数会发生冲突
class Test {
public:
int n;
Test(int n)
: n(n) {
}
};
ostream& operator<<(ostream& os, const Test& test) {
return os << test.n;
}
Test test(2);
cout << test << endl;
- 重载++(自加)、--(自减)运算符,类内重载和类外重载不允许同时存在
class Test {
public:
Test(int n)
: n(n) {
}
// 前置++类内重载
Test& operator++() {
++this->n;
cout << this->n << endl;
return *this;
}
// 后置++类内重载
Test& operator++(int flag) {
this->n++;
cout << this->n << endl;
return *this;
}
friend Test& operator++(Test& test);
friend Test& operator++(Test& test, int flag);
private:
int n;
};
// 前置++类外重载
//Test& operator++(Test& test) {
// ++test.n;
// cout << test.n << endl;
// return test;
//}
// 后置++类外重载
//Test& operator++(Test& test, int flag) {
// test.n++;
// cout << test.n << endl;
// return test;
//}
Test test(2);
++test; // 前置++
test++; // 后置++
转换函数(类型转换的重载)
- C++ 提供了两种强制类型转换的方式,一种是 (int)a;另一种是 int(a),C 语言只提供一种 (int)a
- 转换函数——没有显示返回类型,但是要写返回值(返回值类型就是要转换成的类型);必须定义成类的非静态成员函数,没有参数(有一个默认所在类的参数(this指针));不应该改变对象的内容,所以一般是const函数
class Test {
public:
Test(int n, double d)
: n(n), d(d) {
}
operator int() const {
return this->n;
}
operator double() const {
return this->d;
}
private:
int n;
double d;
};
Test test(2, 3.4);
cout << int(test) << " " << (double)test << endl;
如果未特殊说明,以上测试均是在win10 vs2017 64bit编译器下进行的