Effective STL 章节(七)
——50条有效使用STL的经验(42/50)
函数子、函数子类、函数及其他
文章目录
第三十八条、遵循按值传递的原则来设计函数子类
考虑的是函数嵌套的问题,将a函数作为参数传给b函数是不可行的,只能传递指针
而这样实际上是函数间以值进行传递,所以就需要这个a函数满足:
1、函数要小,否则传递的代价太大了
2、函数需要是单态的,虚函数的存在容易丢失类的元素信息
第三十九条、确保判别式是“纯函数”
判别式:以bool为返回值的函数
纯函数:返回值仅依赖参数的函数
这一条没什么好解释的,如果判别式的返回值不仅仅依赖输入参数,那判别式还有什么意义
第四十条、若一个类是函数子,则应使它可配接
什么是函数子?
函数子(也称为仿函数,functor)是一个表现得像函数的对象。它是一个重载了 operator() 操作符的类的实例。通过重载 operator(),你可以像调用函数一样使用这个类的实例。
举例:
class Add {
public:
int operator()(int a, int b) const {
return a + b;
}
};
int main() {
Add add;
int result = add(2, 3); // 像调用函数一样调用对象
return 0;
}
这里Add就是函数子,看着像函数,但定义上是类
怎么定义可配接?
可配接是指一个函数子能够与标准库中的配接器(如 std::bind, std::function, std::mem_fn 等)一起使用,或者能够适应各种算法和高阶函数的要求。
也就是说需要这个类能够和配接器一起使用,具体做法就是为类构建这些函数需要的内容
result_type:表示返回值类型。
argument_type 或 first_argument_type 和 second_argument_type:表示参数类型。
函数子如何变成可配接的?
继承自类unary_function、binary_function
第四十一条、理解ptr_fun、mem_fun和mem_fun_ref的来由
这几个配接器设计的初衷是为了保证函数调用不出错
f(x); // 语法1:f是一个非成员函数
x.f();// 语法2:f时成员函数,x是一个对象或对象的引用
p->f(); // 语法3:f是成员函数,p是一个对象指针
有的函数只能接受语法1,但是我们只有语法2的x,就需要这些函数去转换
但是C++ 11后有了lambda表达式、std::bind等函数,所以这几个配接器已经很少用了
第四十二条、确保less<T>与operator<具有相同的语义
举例:有一个类定义如下
class Widget
{
public:
...
size_t weight() const;
size_t maSpeed() const;
...
bool operator < (const Widget& lhs, const Widget& rhs)
{
return lhs.weight() < rhs.weight();
}
};
这里<表达的就是小于,那么再考虑一个问题,即multiset<Widget>
,其默认比较函数是less<Widget>
通常情况下,less会调用operator<,此时两者基本上一样
但是如果为了某些条件,如这里以速度来排序,那么显而易见的方法是修改less函数:
template<>
struct std::less<Widget> : public std::binary_function<Widget, Widget, bool>
{
bool operator () (const Widget& lhs, const Widget& rhs) const
{
return lhs.maxSpeed() < rhs.maxSpeed();
}
}
但是这样容易误导其他程序员,因为他们不一定知道你对less进行了特化
为了不引起误会,最好是在简历multiset时就传入判断方式,使得代码清晰易懂
对less的特化,最好是不做,少做,确保它和operator<的语义一样,防止误会