操作符重载
一般情况下, 有交换律的运算符应当定义为非成员函数, 在类外重载operator+(arg1, arg2)
, 又因为操作一般不改变原来的参数状态, 因此两个参数传入都应该是const&状态.
对于<<
运算符, 通常设置为非成员函数, 否则: data <<os
. 同时, 还要在类中把输出流加入友元, 以便读取私有变量
对于>>
运算符, 我们要考虑输出失败的情况, 通过检查输出流的状态即使处理异常.
对于==
和!=
两个操作符, 只定义其中一个操作符, 另一个委托操作.
对于下标运算符, 考虑返回const版本和非const版本两个重载函数
前置++和后置++(int)区分
*
和->
也可以重载, 这两个是一元运算符, 通常是"读取"的含义, 因此this指针应当设置为const类型. ->
运算符很有意思, 当作用于一个指针时, 含义是返回一个成员; 当作用于一个对象时, 返回一个对象并重复进行, 直到是一个指针.
函数调用运算符()
也可以重载, 那么一个对象也可以看做是一个函数.
一个<algorithm>的例子.
for_each(v.begin(), v.end(), myClass());
对于v中的每一个元素, 创建一个myClass的对象, 并隐性调用函数调用运算符(). 需要注意的是, 写出的()只是默认构造对象用.
至此, 我们可以发现, lambda语句其实就是一个重载了函数调用运算符的临时类. 回忆一下, ()
捕获参数, 那么就对应传入函数的参数. [ ]
捕获局部变量, 那么就对应类中的私有成员, 并在调用时初始化.
函数, lambda表达式, 重载()的类, 都是可以调用的, 如何将他们存储为统一格式, 方便之后在函数表的调用?
在<functional>头文件中, 定义了模板function<T>, 可以把可调用对象映射成函数. T是函数信息, 格式是返回值(参数,参数....)
, 后面的初始化值来自于各种可调用对象. 于是, 所有的对象都被映射成了function<T>类型.
function <int( int, int) > f1 = add;
此时add若既有对象, 又有函数, 如何区分二义性?
如果存的是函数, 那么应该改成存指针.
int (*fp)(int, int) = add;
<functional>还定义了标准算法用的模板, 比如 plus<T>, greater<T>. 既然自己实现对比很容易, 为什么标准库还要实现一次这些算法呢? 因为标准库的算法还可以比对指针, 比如容器中的元素的先后, 只有标准库的模板可以比较, 而直接比对指针是未定义的行为.
对象还可以重载为特定的类型, 使用operator type() const{ }
, 为了防止在不经意间被转换, 我们还可以在最前面加上explicit
关键字, 这样如果需要转换, 则需要显式地写出static_cast<type>
. 但是, 如果这个对象被用作在条件语句中, 则仍旧会自动转换. (回忆在cin中, 我们使用过if(cin)
语句判断是否输入成功)
如果一个类能转换成多个基本类型, 则容易产生二义性, 最好的办法是, 一个类最多只有一种转换成的基本类型.
对于运算符的二义性, 我们还要考虑进候选集有类的运算符重载候选集和非类的运算符候选
类的隐式构造举例
struct c{
c(int){};
}
void map(const c&);
map(0);//这个时候将使用0来初始化一个c
网友评论