views:

115

answers:

2

I have an abstract class CommandPath, and a number of derived classes as below:

class CommandPath {
    public:
        virtual CommandResponse handleCommand(std::string) = 0;
        virtual CommandResponse execute() = 0;
        virtual ~CommandPath() {}
};

class GetTimeCommandPath : public CommandPath {
    int stage;
    public:
        GetTimeCommandPath() : stage(0) {}
        CommandResponse handleCommand(std::string);
        CommandResponse execute();
};

All of the derived classes have the member variable 'stage'. I want to build a function into all of them which manipulates 'stage' in the same way, so rather than defining it many times I thought I'd build it into the parent class. I moved 'stage' from the private sections of all of the derived classes into the protected section of CommandPath, and added the function as follows:

class CommandPath {
    protected:
        int stage;
    public:
        virtual CommandResponse handleCommand(std::string) = 0;
        virtual CommandResponse execute() = 0;
        std::string confirmCommand(std::string, int, int, std::string, std::string);
        virtual ~CommandPath() {}
};

class GetTimeCommandPath : public CommandPath {
    public:
        GetTimeCommandPath() : stage(0) {}
        CommandResponse handleCommand(std::string);
        CommandResponse execute();
};

Now my compiler tells me for the constructor lines that none of the derived classes have a member 'stage'. I was under the impression that protected members are visible to derived classes?

The constructor is the same in all classes, so I suppose I could move it to the parent class, but I'm more concerned about finding out why the derived classes aren't able to access the variable.

Also, since previously I've only used the parent class for pure virtual functions, I wanted to confirm that this is the way to go about adding a function to be inherited by all derived classes.

+6  A: 

Try this:

class CommandPath {
protected:
  int stage;
public:
  CommandPath(int _stage) : stage(_stage) {}
};

class GetTimeCommandPath : public CommandPath {
public:
  GetTimeCommandPath(int _stage) : CommandPath(_stage) {}
};

(Omitted extra code for brevity).

You can't use the initializer list on a parent class' members, only the current one's. If that makes sense.

Bryan Ross
+1 Though it's perfectly safe to omit the leading `_` from the `_stage` variable.
Andreas Brinck
True. I add it just as a force of habit to distinguish between the two with a quick visual scan. Although normally my members are prefixed, and parameters aren't. *shrug*
Bryan Ross
Thanks, worked like a charm. One more thing - can a function defined in the abstract class, if it's called from one of its derivatives, refer to a function in the derivative class? I want to call the function execute from confirmCommand.
wyatt
Declare it in CommandPath as `virtual`, and then override it in your derived classes. Then, in CommandPath, you can call that virtual method, and it will be called on the appropriate derived class. Read up on virtual methods.
Bryan Ross
Nitpick: Variable names prefixed by underscore are reserved for the compiler and standard library. Better to use this convention instead: `stage_`
Emile Cormier
i had forgotten that. Been a while since I coded in C++
Bryan Ross
A: 

First of all: don't use protected for attributes.

It may seem arbitrary, but the point is that it breaks encapsulation. Imagine that suddenly you realize what of space it is to use an int when an unsigned short would have done, so you go ahead and change CommandPath.

Unfortunately, since all the classes deriving from CommandPath could access stage directly, there is a strong change the compiler will complain now: void changeStage(int&); is no longer suitable for example, so you have to reword it... and it's messy.

Proper encapsulation requires that you don't expose your attributes: they are defined as private and you never return handles to them. The idiomatic way is to provide Get and Set methods (you don't necessarily have to change their type, or you may provide overloads, etc...)

Also protected is quite a bastard keyword, it does not protect much and the accessibility restriction it is supposed to define is weak:

class Base { protected: void specialMethod(); };

struct Derived: Base { void specialForward() { specialMethod(); } };

A simple case of deriving and it's now public, that's why it can't be used for encapsulation ;)

Matthieu M.