我是否在特定情况下证明多重继承是合理的?

Am I in a specific case justifying multiple inheritance?

目前,我有以下 类 管理不同种类的变量:

class Variable;
class Number : public Variable;
class Boolean : public Variable;
class RealNumber : public Number;
class IntegerNumber : public Number;

这是一个经典的继承树,效果很好。我管理 Number* 个实例的向量和 Boolean* 个实例的向量。

我想添加另一种类型的变量:'virtual variable' 具有特定的行为但具有相似的界面。然后,我想使用 'virtual numbers' 的向量和 'virtual booleans' 的向量。

第一个解决方案是在 Variable 中添加一个标志,但我更喜欢编译安全并管理 std::vector<VirtualResponse*>std::vector<VirtualBoolean*> 以保证变量在矢量。

然后,我想也许我处于多重继承合理的特定情况下,但我是多重继承的新手。 你怎么看待这种类?

class VirtualVariable : public virtual Variable;
class VirtualNumber : public virtual Number, public virtual VirtualVariable;
class VirtualBoolean : public virtual Boolean, public virtual VirtualVariable;
class VirtualRealNumber : public virtual RealNumber, public virtual VirtualNumber;
class VirtualIntegerNumber : public virtual IntegerNumber, public virtual VirtualVariable;

这是一种经典的方法吗?会不会是陷阱?我是否滥用了 virtual 关键字?

编辑:我想做的事的例子:

void my_function(const std::vector<SpecItem>& spec)
{
  // First : create the description from the spec
  std::vector<Number*> descr;
  std::vector<VirtualNumber*> virt_descr;
  for(unsigned long int i = 0; i < spec.size(); ++i)
  {
    if(spec[i].isNumber())
      if(spec[i].isReal())
      {
        double lower_bound = spec[i].lowerBound();
        double upper_bound = spec[i].upperBound();
        if(spec[i].isVirtual())
        {
          std::string formula = spec[i].formula();
          virt_descr.push_back(new VirtualRealNumber(lower_bound,upper_bound,formula));
        }
        else
          descr.push_back(new RealNumber(lower_bound,upper_bound));
      }
      else if(spec[i].isInteger())
      {
        long int lower_bound = ceil(spec[i].lowerBound());
        long int upper_bound = floor(spec[i].upperBound());
        if(spec[i].isVirtual())
        {
          std::string formula = spec[i].formula();
          virt_descr.push_back(new VirtualIntegerNumber(lower_bound,upper_bound,formula));
        }
        else
          descr.push_back(new IntegerNumber(lower_bound,upper_bound));
      }
  }
  // Now, descr is a vector of Number*, virt_descr is a vector of VirtualNumber*

  // Second step : assign values to the numbers
  std::vector<double> values;
  for(unsigned long int i = 0; i < descr.size(); ++i)
  {
    double new_value = (descr[i]->lowerBound() + descr[i]->upperBound()) / 2.0;
    values.push_back(descr[i]->adjust(new_value));
  }

  // Third step : evaluate virtual numbers
  std::vector<double> virt_values;
  for(unsigned long int i = 0; i < virt_descr.size(); ++i)
  {
    double new_value = virt_descr[i]->evaluate(values);
    values.push_back(descr[i]->adjust(new_value));
  }

  return 0;
}

我尽量避免多重继承。 听起来您想要一个对象,它的行为类似于基础对象,但针对特定情况进行了更改。听起来像 class 规范。我不确定您的目标是什么,但也许您可以将您的设计更改为 class 层次结构模型。要实现这一点,请将所有特定代码放在单独的函数中,然后在基础 class.

中调用
class Number : public Variable
{
    ...
    protected:
        virtual void functionWithSpecialUsage() { // Empty in base class, used by inheritance only };
}

class VirtualNumber : public Number
{
    ...
    protected:
        void functionWithSpecialUsage() { // Add class specific behavior here };
}

多重继承旨在通过确保 separation of concerns 来支持简洁的设计。所以可以说是天经地义。

在不了解更多原因和限制的情况下,很难就最好的 class 设计提出建议。但有些问题可能对您有所帮助。

它从以下行开始:

    class VirtualVariable : public virtual Variable;

1)是否需要虚拟继承?

多继承真的很简单有趣,直到你来到虚拟继承。这意味着对于从 VirtualVariable 多次继承的 class,所有 VirtualVariable 子对象实例将共享一个 Variable 基础对象。有关详细信息,请阅读 diamond problem

在您的设计中,您可能需要为 Variable 的每个继承(包括非 "Virtual" 的继承)以避免拥有多个 Variable 子对象,而您真正应该只有一个。其他继承不需要它。

此虚拟继承将要求您为每个构造函数显式初始化您的虚拟基础。

2) VariableVirtualVariable 之间的真正关系是什么?

根据您的解释,我了解到 "Virtuality" 概念独立于 "Variable" 概念。另一方面,在您的代码中,您假设 VirtualVariable Variable

如果它是独立的东西,你应该保持它的独立性。这将是更好的关注点分离。好消息是你不再需要虚拟继承:

class Virtuality;     
class VirtualVariable : public Variable, public Virtuality;
class VirtualNumber : public Number, public Virtuality;
class VirtualBoolean : public Boolean, public Virtuality;
class VirtualRealNumber : public RealNumber, public Virtuality;
class VirtualIntegerNumber : public IntegerNumber, public Virtuality;

3) 你看过模板了吗?

你说:

A first solution is adding a flag in Variable, but I prefer the compilation security and manage

如果只是为了编译类型检查,可以使用模板:

template <bool isvirtual>
Variable { ...};
template <bool isvirtual>
class Number :  Variable<isvirtual> { ...};
...
std::vector<Number<true>*> v; 

对于比变量更复杂的事物,您可以使用基于策略的设计。粗略地说,不是提供 bool 作为模板参数,而是提供实用程序 class 作为参数,class 封装了一系列行为。

编辑:供您编​​辑

解决方案 2 和 3 应该完全能够实现您的意图。

我在这里发布了解决方案 2 的 online demo 和多态性来演示混合 "Virtual" 和非 "Virtual" 元素是多么容易.它定义了一个函数IntegerNumber add (IntegerNumber &a, IntegerNumber&b);,并在这个场景中使用它:

IntegerNumber n(27);  // number with an inital value
VirtualIntegerNumber k;   // value will be calculated from some formula 
add (n, k);            // ok, my demo just displays the value of each parameter ;-)

*顺便说一句,我认为您代码中的构造 if (isxxx())... if (isyyy()) ... 强烈建议您应该使用多态性。