// 重载运算符: 其函数名由operator关键字和定义的运算符号组成
// 其参数数量与该运算符作用的运算对象数量一致
// 对于二元运算符来说,左侧运算对象传递给第一个参数,右侧运算对象传递给第二个参数
// 除 operator() 外,其他重载运算符都不能含有默认实参
// 如果该函数是一个成员函数,则其第一个运算对象绑定到隐式的 this 指针上
// 不能被重载的运算符:
// 域运算符 ::
// 指向成员选择的指针 .*
// 成员选择符 .
// 条件运算符 ?:
// 不应该重载的运算符:可能无法保留运算顺序和短路求值属性
// 逻辑与 &&
// 逻辑或 ||
// 逗号 ,
// 取地址 &
// 必须定义为成员函数:
// 赋值 =
// 下标 []
// 调用 ()
// 成员访问箭头 ->
// 应该定义为成员函数:对象状态的运算符或与给定类型密切相关的运算符
// 符合赋值
// 递增/碱
// 应该定义为普通成员函数:具有对称性的运算符可能转换任意一端的运算对象
// 算术
// 相等性
// 关系
// 位运算
// 重载输出运算符
// 参数一:非常量 ostream 对象的引用
// 参数二:常量的引用
// 因为第一个参数必须是 ostream 对象,所以非成员函数,
// 因为需要输出对象成员,所以友元
// 重载输入运算符
// 参数一:ostream 对象的引用
// 参数二:非常量的引用
// 因为第一个参数必须是 iostream 对象,所以非成员函数,
// 因为需要输出对象非公有成员,所以友元
// 输入运算需要检查错误,并从错误中恢复
// 算术运算符
// 非成员以允许左右两侧的运算对象互换
// 如果定义了复合运算,通常使用复合运算实现
// 相等运算符
// 通常也定义不相等,实际工作由其中一个实现,另一个调用真正实现
// 关系运算符
// 定义时,遵循容器关键字的要求
// 赋值运算符
// 必须为成员函数,必须返回左侧对象的引用
// 拷贝赋值
// 移动赋值
// 针对其它类型的右侧对象的赋值
// 下标运算符
// 容器类需要通过位置访问时
// 返回普通引用
// 返回常量对象的常量引用
// 递增递减运算符
// 通常改变对象状态,建议成员函数
// 前置版本 operator++() 返回引用,改变状态前,需要检查有效性
// 后置版本 operator++(int) 返回原值,使用前置版本实现,int 参数仅用于区别2个版本,并不参与运算
// 成员访问运算符
// -> 必须是成员, 必须返回类的指针或自定义了 ->运算的某个类的对象
// *一般也是,并且通常都为 const 函数
// 函数调用运算符
// 定义了此运算符的类对象成为函数对象(function object) 如 lambda
// 必须成员函数,若定义多个调用运算,需在参数个数和类型上有所区别
// lambda 是函数对象
// 编译器将 lambda 翻译成未命名类的未命名对象,在该类中产生一个重载的函数调用运算符
// eg.
vector words;
stable_sort(words.begin(), words.end(), [](const string &a, const string &b){return a.size() < b.size();})
// 类似类:
class Shortstring{
public:
bool operator()(const string &a, const string &b) const {
return a.size() < b.size();
}
};
// 使用该类重写:
stable_sort(words.begin(), words.end(), Shortstring())
// 对于通过引用捕获变量时,由程序确认引用对象的存在,无须再产生的类中将其存储为数据成员
// 对于值捕获时,必须为其建立成员变量,同时构建构造函数,使用捕获的值来初始化数据成员
// eg.
vector words;
size_t sz = 0;
cin >> sz;
auto wc = find_if(words.begin(), words.end(), [sz](const string &a){return a.size() >= sz;})
// 类似类:
class Sizecomp{
public:
Sizecomp(size_t n):sz(n){}
bool operator(const string &a) const {
return a.size() >= sz;
}
private:
size_t sz;
};
// 使用该类重写:
auto wc = find_if(words.begin(), words.end(), Sizecomp(sz));
// 标准库函数对象 定义于 functional.h
// 算术运算符
// plus
// minus
// multiplies
// divides
// modulus
// negate
// 关系运算符
// equal_to
// not_equal_to
// greater
// greater_equal
// less
// less_equal
// 逻辑运算符
// logical_and
// logical_or
// logical_not
// 可调用对象:
// 函数,
// 函数指针,
// lambda,
// bind创建的对象,
// 重载的函数调用运算符
// 函数表(function table),储存指向可调用对象的指针,用 map 实现
//加法 普通函数
int add(int a, int b){
return a + b;
}
//求余 lambda
auto mod = [](int a, int b){return a % b;};
//除法 函数对象类
class divide{
public:
int operator()(int a, int b){
return a / b;
}
};
//函数表
map< string, int(*)(int, int)> binops;
//普通函数直接插入表中
binops.insert({"+", add});
//对于 lambda 和 类,明显不同于表需要的值类型
// function 模板类, 定义于 functional.h
function f1 = add;
function f2 = [](int a, int b){return a % b;};
function f3 = divide();
//改写函数表类型
map> binops{
{"+", add},
{"-", std::minus()},
{"*", [](int a, int b){return a * b;}},
{"/", divide()},
{"%", mod}
};
//调用
binops["+"](10,5);
binops["-"](10,5);
binops["*"](10,5);
binops["/"](10,5);
binops["%"](10,5);
//使用 function 时,注意重载函数的二义性
// 类的类型转换
// 特殊的成员函数
// operator type() const;
// type 表示某种类型,只要该类型能作为函数的返回类型.因此不能是数组或函数,可以是指针或引用
// 没有显式的返回类型,也没有形参,通常不改变转换对象的内容,一般为 const
class SmallInt{
public:
SmallInt( int i = 0):val(i){
if(i < 0 || i > 255)
throw std::out_of_range("wrong smallint value!");
}
operator int() const {
return val;
}
private:
std::size_t val;
}
SmallInt si;
si = 1; //1 转换为 SmallInt 类型,然后operator=
si + 3; //si 转换为 int
si + 3.14; //si 转换为 int,再转换为 double
如果 istream::cin 定义了 operator bool() 会发生什么?
int n = 5;
cin << n; //合法,cin 隐式转换为 bool,然后再提升为 int, 然后位运算左移 n 位.
// 为了避免上面的问题,c++11 引入显式类型转换运算符
class SmallInt{
public:
SmallInt( int i = 0):val(i){
if(i < 0 || i > 255)
throw std::out_of_range("wrong smallint value!");
}
explicit operator int() const {
return val;
}
private:
std::size_t val;
}
si = 1; //1 通过构造转换为 SmallInt 类型,然后operator=
si + 3; //错误,禁止隐式转换
static_cast(si) + 3; //显式请求转换
// 表达式用于条件时,显式的转换会自动执行
// 即 if(si + 3) 正确,
// 在 while, if, for, !, ||, &&, ?: 条件部分都会自动发生
// 避免二义性,
// 如:A类定义了B类型参数的构造,B类定义了A类型的转换运算符
// 又如:定义了2个转换对象都为算术类型的转换运算符