5

Given a typical strategy pattern

class Strategy
{
public:
    virtual int execute() const = 0;
}

class StrategyA : public Strategy
{
public:
    int execute() const override;
}

class StrategyB : public Strategy
{
public:
    int execute() const override;
}

I believe the 'pre-C++11' way to implement a context class would be something like

class ContextRaw
{
public:
    ContextRaw(Strategy* the_strategy);
    ~ContextRaw(); // Should this delete the_strategy_?
    int execute() const;
private:
    Strategy* the_strategy_;
}

To me, in this design it's not clear if Context should take responsibility for Strategy, and unless there is clear documentation stating otherwise, bad things might happen if it does

void trouble()
{
    StrategyA a_concrete_strategy;
    ContextRaw a_context(&a_concrete_strategy); // Oops, Context may try to delete stack variable
}

void more_trouble()
{
    Strategy* a_concrete_strategy = new StrategyA;
    ContextRaw* a_context       = new ContextRaw(a_concrete_strategy);
    ContextRaw* another_context = new ContextRaw(a_concrete_strategy);
    delete a_context;
    std::cout << another_context.execute() << std::endl; // Oops, the_strategy is deleted
}

In light of safe-pointers, should it now be preferable to inject a safe pointer, and have Context take ownership of the Strategy?

class ContextUnique
{
public:
    ContextUnique() = delete;
    ContextUnique(std::unique_ptr<Strategy> the_strategy);
    ~ContextUnique();
    int execute() const;
private:
    std::unique_ptr<Strategy> the_strategy_;
}

or if Strategy can be shared amongst different Context?

class ContextShared
{
public:
    ContextShared() = delete;
    ContextShared(std::shared_ptr<Strategy> the_strategy);
    ~ContextShared();
    int execute() const;
private:
    std::shared_ptr<Strategy> the_strategy_;
}

This design of course introduces problems of it's own, in particular only dynamically allocated Strategy's can be injected into Context.

Daniel
  • 8,179
  • 6
  • 31
  • 56

3 Answers3

7

The design is up to the implementator.

Notice that in your examples you reference different ways of screwing up with Strategy pattern using non C++11 pointers.

To answer directly to your question :

Yes you should use smart pointers in strategy pattern.

To further answer the question :

You should use smart pointers whenever possible.

The reason is that smart pointers practically are self documenting in terms of memory ownership policy, so you get rid of some the drawbacks of "If no good documentation".

Considering the prototype that you expose for your Context class , you can tell users what are your expectations :

  • unique_ptr if you expect the user to pass memory ownership to you
  • shared_ptr if you expect the same strategy implementation to be used on multiple owners
  • weak_ptr if you want the user to handle memory management

What is safer, it's up to you. However , you are able to tell users that the Context can share it's concrete strategy with other Contexts OR that there is 1 Concrete strategy per Context.

As a design approach, I would suggest to go with 1 Strategy / Context (so unique_ptr) since your concrete strategies might end up having some internal variables that are unique / context , and things will get complicated from there on.

MichaelCMS
  • 4,703
  • 2
  • 23
  • 29
3

You're doing it wrong.

In the light of std::function, everything you've just written is completely obsolete and you should just use std::function<int()> and some lambdas.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • You aren't required to use lambdas with `std::function` it merely allows you to do so. Of course it also avoids confusing ownership semantics. – Lionel Feb 06 '15 at 13:11
  • 5
    While I appreciate your input, it would be nice if you could explain why using `std::function` is a better approach rather than just berating my question. – Daniel Feb 06 '15 at 13:47
  • 1
    @Daniel Using `std::function` will avoid the ownership question since you no longer to have to pass arguments through a pointer to base. Just allow `std::function` to wrap your functor and it will take care of the memory management on your behalf. – Lionel Feb 06 '15 at 14:12
  • @Daniel: Because `std::function` completely replaces your base class and all of your derived classes in a completely generic way and makes it infinitely easier to add new strategies and handles all the ownership for you. In other words, `std::function` is pre-written, safer, faster, more generic, more flexible, and easier to use. So it's basically better in every way. – Puppy Feb 07 '15 at 12:19
  • what is happening if we need non const class for this? e.g. strategy will work with class fields, like function with static or global variables. – Nick Aug 07 '15 at 13:28
1

It strongly depends on what's the real purpose of Strategy objects whether they should be shared between various Context objects or owned by them.

At least when you use the shared or unique ptr, you clearly define your intentions. You should use a "raw" pointer only if you are going to "view" some other objects (you don't share nor own it - and you are sure that the pointing object won't outlive the pointed one).

Ethouris
  • 1,791
  • 13
  • 18