【问题标题】:How to implement strategy pattern in C++ with std::function如何在 C++ 中使用 std::function 实现策略模式
【发布时间】:2015-05-15 21:48:16
【问题描述】:

我正在讨论在 C++ 中实现策略模式的最佳方式。到目前为止,我一直使用标准方式,其中上下文有一个指向基本策略类的指针,如下所示:

 class AbstractStrategy{
 public:
     virtual void exec() = 0;
 }
 class ConcreteStrategyA{
 public:
     void exec();
 }
 class ConcreteStrategyB{
 public:
     void exec();
 }

 class Context{
 public:
     Context(AbstractStrategy* strategy):strategy_(strategy){}
     ~Context(){
          delete strategy;
       }
      void run(){
           strategy->exec();
      }
 private:
     AbstractStrategy* strategy_;

由于指向对象的指针会导致不良行为,因此我一直在寻找一种更安全的方法来实现此模式,我发现this question 其中std::function 被提议作为处理此模式的更好方法。

有人可以更好地解释std::function 的工作原理吗,也许可以举个策略模式的例子?

【问题讨论】:

    标签: c++ pointers c++11 strategy-pattern std-function


    【解决方案1】:

    请注意,单方法对象与函数同构,策略只是单方法对象。

    所以基本上,你摆脱了所有的课程,而只使用 std::function<void()> 代替:

    class Context {
    public:
        template<typename F>
        explicit Context(F strategy) : strategy(std::move(strategy)) { }
    
        void run() { strategy(); }
    
    private:
        std::function<void()> strategy;
    };
    

    然后你可以将任何可调用对象传递给Context的构造函数:

    Context ctx([] { std::cout << "Hello, world!\n"; });
    ctx.run();
    

    【讨论】:

    • 在这种情况下,您将如何实施 F(策略)?以及为什么需要使用 std::move?
    • @Gabrielecswoosh 该策略是任何函数指针或对象,operator() 重载。我的示例传递了一个 lambda(它定义了 void operator()() const)。 std::move 是为了防止复制。
    【解决方案2】:

    关于这个话题herehere 有一些讨论。我认为这取决于手头的具体情况。例如,您的策略是否仅是一个简单的函数调用 - 我经常有策略模式,其中我的策略需要多种功能,仅通过一个函数或仿函数就无法很好地处理这些功能。但是,如果您确实只需要一个函数或仿函数,那么std::function 是一种方便的方法,可以实现最大的灵活性,存储函数指针、lambda 或仿函数。可能存在性能问题,这些问题已在 here 中针对原始 boost 实现进行了讨论。

    【讨论】:

    • 如果策略不是简单的函数调用,您有什么建议?在那种情况下,指向抽象类的指针是唯一的方法?
    • 是的。您可以根据需要使用std::unique_ptrstd::shared_ptr 处理适当的资源控制。
    【解决方案3】:

    正在研究райтфолд的答案

    基本上,您摆脱了所有类,而只使用 std::function 代替。

    此通用函数允许您传递函数、lambda、仿函数和成员函数(使用 std::bind)

    class Context {
    public:
       explicit Context(std::function<void()> input) : strategy(input) { }
    
    void run() { strategy(); }
    
    private:
       std::function<void()> strategy;
    };
    

    然后你可以将任何可调用对象传递给 Context 的构造函数:

    Context ctx([] { std::cout << "Hello, world!\n"; });
    ctx.run();
    

    void sayHelloWorld(){
       std::cout << "Hello, world!\n";
    }
    
    
    int main(){
       Context ctx( sayHelloWorld );
       ctx.run();  
    }
    

    class SayHelloWorld{
       operator()(){std::cout << "Hello, world!\n";}
    }
    
    int main(){
       SayHelloWorld hello_world;
       Context ctx( hello_world );
       ctx.run();  
    }
    

    【讨论】:

      【解决方案4】:

      这样的?

      #include <functional>
      #include <iostream>
      
      
      typedef std::function<int(int)> Strategy;
      
      void execute_strategy(Strategy strategy, int object) {
          std::cout << strategy(object) << std::endl;
      };
      
      int power2(int i) {
          return i*i;
      };
      
      int main() {
          execute_strategy(power2, 3);
      }
      

      我的意思是,策略模式是针对没有实际 lambda 的缺点的解决方案。这已经解决了,所以你可以传递适当的函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-07-29
        • 1970-01-01
        • 1970-01-01
        • 2011-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多