如果类重载了函数调用运算符,则我们可以像使用函数一样使用该类的对象。因为这样的类同时也能存储状态,所以与普通函数相比它们
更加灵活。
举个简单的例子,下面这个名为 absInt 的 struct 含有一个调用运算符,该运算符负责返回其参数的绝对值:
struct absInt
{
int operator()(int val) const
{
return val < 0 ? -val : val;
}
}
这个类只定义了一种操作:函数调用运算符,它负责接受一个 int 类型的实参,然后返回该实参的绝对值。
我们使用调用运算符的方式是令一个 absInt 对象作用于一个实参列表,这一过程看起来非常像调用函数的过程:
int i = -42;
absInt absObj; //含有函数调用运算符的对象
int ui = absObj(i); //将传递给absObj.operator()
即使 absObj 只是一个对象而非函数,我们也能 “调用” 该对象。调用对象实际上是在运行重载的调用运算符。在此例中,该运算符接受一个 int 值并返回其绝对值。
函数调用运算符必须是成员函数。一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别。
如果类定义了调用运算符,则该类的对象称作函数对象(function object)。因为可以调用这种对象,所以我们说这些对象的 “行为像函
数一样”。
和其他类一样,函数对象类除了 operator()
之外也可以包含其他成员。函数对象类通常含有一些数据成员,这些成员被用于定制调用
运算符中的操作。
举个例子,我们将定义一个打印 string 实参内容的类。默认情况下,我们的类会将内容写入到 cout 中,每个 string 之间以空格隔开。同
时也允许类的用户提供其他可写入的流及其他分隔符。我们将该类定义如下:
class PrintString
{
public:
PrintString(ostream &o = cout, char c= ' '):
os(o), sep(c) { }
void operator()(const string &s) const { os << s << sep; }
private:
ostream &os; //用于写入的目的流
char sep; //用于将不同输出隔开的字符
};
我们的类有一个构造函数,它接受一个输出流的引用以及一个用于分隔的字符,这两个形参的默认实参分别是 cout 和 空格。之后的函数
调用运算符使用这些成员协助其打印给定的 string。
当定义 Printstring 的对象时,对于分隔符及输出流既可以使用默认值也可以提供我们自己的值:
PrintString printer; // 使用默认值,打印到 cout
printer(s); // 在cout中打印s,后面跟一个空格
PrintString errors(cerr, '\n');
errors(s); //在cerr中打印s,后面跟一个换行符
函数对象常常作为泛型算法的实参。例如,可以使用标准库 for_each 算法和我们自己的 PrintString 类来打印容器的内容:
for_each(vs.begin(), vs.end(), PrintString(cerr, '\n'));
for_each 的第三个实参是类型 PrintString 的一个临时对象,其中我们用 cerr 和换行符初始化了该对象。当程序调用 for_each 时,将会把 vs 中的每个元素依次打印到 cerr 中,元素之间以换行符分隔。
该文章会更新,欢迎大家批评指正。
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,
分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
服务器课程:C++服务器