Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Solving design involving multiple inheritance and composite classes in c++

I have struggled with this design problem for some time. I will do my best to explain what I am trying to do and the various approached that I have seen, what I am trying and why.

I work in a scientific computing environment where I deal with the same kinds of objects repeatedly. Imagine a galaxy which contains solar systems, each solar system contains planetary systems and each planetary system contains moons. To this end I think of the situation as a “has a” situation, and thus I have used composition to give the galaxy access to its solar systems, and each solar system access to the planetary systems which have access to their moons: each category being its own class.

It is often the case that the various problems I am working on contain different types of data about these objects. And, as different types of data become available I can do certain things with my objects. So, when I have data type 1 available to me I create the following classes

class GalaxyOne { /* … */ };
class SolarSystemOne { /* … */ };
class PlanetOne{ /* … */ };
class MoonOne{ /* … */ };

And when I have data type 2 available to me I create

class GalaxyTwo { /* … */ };
class SolarSystemTwo { /* … */ };
class PlanetTwo{ /* … */ };
class MoonTwo{ /* … */ };

The composite aspects of each class are dealt with by using container variables such as vectors which contain pointers to the contained classes. For example, within each Galaxy class one would find.

class GalaxyTwo{ /* … */
protected:
std::vector<SolarSystemTwo*> solarSystems;
/* … */
};

The difficulty comes when I want to combine classes into higher-order classes.

class GalaxyOneTwo: public GalaxyOne, public GalaxyTwo{
/* Additional methods */
};

However, this creates a problem of ambiguity in the solarSystems vector because GalaxyOneTwo would have a version of it from GalaxyOne and GalaxyTwo. Furthermore, the vectors it inherits contain pointers to objects which are not of type SolarSystemOneTwo, which would be required. So, I thought I could create a template class which all of my objects inherit from where I put all of my container variables.

template<MoonT,PlanetT,SolarSystemT>
    class PrimitiveGalaxy {
    private:
        std::vector<SolarSystemT*> solarSystems
};

class GalaxyOne: public PrimitiveGalaxy <MoonOne,PlanetOne,SolarSystemOne>{
    /* No longer defining any container variables for composition */
};

This approach works very nicely for those fundamental Galaxy types (GalaxyOne and GalaxyTwo). However, whenever I try to create the combined galaxy type I get all kinds of ambiguity.

class GalaxyOneTwo: public GalaxyOne, public GalaxyTwo, public     PrimitiveGalaxy<MoonOneTwo,PlanetOneTwo,SolarSystemOneTwo>{
    /* … */
};

I get ambiguity if I use solarSystems within any method defined in GalaxyOneTwo because it is defined three times, once through each inherited version from GalaxyOne and GalaxyTwo and a third time through GalaxyOneTwo.

I can get rid of this ambiguity by being specific and using

PrimitiveGalaxy::solarSystems

each time to refer to the proper vector, but this is not desirable because it requires a LOT of extra typing and syntax.

I have considered using friendship between each galaxy type and the primitive galaxy but this requires a similar level of verbose writing.

I have a hunch that namespaces may simplify my writing of code but I am not sure how to define the namespace such that within a namespace defined for GalaxyOneTwo that any reference to solarSystems is a reference to PrimitiveGalaxy::solarSystems

Edit:

Please note, the only difference between GalaxyOne and GalaxyTwo is NOT the type of the class contained in solarSystems. There are many differences because each class deals with different data relevant to the galaxy. Thus, I create different classes which will have different state variables, getters and setters for those state variables and methods to calculate and print data. solarSystems is an example of the feature which is giving me problems thus that is what I have described here. When GalaxyOneTwo is created it will use the same data that is used for GalaxyOne and for GalaxyTwo and thus I want to inherit all of their variables and methods. And because the data can be combined in different ways I need to create new methods for that within GalaxyOneTwo. These are some of the many differences that lead me to use inheritance. That being said, it is the container variables that allow for composition that give me problems. Within each solarSystem class there will be a similar vector giving them access to their planets, and so on and so forth.

