假设我想拥有这样的继承层次结构。
class Base
class DerivedOne : public Base
class DerivedTwo : public Base
基类不是要实例化的,因此有一些派生类必须定义的纯虚函数,使它成为一个抽象基类。
但是,您希望派生类从基类中获取一些函数。这些函数修改 DerivedOne 和 DerivedTwo 都将拥有的私有数据成员。
class Base {
public:
virtual void MustDefine() =0; // Function that derived classes must define
void UseThis(); // Function that derived classes are meant to use
};
但是,UseThis()
函数用于修改私有数据成员。这就是问题所在。我应该给 Base 类虚拟私有数据成员吗?我应该给它受保护的数据成员(因此派生类不会声明自己的私有数据成员)。我知道第二种方法会减少封装。
这种情况的最佳方法是什么?如果需要更详细的解释,我很乐意提供。
答案 0 :(得分:5)
如果这些成员变量应该存在于所有派生类中,那么您应该在基类中声明它们。如果您担心封装,可以使它们private
并为派生类提供protected
访问器方法。
答案 1 :(得分:2)
另外五美分:好的做法是拥有抽象的接口类,它没有其他成员,只有公共的纯虚方法和公共虚拟析构函数。然后你创建基本实现,它也可以是抽象的,但可以有受保护的字段等。
在你的情况下,它会是这样的:
class IBlaBla;
class BlaBlaBase : public IBlaBla;
class DerivedOne : public BlaBlaBase
class DerivedTwo : public BlaBlaBase
如果您认为Base不再适合某些专业任务,那么您将来可以更灵活。
我应该给Base类假 私人数据成员?
如果您可以实现一部分功能而不将细节暴露给派生类,那么在基类中执行它。如果派生类需要访问这些成员,请提供setter和getter。但是,为派生类设置setter是不方便的,因为你的代码会紧密耦合。
答案 2 :(得分:1)
封装有时会被高估。如果您的基类和派生类需要访问这些成员,那么它们应该是protected
,而不是private
。如果它确实是需要封装的东西,那么你可能想要它们private
但是提供getter和setter(要么将它们设为私有Base
,要么在那里定义getter和setter,要么私有派生类,Base
)中的纯虚拟getter和setter。
在不知道您要解决的实际问题的情况下,向您提供更具体的建议有点困难。
答案 3 :(得分:0)
您必须定义Base :: UseThis(),在其体中您将使用Base的字段(您还需要在上面的类定义中声明)。如果您只需要在UseThis中访问它们,它们可以是私有的。如果DerivedOne / Two需要访问它们,您应该对它们进行保护。
答案 4 :(得分:0)
以下是您的困境的可能解决方案:
class Base {
public:
virtual ~Base() {}
virtual void redefine_me() = 0;
void utility_function();
private:
virtual int get_data_member() = 0;
virtual void set_data_member(int v) = 0;
};
class Derived1 : public Base {
public:
virtual void redefine_me() { do_d1_stuff(); }
private:
int my_private_idaho_;
virtual int get_data_member() { return my_private_idaho_; }
virtual void set_data_member(int v) { my_rpviate_idaho_ = v; }
};
class Derived2 : public Base {
public:
virtual void redefine_me() { do_d2_stuff(); }
private:
int gomer_pyle_;
virtual int get_data_member() { return gomer_pyle_; }
virtual void set_data_member(int v) { gomer_pyle_ = v; }
};
void Base::utility_function()
{
set_data_member(get_data_member() + 1);
}
最大的缺点是现在访问私有数据成员是由虚函数调用调解的,这不是最便宜的事情。它也被优化器隐藏了。
这意味着如果选择它,则应该采用一种模式,在实用程序函数开头将私有数据成员提取到局部变量中,并在返回之前从局部变量设置它。当然,一些实用程序函数可能会调用需要在调用对象状态之前更新的函数,然后必须修改此模式以解决该问题。但话说回来,这些效用函数可能无法满足强大的异常处理保证,无论如何都应该重新考虑。
答案 5 :(得分:0)
看起来你需要一些客户端代码接口,以及接口实现者的一些“方便”功能,只有当他们遵循调用便利层的useThis
功能的规则时才能使用它们,这将调整他们的私人成员。
每当我屈服于将便利功能放在我的抽象基类中的诱惑时,我很后悔(很快!)。它带走了很多灵活性。 solution proposed by AlexKR使这种情况略好一些。
另一种方法是提供一些便利类,接口的实现者可以聚合而不是继承它。它可以提供一个函数,将实现者的成员作为参数。
class Interface { public: virtual void f() = 0; };
class Convenience {
public:
void tweakMyMembers( int& member1, float& member2 );
bool somestate;
};
class Implementor : public Interface {
int m1; float m2;
public: Implementor( bool b ): conv( b ) {}
virtual void f() { conv.tweakMyMembers( m1, m2 ); if( m1<m2 ) dothis(); }
};