我有一个抽象类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',所以我没有多次定义它,而是认为我将它构建到父类中。我将'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'。我的印象是受保护的成员对派生类是可见的吗?
构造函数在所有类中都是相同的,所以我想我可以将它移动到父类,但我更关心的是找出派生类无法访问变量的原因。
此外,由于之前我只使用了父类用于纯虚函数,我想确认这是添加一个要由所有派生类继承的函数的方法。
答案 0 :(得分:14)
试试这个:
class CommandPath {
protected:
int stage;
public:
CommandPath(int stage_) : stage(stage_) {}
};
class GetTimeCommandPath : public CommandPath {
public:
GetTimeCommandPath(int stage_) : CommandPath(stage_) {}
};
(为简洁省略了额外的代码)。
您不能在父类的成员上使用初始化列表,只能使用当前的成员。如果这是有道理的。
答案 1 :(得分:4)
首先:不要将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(); } };
一个简单的派生案例,它现在已公开,这就是为什么它不能用于封装;)