将util方法放在C ++层次结构中的位置

时间:2019-03-07 18:55:42

标签: c++ oop

我有一个基类作为接口和多个派生类。我有2个util函数,每个派生类方法都需要使用。所以我不能将util函数放在派生类中,因为它将是代码的重复。因此,一种选择是我可以将它们放入单独的util名称空间,但是我不希望它公开给客户端。因此,这些方法需要隐藏在某个地方。我应该设计什么,或者应该如何使用这些功能?

1 个答案:

答案 0 :(得分:2)

有些人希望接口基类是空的纯虚拟抽象类,这对他们来说可能感觉像是“正确的设计”,但是抽象级别导致实现本身的效率更低。但是,如果您真的想让您的接口类完全抽象,只需在所有实现的基础上再添加一层,即可得出:

Abstract pure virtual interface base class
        ^
        |
        |
Implementation base class: common things, including your utility functions
  ^            ^             ^
  |            |             |
  |            |             |
Impl1         Impl2         Impl3

-------- ...in code: --------

class IThing
{
public:
   virtual void runAround();
   virtual void makeAMess();
};

class ThingBase : public IThing
{
public:
   // runAround() is not implemented here; implement it in derived classes
   virtual void makeAMess()
   {
      // ...default implementation of makeAMess(), which a derived class
      // can override if necessary...
   }
private:
   int utilFunc1(int a) { return ...whatever...; } // not virtual! (or, it could be, if necessary)
   int utilFunc2(int a) { return ...whatever...; } // not virtual! (or, it could be, if necessary)
}

class Thing1 : public ThingBase
{
public:
   virtual void runAround() { /* ...special code for Thing1... */ }
   // Thing1 uses the default implementation of makeAMess()
}

class Thing2 : public ThingBase
{
public:
   virtual void runAround() { /* ...special code for Thing2... */ }
   // Thing2 uses the default implementation of makeAMess()
}

class Thing3 : public ThingBase
{
public:
   virtual void runAround() { /* ...special code for Thing3... */ }
   virtual void makeAMess() { /* ...special code for Thing3... */ }
}

如果在所有实现之间都存在任何公共数据或功能,请务必将其移至上面的实现基类。将其保留在每个派生的实现中。但是,如果该功能(方法)是接口方法的实现,那么您将不必要地负担所有用户的多态方法调用的(少量)开销,以便调用它:

每个派生的vtable都有一个用于该公共函数的额外函数指针,即使从该接口指针/引用调用该函数,也必须以多态方式对其进行调用,即使该方法只有一种实现。每次这样的调用都需要额外的两三个指令,以及额外的内存提取,因为它是多态的。额外的开销通常不会被认为是过多的,但是它是额外的开销,并且在某些情况下可能非常重要(例如,“数组”类的多态operator[]()在一个数组中使用了数千次)紧密循环:如果所有派生对象均具有指向数据分配位置的private指针,则只需将该指针放在基类中并实现operator[]()的单个非多态版本即可访问基于该指针的适当元素)。

如果由于类型的性质,某些功能(及其关联的数据)将始终在其所有派生类之间是通用的,那么降低开销的事情是放弃使接口类完全抽象的理想,并将该通用功能(及其关联的数据)移到接口类(现在还不是完全抽象的)中,而不要使用方法virtual。我知道这激怒了一些人的神经,但实际上,如果不需要的话,为什么要付出多态性的代价?如果事实证明某些派生实际上确实需要其他功能,则可以随时对其进行重构。

Not-fully-abstract "interface" base class
  ^            ^             ^
  |            |             |
  |            |             |
Impl1         Impl2         Impl3

请注意,如果这样做,在那些非多态方法中仍然可以具有特定于派生的行为,因为非多态方法可以在必要时调用其他属于 的类方法。