переменные в абстрактных классах C ++ - PullRequest
4 голосов
/ 16 мая 2010

У меня есть абстрактный класс CommandPath и несколько производных классов, как показано ниже:

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

Все производные классы имеют переменную-член "stage". Я хочу встроить функцию во все из них, которая одинаково манипулирует «стадией», поэтому вместо того, чтобы определять ее много раз, я решила встроить ее в родительский класс. Я переместил «stage» из закрытых разделов всех производных классов в защищенный раздел CommandPath и добавил функцию следующим образом:

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

Теперь мой компилятор сообщает мне для строк конструктора, что ни один из производных классов не имеет члена "stage". У меня сложилось впечатление, что защищенные члены видны производным классам?

Конструктор одинаков во всех классах, поэтому я полагаю, что могу переместить его в родительский класс, но меня больше беспокоит выяснение, почему производные классы не могут получить доступ к переменной.

Кроме того, поскольку ранее я использовал родительский класс только для чисто виртуальных функций, я хотел подтвердить, что это способ добавления функции, которая наследуется всеми производными классами.

Ответы [ 2 ]

14 голосов
/ 16 мая 2010

Попробуйте это:

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

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

(опущен дополнительный код для краткости).

Вы не можете использовать список инициализаторов для членов родительского класса, только для членов текущего класса. Если это имеет смысл.

4 голосов
/ 16 мая 2010

Прежде всего: не используйте protected для атрибутов.

Это может показаться произвольным, но дело в том, что он нарушает инкапсуляцию. Представьте себе, что внезапно вы понимаете, что такое пространство, чтобы использовать int, когда unsigned short сделал бы, поэтому вы продолжаете и меняете CommandPath.

К сожалению, поскольку все классы, производные от CommandPath, могут напрямую обращаться к stage, компилятор теперь будет жаловаться на сильное изменение: void changeStage(int&); больше не подходит, например, поэтому вы должны перефразировать его и это грязно.

Правильная инкапсуляция требует, чтобы вы не выставляли свои атрибуты: они определены как private, и вы никогда не возвращали им дескрипторы. Идиоматический способ - предоставить методы Get и Set (вам не обязательно менять их тип, или вы можете предоставить перегрузки и т. Д.)

Также protected - довольно ублюдочное ключевое слово, оно мало защищает, и ограничение доступности, которое он должен определить, является слабым:

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

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

Простой случай получения и теперь публичный, поэтому его нельзя использовать для инкапсуляции;)

...