-2

Arduino project. Using C++ 11 compiler. I have created an "interface" class, and several implementation classes. I want to implement stategy pattern. In my strategy manager class, I want to create an array of fixed length and initialize it in the constructor.

Java example of what I'm trying to do (and should be piece of cake in any modern language, right Stroustrup?)

public interface IState {
    public void handle();
}

public class StateImpl implements IState {
    @Override
    public void handle(){
        //Do something
    }
}

public class StrategyManager {
    private IState[] states;

    public StrategyManager(){
        states = new IState[3];
        state[0] = new StateImpl();
        state[1] = new StateImpl();
        ...
    }
}

My first attempt in C++:

IState.h:

class IState {
    public:
        virtual ~IState() {}
        virtual void handle() = 0;    
};

StateImpl.h:

#ifndef StateImpl_h  
#define StateImpl_h

#include "IState.h"

class StateImpl : public IState {

    public:
        StateImpl();
        virtual void handle() override;
};

#endif

StateImpl.cpp:

#include "StateImpl.h"

StateImpl::StateImpl(){}

void StateImpl::handle(){
    //Do something
}

So far it's ok. I've simplified my classes for brevity so the code might not compile, but mine's does, Now here comes the problem:

StrategyManager.h:

#ifndef StrategyManager_h  
#define StrategyManager_h

#include "IState.h"

class StrategyManager {

  private:
     extern const IState _states[3];          

  public:     
      StrategyManager(); 
};

#endif

StrategyManager.cpp:

#include "StrategyManager.h"

StrategyManager::StrategyManager(){    
    IState _states[3] = {
        new StateImpl(),  
        new StateImpl(), 
        new StateImpl()
    };
}

This gives me all sort of errors:

error: storage class specified for '_states'
error: invalid abstract type 'IState' for '_states' because the following virtual functions are pure within 'IState':
    virtual void IState::handle()
error: cannot declare field 'StrategyManager::_states' to be of abstract type 'IState'
since type 'IState' has pure virtual functions
... etc

So I have changed the array to hold pointers instead. In StrategyManager.h:

extern const IState* _states[3];

And now in StrategyManager.cpp constructor:

StrategyManager::StrategyManager(){ 
    IState impl = new StateImpl(); //I hope this will be stored in the heap.  
    IState* _states[3] = {
        &impl,  
        &impl, 
        &impl
    };
}

But still got errors:

error: storage class specified for '_states'
error: cannot declare variable 'impl' to be of abstract type 'IState'
since type 'IState' has pure virtual functions

And on and on and on...

How can I do this in a simple way without using vectors or boost or any other fancy stuff? (Remember this is Arduino)

Mister Smith
  • 27,417
  • 21
  • 110
  • 193
  • a `new` expression evaluates to a pointer in C++. – juanchopanza Aug 24 '15 at 23:13
  • 1
    Why extern? Why fixed size? – Amit Aug 24 '15 at 23:13
  • @juanchopanza If I don't use new, it will be created in the stack, since I'm in the constructor? – Mister Smith Aug 24 '15 at 23:14
  • @Amit extern to be defined in the cpp file instead of the .h; fixed size because this is embedded and I don't need it to grow. – Mister Smith Aug 24 '15 at 23:15
  • I mean this can't work: `IState _states[3] = { new StateImpl(), ...` That is the source of one of the errors. – juanchopanza Aug 24 '15 at 23:17
  • Remove the extern & const, use pointers (`*`), set values by index (not an array assignment), each instance with its own `new`. – Amit Aug 24 '15 at 23:19
  • @Amit I forgot to mention the actual `StateImpl' class has a constructor with parameters. if I remove the extern, won't it try to call the default StateImpl parameterless constructor? – Mister Smith Aug 24 '15 at 23:24
  • Instead of posting incorrect and meaningless code, you should explain what you are trying to do. Also, you don't seem to understand that in Java everything is a basic type or a **reference (aka pointer)**. C++ doesn't force you to have pointers everywhere. – curiousguy Aug 25 '15 at 00:21
  • @curiousguy I spent some precious time posting code so that I won't be critziced for not posting code. But I summarized my intent in the first three lines for the lazy readers. Maybe you belong to the taliban sect of downvoters who didn't like my remarks on C++ being complex? – Mister Smith Aug 25 '15 at 08:57
  • @MisterSmith C++ is complex, but the simple C++ you are writing is not. You simply don't seem to understand C++ or Java. And no, you didn't made your intent clear. – curiousguy Aug 25 '15 at 18:49

1 Answers1

4

It's really much simpler than that, and closer to your java code (only showing relevant parts):

class StrategyManager {

  private:
     IState *_states[3];          

  public:     
      StrategyManager(); 
};

StrategyManager::StrategyManager(){    
    _states[0] = new StateImpl();
    _states[1] = new StateImpl();
    _states[2] = new StateImpl();
    };
}

Just remember, C/C++ is not java, there's no GC so cleanup your objects

Amit
  • 45,440
  • 9
  • 78
  • 110
  • Thanks for your answer. Yes, that compiles. If it weren't an array of `IState*`, and instead it were an array of `IState`, i thought it will try to call the default constructor for each position in the array. But since they are pointers now I think they will be initialized to zero, then the constructor will initialize them to the correct value, isn't it? – Mister Smith Aug 24 '15 at 23:32
  • Don't assume zero initialization. Uninitialized memory can have any "garbage" value. Other than that, yes – Amit Aug 24 '15 at 23:34
  • Alternatively, inside the constructor, `for (auto&& p : _states) p = new StateImpl();` – M.M Aug 24 '15 at 23:41
  • 2
    Expanding on "clean up your objects": the destructor should `delete` them all, and you should disable the copy-constructor and copy-assignment operator to prevent accidental shallow copies. You may provide a move-constructor and move-assignment operator if you want. Those actions are all performed for you if you use `std::unique_ptr _states[3];` instead . Which I would strongly recommend. – M.M Aug 24 '15 at 23:43
  • @MattMcNabb - You're right. On all points. I just think you're not considering your "audience" here... My answer was deliberately aiming a little lower (without disrespecting anybody) – Amit Aug 24 '15 at 23:47
  • 1
    @Amit Yep, I am just giving additional info for OP. `unique_ptr` is only a few extra characters to type, and it will solve problems for him :) – M.M Aug 24 '15 at 23:47
  • Agreed, and its worth an edit. I'll do so when I'm on a proper pc and not mobile ;D – Amit Aug 24 '15 at 23:50
  • Guys, thanks for the deletion remarks, but I'm not going to delete anything. All these objects should live for as long as the program is running. – Mister Smith Aug 25 '15 at 00:02
  • 2
    @MisterSmith `IState` is an abstract class; you can't make instances of an abstract class! – curiousguy Aug 25 '15 at 00:23