2015-03-13 13 views
5

Sono entusiasta del modo migliore per implementare il modello di strategia in C++. Fino ad ora, ho sempre usato il modo standard, dove il contesto ha un puntatore alla classe strategia di base come segue:Come implementare il modello di strategia in C++ con std :: function

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

Dal momento che avere puntatori a oggetti può provocare un cattivo comportamento, ero alla ricerca per un modo più sicuro per implementare questo modello e ho trovato this question dove std::function sono proposti come un modo migliore per gestire questo modello.

Qualcuno potrebbe spiegare meglio come funziona std::function, magari con un esempio con lo schema di strategia?

risposta

7

Si noti che gli oggetti a metodo singolo sono isomorfi alle funzioni e le strategie sono solo oggetti a metodo singolo.

Quindi, fondamentalmente, a sbarazzarsi di tutte le classi, e basta usare std::function<void()> invece:

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

    void run() { strategy(); } 

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

Quindi è possibile passare qualsiasi callable al costruttore di Context:

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

In questo caso, come implementeresti F (la strategia)? E perché è necessario utilizzare std :: move? – gcswoosh

+2

@Gabrielecswoosh La strategia è qualsiasi funzione puntatore o oggetto con 'operator()' sovraccarico.Il mio esempio passa a lambda (che definisce 'void operator()() const'). 'std :: move' serve per prevenire una copia. – rightfold

0

Qualcosa di simile Questo ?

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

Voglio dire, il modello di strategia è una soluzione per la mancanza di un vero lambda. Questo è risolto, quindi puoi semplicemente passare la funzione appropriata in giro.

3

C'è un po 'di una discussione su questo argomento here e here. Penso che dipenda dal caso particolare in questione. La tua strategia è una semplice funzione chiamata solo, ad esempio: spesso ho schemi di strategia in cui la mia strategia avrà bisogno di più capacità, che non sono gestite bene semplicemente avendo una funzione o un funtore. Ma se hai bisogno solo di una funzione o di un functor, allora std::function è un modo pratico per consentire la massima flessibilità, memorizzando puntatori di funzioni, lambda o funtori. Ci possono essere problemi di prestazioni, che sono stati discussi here per l'implementazione boost originale.

+0

Cosa suggeriresti nel caso in cui la strategia non sia semplice una chiamata di funzione? In tal caso un puntatore a una classe astratta è l'unico modo? – gcswoosh

+1

Sì. Puoi gestire il corretto controllo delle risorse con 'std :: unique_ptr' o' std :: shared_ptr' come appropriato. – sfjac

1

Lavorando sulla risposta di райтфолд

In sostanza, a sbarazzarsi di tutte le classi, e basta usare std :: funzione invece.

Questa funzione generalizzata permette di passare funzioni, lambda, funtori, e funzioni membro (utilizzando std :: bind)

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

void run() { strategy(); } 

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

Quindi è possibile passare qualsiasi callable al costruttore di Context:

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

o

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


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

o

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

int main(){ 
    SayHelloWorld hello_world; 
    Context ctx(hello_world); 
    ctx.run(); 
} 
Problemi correlati