Edit:

For a question specifically dedicated to my design philosophy here in general (as opposed to this questions emphasis on trying to resolve my current design attempt) see the following link: Guidance in creating design for multiple-inheritance composite classes in c++

like image 677
PhiloEpisteme Avatar asked Feb 25 '13 19:02

PhiloEpisteme


People also ask

What is multiple inheritance in C?

Multiple inheritance occurs when a class inherits from more than one base class. So the class can inherit features from multiple base classes using multiple inheritance. This is an important feature of object oriented programming languages such as C++.

Can a class be inherited from multiple classes?

Multiple Inheritance is a feature of C++ where a class can inherit from more than one classes. The constructors of inherited classes are called in the same order in which they are inherited. For example, in the following program, B's constructor is called before A's constructor.

How many classes are there in multiple inheritance?

Diagram of the Multiple Inheritance In the above diagram, there are two-parent classes: Base Class 1 and Base Class 2, whereas there is only one Child Class. The Child Class acquires all features from both Base class 1 and Base class 2. Therefore, we termed the type of Inheritance as Multiple Inheritance.


2 Answers

I think you have to have one class Galaxy, one class SolarSystem, etc. And yours GalaxyOne, GalaxyTwo SolarSystemOne, SolarSystemTwo etc. are only different objects instantited from these classes.

class SolarSystem { /* … */ };
class Planet{ /* … */ };
class Moon{ /* … */ };

class Galaxy{ /* … */
 public: // Galaxy(...?...){for (...) {read data??; solarSystem.push_back(createSolarSystem(data)); }
 void AddSolarSystem(SolarSystem* ss){solarSystem.push_back(ss);}
 protected:
   std::vector<SolarSystem*> solarSystems;
   /* … */
};

....

Galaxy GalaxyOne, GalaxyTwo;

If we have no way to use this simple aproach... Lets see yours:

class GalaxyOneTwo: public GalaxyOne, 
                    public GalaxyTwo, 
                    public PrimitiveGalaxy<MoonOneTwo,PlanetOneTwo,SolarSystemOneTwo>{
    /* … */
 using PrimitiveGalaxy<MoonOneTwo,PlanetOneTwo,SolarSystemOneTwo>::solarSystems;
 GalaxyOneTwo(){solarSystems.reserve(10);}
};

Here you have three private vectors: (using using you make it direct accesible)

std::vector<SolarSystemOne*   > GalaxyOne::solarSystems;
std::vector<SolarSystemTwo*   > GalaxyTwo::solarSystems;
std::vector<SolarSystemOneTwo*> solarSystems; //GalaxyOneTwo::solarSystems;

Is this what you need? Make it protected? enter image description here

like image 93
qPCR4vir Avatar answered Sep 27 '22 17:09

qPCR4vir


Maybe you could change data models to implement something component based. Each component could contain the same state information you mentioned for the different types. And you could inherit virtual functions

class Galaxy
{
  // galaxy information

  // Virtual functions shared among galaxies
  virtual void sharedGalaxyFunction() = 0;

  // Vector containing all solar systems in this galaxy
  std::vector<SolarSystem*> solarSystems_;
};

class SolarSystem
{     
  // solar system info

  // Virtual functions shared among all solar systems
  virtual void sharedSolarSystemFunction() = 0;

  // Vector containing planets
  std::vector<Planets*> planets_;
};

// etc for planets...

You could then inherit the different types of solar systems or galaxies to create the special cases and fill out the virtual function calls

class CoolSolarSystem : public SolarSystem
{
  // Special variables

  // Fill in the virtual function
  void sharedSolarSystemFunction();
};

You could then populate the containers inside the different base types with the pointers to your special types. You should be able to avoid a cast back to the special type if the virtual function calls handle enough of that information.

like image 24
Connor Hollis Avatar answered Sep 27 '22 18:09

Connor Hollis