抽象基类的C ++函数

时间:2009-11-01 19:57:34

标签: c++ abstract-class

假设我想拥有这样的继承层次结构。

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 类虚拟私有数据成员吗?我应该给它受保护的数据成员(因此派生类不会声明自己的私有数据成员)。我知道第二种方法会减少封装。

这种情况的最佳方法是什么?如果需要更详细的解释,我很乐意提供。

6 个答案:

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