【问题标题】:Where to put util methods in C++ hierarchy在 C++ 层次结构中放置 util 方法的位置
【发布时间】:2019-03-07 18:55:41
【问题描述】:

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

【问题讨论】:

  • 你不能把它们放到基类中并让它们成为protected吗?
  • 我想过,但你认为这会是一个好的设计吗?
  • 我不明白为什么不呢?
  • 没有理由必须向客户端公开单独的 util 命名空间。只需将其放在客户无权访问的标头中即可。
  • @HolyBlackCat - 正确。为派生子类提供通用功能(受保护的行为方法)是拥有基类的目的之一。

标签: c++ oop


【解决方案1】:

有些人希望接口基类是空的纯虚拟抽象类,这对他们来说可能感觉像是“适当的设计”,但这种抽象级别会导致实现本身的效率更低。但是,如果您真的想保持您的接口类完全抽象,只需在其之上添加另一个层,然后所有实现都从该层派生:

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

如果由于您的类型的性质,某些功能(及其相关数据)在它的所有派生中总是通用的,那么开销较低的事情就是放弃使您的接口类完全抽象的理想,并将该通用功能(及其相关数据)移动到接口类(现在不是完全抽象的)中,并且不要创建方法virtual。我知道这会刺激一些人的神经,但实际上,如果不需要,为什么要支付多态性的成本呢?如果事实证明某些派生确实需要不同的功能,您可以随时对其进行重构。

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

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

【讨论】:

  • 那么这个额外的层会存在哪里呢?实用程序将是类,哪个类派生自它?请注意,util 函数不是多态的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-19
  • 1970-01-01
  • 2011-12-26
  • 2010-11-05
相关资源
最近更新 更多