1

Currently, I have the following classes managing different kinds of variables :

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

This is a classical inheritance tree, and it works well. I manage vectors of Number* instances and vectors of Boolean* instances.

I would like to add another type of variable : a 'virtual variable' with a specific behaviour but a similar interface. Then, I want to work with vectors of 'virtual numbers' and vectors of 'virtual booleans'.

A first solution is adding a flag in Variable, but I prefer the compilation security and manage std::vector<VirtualResponse*> and std::vector<VirtualBoolean*> in order to have the guarantee the variables are virtual in the vector.

Then, I think maybe I am in a specific case where multiple inheritance is justified, but I'm new with multiple inheritance. What do you think about this kind of classes ?

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;

Is it a classical way to do that ? Could it be a trap ? Do I abuse of the virtual key-word ?

EDIT: example of what I want to do :

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;
}
Caduchon
  • 4,574
  • 4
  • 26
  • 67
  • 2
    I recommend you take a look at this [multiple-inheritance example](https://isocpp.org/wiki/faq/multiple-inheritance#mi-example) (under *Can you provide an example that demonstrates the above guidelines?*) and see if any of the alternatives might work well for you. – Joseph Mansfield Apr 28 '15 at 13:42
  • 5
    My opinion is that inheritance (multiple or otherwise, virtual or otherwise) cannot be designed properly, without understanding the *goal* first. Writing inheritance or object-oriented design *cannot* be the goal. So what is the goal here? What are you trying to achieve? – Nawaz Apr 28 '15 at 13:42
  • Manage `std::vector` and `std::vector` objects in the same way I currently use `std::vector` with some algorithms. `VirtualVariables` has only specific behaviour when evaluated but keep the same behaviour for the mathematical point of view. – Caduchon Apr 28 '15 at 13:45
  • 3
    That was not informative. You are in a language where subtype relationships and interfaces are not limited to inheritance, where there are multiple kinds of inheritance, etc. In order to understand your problem, use cases of the types need to be explained. The very first thing you start with, having C++ inheritance wrapping an integer, is questionable: even oo-addicted languages do that with trepidation. – Yakk - Adam Nevraumont Apr 28 '15 at 13:47
  • http://en.wikipedia.org/wiki/Bridge_pattern – el.pescado - нет войне Apr 28 '15 at 13:49
  • Actually, it's a simplified example. In my application, a variable is the description of how can be a value (bounds, variation, etc..). Then I have a vector of `Number*` describing my space, and a correlated vector of `double` where each value respects the constraints of the Number. From that, I have a lot of mathematical algorithms working in the defined space. The specific case of virtual variables is for variables extending the space dimension and having some special constraints. I want to manage the correlated values in another vector of values, with respect to a `std::vector` – Caduchon Apr 28 '15 at 13:58

2 Answers2

1

Multiple inheritance is meant to favour clean design by ensuring separation of concerns. So it can be perfectly justified.

It's difficult to advise on the best possible class design without knowing more about the reasons and the constraints. But there are some questions that may help you.

It starts at the line:

    class VirtualVariable : public virtual Variable;

1) Do you need virtual inheritance ?

Multiple inheritance is really simple and fun until you come to virtual inheritance. It means that for a class inheriting several times from VirtualVariable, all the VirtualVariable sub-object instances would share a single Variable base object. For more information, read on the diamond problem.

In your design, you might need it for every inheritance of Variable (including for the non "Virtual" ones) in order to avoid having several Variable subobjects, where you really should have only one. You shouldn't need it for the other inheritance.

This virtual inheritance will require that, for every constructor, you explicitly initialize your virtual base.

2) What is the real relation between Variable and VirtualVariable ?

According to your explanation, I've understood that the "Virtuality" concept is independent of the "Variable" concept. On the other hand, in your code you pretend that VirtualVariable is a Variable.

If it is something independent you should keep it independent. This would be better separation of concerns. The good news is that you then no longer need virtual inheritance:

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) Have you looked at templates ?

You said:

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

If it's only for compilation type checking, you could use templates:

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

For more complex things than just a variable, you could use policy based design. Roughly speaking, instead of providing a bool as template parameter, you provide a utility class as parameter, the class encapsulating a family of behaviors.

EDIT: for your edit

Solution 2 and 3 should perfectly be able to implement what you intend to do.

I have posted here an online demo of solution 2 with polymorphism to demonstrate how easily you could mix "Virtual" and non "Virtual" elements. It defines a function IntegerNumber add (IntegerNumber &a, IntegerNumber&b);, and uses it in this scenario:

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 ;-)

*By the way, I think that the constructs if (isxxx())... if (isyyy()) ... in your code strongly suggest that you should use polymorphism.

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Thx for your answer. I want to be able to manipulate `Number*` pointers who are `IntegerNumber*` or `RealNumber*` in reality, but I also want to manipulate `VirtualNumber*` pointers who are `VirtualIntegerNumber*` or `VirtualRealNumber*` in reality. In think it's not possible with your solutions 2) and 3), right ? – Caduchon Apr 29 '15 at 06:44
  • I think it should. Can you edit your question, showing an example of what you think works with your approach and doesn't work with 2 and 3 , so that i can refine my answer if needed ? – Christophe Apr 29 '15 at 10:55
  • @Caduchon I've edited the answer, including a link to source code demonstrating a short proof of concept. – Christophe Apr 29 '15 at 18:48
  • 1
    Thanks for this nice answer. I have to read it again, but it seems to work, actually. ;-) About the construction `if (isxxx())... if (isyyy())`, it's justified because it comes from a JSON format, and it was simplified for the example. – Caduchon Apr 29 '15 at 19:57
-1

I try to avoid multiple inheritance as every possible. Its sound like you want an object what acts like the base objects but with changes for specific cases. Sounds like a class specification. I am not sure whats your goal but maybe you can change your design to a classical hierarchy model. To achieve this make all specific code in seperate functions and call then in the base 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 };
}
norca
  • 138
  • 1
  • 14
  • I have two problems with this hierarchy. 1/ I have to rewrite the virtual functionality in each subclass. 2/ If I define `class VirtualRealNumber : public RealNumber`, I can't manage a vector of `VirtualNumber*`. – Caduchon Apr 28 '15 at 14:07
  • for iterating over a set of classes you need to choose the base class for them. in your case a std::vector. You can cast them with dynamic or static casts. – norca Apr 28 '15 at 14:31
  • No. I need to have pointer to my classes from level 2. `Variable` is only a common type to avoid code duplication. Indeed, I implemented functions like `cloneAsNumber() const` in my level 2 subclasses. Do a dynamic cast on each access to a variable is really too slow for what I do. – Caduchon Apr 28 '15 at 14:38
  • I don`t get the goal of this. Maybe you share more code or use cases. If you want to use your own numbers for something with high performance like a physics simulation then its a general better way to accept code duplication for performance and create every class with no inheritance. – norca Apr 28 '15 at 14:48
  • `Variable` is a very big abstract class. I have 9 classes inheriting from it. There is really no reason to duplicate 9 times thousands lines of code only to avoid a working pattern... – Caduchon Apr 28 '15 at 14:57