C++仿函数

仿函数(Functor)又叫函数对象(Function Object), 这是一个很有趣的编程能力,它来源于编程语言的操作符重载的特性,只要允许操作符重载的语言都可以实现仿函数的特性用法,比如c++,甚至一些本身并不包含函数特性的语言,例如java、c#。

我们知道C++允许很多操作符重载,其中就包含()即函数调用操作符,如果将一个类的()操作符进行重载,在类被示例化后在对象名称后加上()就会显得非常有趣,他就像一个函数一样被调用,被传递,甚至放在全局它看起来就像一个真的函数一样,这就是仿函数。

看下面的例子:

class loging {
public:
   explicit CalculateSalary(const char* channel): channel_(channel) {
       if (files_.find(channel) === files_.end()) {
          throw std::exception("未配置日志文件");
       }
       file_ = files_[channel];
   }

   bool operator()(std::string source, std::string message) {
       if (source_ != source) {
          ++step_;
       }
       ... 记录日志
   }
private:
   const char* channel_;
   int step_ = 0;
   std::map<std::string, std::string> files_ = {
      {"pay", "/var/run/logs/pay.log"},
      {"async", "/var/run/logs/async.log"}
   };
   std::string file_;
   std::string source_;
}

class step1 {
public:
   void run(loging log) {
      ......
      log("step1", "发生了错误1");
      ......
      log("step1", "发生了错误2”);
   }   
}

class setp2 {
public:
   void run(loging log) {
       ....
       log("step2", “发生了错误3”);
       ...
       log("step3", "发生了错误4");
   }
}

int main(void) {
   loging log;

   step1  s1;
   s1.run(log);

   step2 s2;
   s2.run(log);
}

首先实例化loging类, 然后传递了log对象, 在使用时使用了log("step2", “发生了错误3”);, 和函数的调用方式非常相象。

如果采用函数指针作为参数传递,那我们看一下会发生什么情况,我们需要将step,files,file_,source_都做成全局的参数,这种情况下,你很难保证这些变量不被其他代码修改, 如果我们将从files_根据日志的channel选择日志存储的逻辑全部封装在函数内部,但很 明显这些都是可以复用的,封进函数会牺牲性能。而采用伪函数,则可以利用类的封装特性,而相比于类为函数显得更加简洁, 更加干净。

发表回复

您的电子邮箱地址不会被公开。