- 使用operator关键字引入重载函数
- 运算符的进一步划分
- 运算符重载——详述1
- 运算符重载——详述2
- 类型转换运算符
- C++ 20 中对 == 与 <=> 的重载
#include#include #include using namespace std; struct Str { int val = 3; }; struct Str2 { }; Str Add(Str x, Str y) { Str z; z.val = x.val + y.val; return z; } auto operator + (Str x, Str y) { Str z; z.val = x.val + y.val; return z; } auto operator + (Str2 x, Str2 y) // 函数名称相同,参数类型不同,本质是函数重载 { } int main() { Str x; Str y; // Str z = Add(x, y); // x + y 更简洁 Str z = x + y; // 运算符重载 cout << z.val << endl; }
重载不能发明新的运算,重载的一定是已经存在的运算。
不能改变运算的优先级与结合性。虽然我们可以定义运算的具体行为,但是不改变优先级和结合性。
通常不改变运算含义,比如本来是加号,不要内部改为-的含义。这样对使用者不友好。
函数参数个数与运算操作数个数相同,至少一个为类类型
除 operator() 外其它运算符不能有缺省参数,因为()里面对于个数没有要求。
可以选择实现为成员函数与非成员函数
通常来说,实现为成员函数会以 *this 作为第一个操作数(注意 == 与 <=> 的重载)
#include运算符的进一步划分#include #include using namespace std; struct Str { int val = 3; auto operator () (int y = 100) // 可以是缺省参数 { return val + y; } auto operator + (Str x) // { Str res; res.val = val + x.val; return res; } }; int main() { Str x; cout << x(5) << endl; // 8 Str a; Str b; b.val = 10; Str c = a + b; // 成员函数+重载,a对应缺省的this*, b对应x cout << c.val << endl; // 13 }
根据重载特性,可以将运算符进一步划分
上面的运算符都是可以重载的。
- 可重载且必须实现为成员函数的运算符( =,[],(),-> 与转型运算符)
- 可重载且可以实现为非成员函数的运算符
- 可重载但不建议重载的运算符( &&, ||, 逗号运算符)C++17 中规定了相应的求值顺序但没有方式实现短路逻辑
- 不可重载的运算符(如 ? :运算符)
对称运算符通常定义为非成员函数以支持首个操作数的类型转换
#include#include #include using namespace std; struct Str { Str(int x) : val(x) {} int val; }; // 这样就可以考虑 4+x 了 auto operator + (Str input1, Str input2) { return Str(input1.val + input2.val); } int main() { Str x = 3; Str y = 4; Str z1 = x + y; // 可以 Str z2 = x + 4; // 可以 Str z3 = 4 + x; // 不可以,因为不会调用重载的成员+函数,因为4不是Str类型,和this不匹配 // 这就是为什么要把对称的操作符重载改成非成员函数 // cout << z.val << endl; }
移位运算符一定要定义为非成员函数,因为其首个操作数类型为流类型
#include#include #include using namespace std; struct Str { Str(int x) : val(x) {} friend auto& operator << (std::ostream& ostr, Str input) { ostr << input.val; return ostr; } private: int val; }; int main() { Str x = 3; Str y = 4; cout << x << y; }
赋值运算符也可以接收一般参数
#include#include #include #include using namespace std; struct Str { Str(int x) : val(x) {} // 拷贝赋值运算符 Str& operator = (const Str& input) { val = input.val; return *this; } // 赋值运算符也可以接收一般参数 Str& operator = (const std::string input) { val = static_cast (input.size()); return *this; } friend auto& operator << (std::ostream& ostr, Str input) { ostr << input.val; return ostr; } int val; }; int main() { Str x = 3; x = "12345"; // 赋值运算符 cout << x.val; }
operator [] 通常返回引用,来模拟数组的访问
#include#include #include #include using namespace std; struct Str { Str(int x) : val(x) {} // 拷贝赋值运算符 Str& operator = (const Str& input) { val = input.val; return *this; } // 赋值运算符也可以接收一般参数 Str& operator = (const std::string input) { val = static_cast (input.size()); return *this; } friend auto& operator << (std::ostream& ostr, Str input) { ostr << input.val; return ostr; } // 只有这里返回引用,[]这个操作符才能有类似数组写的操作 int& operator[] (int id) { return val; } // const [] 的重载,不要返回引用,因为const不让他修改 int operator[] (int id) const { return val; } int val; }; int main() { Str x = 3; x = "12345"; // 赋值运算符 cout << x[0] << endl; // 5 x[0] = 100; // 因为是返回int&,所以才能写操作 cout << x[0] << endl; // 100 const Str cx = 11; cout << cx[0] << endl; // 11 调用的重载的[],带const的。如果x是非const,就调用非const的 }
自增、自减运算符的前缀、后缀重载方法
怎么来判断是前缀自增++x或者x++后缀自增呢?
#include#include #include #include using namespace std; struct Str { Str(int x) :val(x) {} // 拷贝赋值运算符 Str& operator = (const Str& input) { val = input.val; return *this; } // 赋值运算符也可以接收一般参数 Str& operator = (const std::string input) { val = static_cast (input.size()); return *this; } // 前缀自增 Str& operator++ () { val += 100; return *this; } // 后缀自增 Str operator++ (int) { Str tmp(*this); // 拷贝构造副本 val += 10; return tmp; } int val; }; int main() { Str s(3); cout << (++s).val << endl; // 103 int y = s++.val; cout << y << endl; // 103 s的值先赋予y,再加10 cout << s.val << endl; // 113 }
能使用前缀自增自减,就使用前缀,不使用后缀的
使用解引用运算符( * )与成员访问运算符( -> )模拟指针行为
.运算符一般是用于成员对象的对象.x,对象.fun();
->一般是用于指针的,对象指针->x, 对象指针->fun();
.不能重载,->可以重载
#include#include #include #include using namespace std; struct Str { Str(int* p) : ptr(p) { } // 参数为空,隐式参数Str,一个就够了 // 这里为什么要写int型引用呢?因为这样可以对*ptr进行写操作 int& operator * () { return *ptr; } // ->成员访问操作符的重载 Str* operator -> () { return this; } int val = 5; private: int* ptr; }; int main() { int x = 100; Str ptr(&x); cout << ptr->val << endl; }
使用函数调用运算符构造可调用对象,接收参数个数不定
#include运算符重载——详述2 类型转换运算符#include #include #include using namespace std; struct Str { Str(int p) : val(p) {} // 第一个括号是函数调用运算符的重载,第二个是放参数 int operator() () { return val; } int operator() (int x, int y, int z) { return val + x + y + z; } private: int val; }; int main() { Str obj(100); // obj()很想一个函数调用,但obj本质是一个对象,可以使用函数调用的方式调用obj // 这样做的好处:没有lambada之前,可以构造函数的可调用对象 cout << obj() << endl; // 100 cout << obj(1, 2, 3) << endl; // 106 合法的函数调用 }
类型转换运算符,把一种类型转换成另外一种类型。
#include#include #include #include using namespace std; struct Str { Str(int p) : val(p) {} // 函数声明 没有显式的返回类型,返回类型已经在类型的名称那里体现了,int类型 // 对象本身不改变,加const operator int() const { return val; } private: int val; }; int main() { Str obj(100); int v = obj; // 对int变量的构造,等号右边也是int类型才行 cout << v << endl; }
与单参数构造函数一样,都引入了一种类型转换方式
注意避免引入歧义性与意料之外的行为
通过 explicit 引入显式类型转换
#include#include #include #include using namespace std; struct Str { explicit Str(int p) : val(p) {} // 如果这里加explicit,那么obj+3就不调用这个,因为obj无法隐式转换 operator int() const { return val; } friend auto operator + (Str a, Str b) { return Str(a.val + b.val); } private: int val; }; int main() { Str obj(100); // 3不能隐式转换,所以走不了3转成Str,只能走obj转int obj + 3; // 这里就会有歧义,不知道是ob转成了int与3相加,还是3转成obj,做+重载 }
explicit bool 的特殊性:用于条件表达式时会进行隐式类型转换
#includeC++ 20 中对 == 与 <=> 的重载#include #include #include using namespace std; struct Str { explicit Str(int p) : val(p) {} explicit operator bool() const { return (val == 0); } private: int val; }; int main() { Str obj(100); auto var = obj ? 1 : 0; cout << var << endl; if (obj) // 如果explicit返回的是bool值,并且对象明显用于条件表达式,那么可以隐式转换 { cout << 1 << endl; } else { cout << 0 << endl; } }
#include#include #include #include using namespace std; struct Str { explicit Str(int p) : val(p) {} friend bool operator == (Str obj, Str obj2) { return obj.val == obj2.val; } private: int val; }; int main() { Str obj(100); Str obj2(100); cout << (obj == obj2) << endl; cout << (obj != obj2) << endl; // c++20,只要定义了==,编译器可以自动推导!=,其他标准不行 }
注意 <=> 可返回的类型: strong_ordering, week_ordering, partial_ordering



