2015-03-13 30 views
5

Strateji modelini C++ uygulamasında en iyi şekilde kullanıyorum.C++ ile strateji kalıbını std :: function ile nasıl uygulanır

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_; 

kötü davranışlara neden olabilir nesnelere işaretçileri sahip beri, arıyordu: Şimdiye kadar hep şöyle bağlam taban strateji sınıf göstericisi vardır standart bir yol, kullandım Bu kalıbı uygulamak için daha güvenli bir yol ve this question'u buldum. std::function bu deseni işlemenin daha iyi bir yolu olarak öneriliyor.

Birisi, std::function'un nasıl çalıştığını daha iyi açıklayabilir mi, belki de strateji modeliyle ilgili bir örnekle?

cevap

7

Tek yöntemli nesnelerin işlevlere izomorf olduğunu ve stratejilerin yalnızca tek yöntem nesneleri olduğunu unutmayın.

Yani temelde, tüm sınıfların kurtulmak ve sadece yerine std::function<void()> kullanın:

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

Bu durumda F'yi nasıl uygularsınız (strateji)? Ve neden std :: move kullanmanız gerekiyor? – gcswoosh

+2

@Gabrielecswoosh Bu strateji, yalnızca işleve() sahip aşırı yüklenmiş bir işlev işaretçisi veya nesnesidir.Örneğim bir lambda ('void operator()() const' öğesini tanımlar) geçer. std :: move' bir kopyasını engellemektir. – rightfold

0

şey gibi:

class Context { 
public: 
    template<typename F> 
    explicit Context(F strategy) : strategy(std::move(strategy)) { } 

    void run() { strategy(); } 

private: 
    std::function<void()> strategy; 
}; 

Sonra Context kurucusuna herhangi çağrılabilir geçebilir bu ?

#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); 
} 

Strateji kalıbı, gerçek lambdalara sahip olmamanın kısaltılması için bir çözümdür. Bu çözüldü, böylece uygun işlevi çevirin.

3

Bu konu hakkında here ve here ile ilgili biraz tartışma var. Sanırım, eldeki davaya bağlı. Stratejiniz yalnızca basit bir işlev çağrısı mıdır? Örneğin, stratejimin genellikle birden fazla yeteneğe ihtiyaç duyacağı, ancak bir işlev veya işlev sahibi olmanın çok iyi bir şekilde ele alınamayacağı stratejiler var. Ancak, sadece bir fonksiyona veya bir fonksiyona ihtiyacınız varsa, std::function, nihai esnekliğe, fonksiyon göstergelerinin, lambdaların veya funktörlerin depolanmasına izin vermenin kullanışlı bir yoludur. Orijinal boost uygulaması için here tartışılan performans sorunları olabilir.

+0

Stratejinin basit bir işlev çağrısı olmadığı durumda ne önerirsiniz? Bu durumda, soyut bir sınıfa işaretçi tek yoldur? – gcswoosh

+1

Evet. Uygun kaynak denetimini 'std :: unique_ptr' veya 'std :: shared_ptr 'ile uygun şekilde işleyebilirsiniz. – sfjac

1

Temel olarak, tüm sınıfların kurtulmak райтфолд

ait yanıta Çalışma ve sadece yerine std :: işlevini kullanın.

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

Bu genelleştirilmiş işlev

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

void run() { strategy(); } 

private: 
    std::function<void()> strategy; 
}; 

Sonra Bağlamında kurucusuna herhangi çağrılabilir geçebilir (STD :: bağlama kullanarak) işlevleri, lambda, funktorlar ve üye fonksiyonlarını geçmesine izin verir

veya

void sayHelloWorld(){ 
    std::cout << "Hello, world!\n"; 
} 


int main(){ 
    Context ctx(sayHelloWorld); 
    ctx.run(); 
} 

veya

class SayHelloWorld{ 
    operator()(){std::cout << "Hello, world!\n";} 
} 

int main(){ 
    SayHelloWorld hello_world; 
    Context ctx(hello_world); 
    ctx.run(); 
} 
İlgili konular