使用operator关键字引入重载函数
#include <iostream>
#include <string>
#include <vector>
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 <iostream>
#include <string>
#include <vector>
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 中规定了相应的求值顺序但没有方式实现短路逻辑
- 不可重载的运算符(如 ? :运算符)
运算符重载——详述1
对称运算符通常定义为非成员函数以支持首个操作数的类型转换
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Str
{
Str(int x)
: val(x)
{}
/* auto operator + (Str input)
{
return Str(val + input.val);
} */
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 <iostream>
#include <string>
#include <vector>
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 <iostream>
#include <string>
#include <vector>
#include <string>
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<int>(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 <iostream>
#include <string>
#include <vector>
#include <string>
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<int>(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 <iostream>
#include <string>
#include <vector>
#include <string>
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<int>(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 <iostream>
#include <string>
#include <vector>
#include <string>
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 <iostream>
#include <string>
#include <vector>
#include <string>
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 合法的函数调用
}
运算符重载——详述2
类型转换运算符
类型转换运算符,把一种类型转换成另外一种类型。
#include <iostream>
#include <string>
#include <vector>
#include <string>
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 <iostream>
#include <string>
#include <vector>
#include <string>
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 的特殊性:用于条件表达式时会进行隐式类型转换
#include <iostream>
#include <string>
#include <vector>
#include <string>
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;
}
}
C++ 20 中对 == 与 <=> 的重载
#include <iostream>
#include <string>
#include <vector>
#include <string>
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