在c ++中解决涉及多继承和复合类的设计

时间:2013-02-25 19:50:37

标签: c++ templates multiple-inheritance composite ambiguity

我一直在努力解决这个设计问题。我将尽我所能来解释我正在尝试做什么以及我所看到的各种接近,我正在尝试什么以及为什么。

我在科学计算环境中工作,在那里我反复处理相同类型的对象。想象一个包含太阳系的星系,每个太阳系都包含行星系统,每个行星系统都包含卫星。为此,我将这种情况视为一种“有”的情况,因此我使用组合来让银河系进入其太阳系,并且每个太阳系都可以进入可以进入卫星的行星系统:每个类别是自己的班级。

通常情况下,我正在处理的各种问题包含有关这些对象的不同类型的数据。而且,随着不同类型的数据变得可用,我可以使用我的对象做某些事情。所以,当我有数据类型1可用时,我创建以下类

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

当我有数据类型2可供我使用时,我创建了

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

每个类的复合方面都是通过使用容器变量来处理的,例如包含指向所包含类的指针的向量。例如,在每个Galaxy类中都可以找到。

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

当我想将类组合成更高阶的类时,困难就出现了。

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

然而,这会在solarSystems矢量中产生歧义问题,因为GalaxyOneTwo会有GalaxyOne和GalaxyTwo的版本。此外,它继承的向量包含指向不属于SolarSystemOneTwo类型的对象的指针,这是必需的。所以,我想我可以创建一个模板类,我的所有对象都从我放置所有容器变量的地方继承。

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 */
};

这种方法非常适用于那些基本的Galaxy类型(GalaxyOne和GalaxyTwo)。但是,每当我尝试创建组合星系类型时,我都会产生各种歧义。

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

如果我在GalaxyOneTwo中定义的任何方法中使用solarSystems,我会产生歧义,因为它定义了三次,一次是通过GalaxyOne和GalaxyTwo的每个继承版本,第三次是通过GalaxyOneTwo。

我可以通过具体和使用

摆脱这种歧义

PrimitiveGalaxy :: solarSystems

每次引用正确的向量,但这是不可取的,因为它需要大量的额外输入和语法。

我考虑过在每个星系类型和原始星系之间使用友谊,但这需要类似的冗长写作水平。

我有一种预感,命名空间可能会简化我的代码编写,但我不确定如何定义命名空间,以便在为GalaxyOneTwo定义的命名空间内,对solarSystems的任何引用都是对 PrimitiveGalaxy :: solarSystems

编辑:

请注意,GalaxyOne和GalaxyTwo之间的唯一区别不在于solarSystems中包含的类的类型。由于每个班级都处理与银河系相关的不同数据,因此存在许多差异。因此,我创建了不同的类,这些类将具有不同的状态变量,用于那些状态变量的getter和setter以及计算和打印数据的方法。 solarSystems就是这个功能的一个例子,它给我带来了问题,因此这就是我在这里所描述的。当创建GalaxyOneTwo时,它将使用与GalaxyOne和GalaxyTwo相同的数据,因此我想继承它们的所有变量和方法。因为数据可以以不同的方式组合,我需要在GalaxyOneTwo中为它创建新的方法。这些是导致我使用继承的许多差异中的一些。话虽这么说,容器变量允许组合给我带来问题。在每个solarSystem类中,将有一个类似的向量,允许他们访问他们的行星,依此类推。

编辑:

对于一般专门针对我的设计理念的问题(与强调尝试解决当前设计尝试的问题相反),请参阅以下链接: Guidance in creating design for multiple-inheritance composite classes in c++

4 个答案:

答案 0 :(得分:3)

我认为您必须拥有一个class Galaxy,一个class SolarSystem等等。您的GalaxyOneGalaxyTwo SolarSystemOneSolarSystemTwo等。只是从这些类中提供的不同对象。

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;

如果我们无法使用这个简单的方法......让我们看看你的:

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

这里有三个私有向量:(使用using使其可以直接访问)

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

这是你需要的吗?让它受到保护? enter image description here

答案 1 :(得分:1)

也许你可以改变数据模型来实现基于组件的东西。每个组件可以包含您为不同类型提到的相同状态信息。你可以继承虚拟功能

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...

然后你可以继承不同类型的太阳系或星系来创建特殊情况并填写虚函数调用

class CoolSolarSystem : public SolarSystem
{
  // Special variables

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

然后,您可以使用指向特殊类型的指针填充不同基类型中的容器。如果虚函数调用处理足够的信息,你应该能够避免强制转换回特殊类型。

答案 2 :(得分:0)

你需要将'One'和'two'版本之间的常见内容抽象为一组'Common'基类:

class GalaxyCommon {
    // all the common stuff between GalaxyOne and GalaxyTwo including
    std::vector<SolarSystemCommon *> solarSystems;
    ...
};

class GalaxyOne : public virtual GalaxyCommon {
    // all the stuff that is unique to model One
};

class GalaxyTwo : public virtual GalaxyCommon {
    // all the stuff that is unique to model Two
};

class GalaxyOneTwo : public virtual GalaxyOne, public virtual GalaxyTwo {
    // should be complete
};

请注意使用virtual - 正确运行多重继承所需。

答案 3 :(得分:0)

所以,如果我理解你的问题:

  1. 有几种类型的星系在它们之间根本没有任何共同之处。
  2. 太阳能和行星系统也是如此。
  3. 您希望合并这些不同类型的集合,并且可以完全访问该类型的基础方法。
  4. 这听起来像是boost::any

    的情况

    所以现在你的银河系是:

    #include <list>
    #include <boost/any.hpp>
    typedef std::vector<boost::any> collection;
    class Galaxy
    {
       collection SolarSystems;
       public:
       ...methods...
    };
    class SolarSystem
    {
       collection PlanetarySystems;
       public:
       ...methods...
    };
    class PlanetarySystem
    {
       collection Moons;
       public:
       ...methods...
    };
    

    当然,这并没有真正做任何事情,但允许你对它们进行分组。为了做任何有用的事情(调用方法),你仍然需要检测类型,将其转换为该类型,然后运行你需要的代码。

    然而,这可以让你做的是将不同类型的对象组合在一起,我认为这是你在GalaxyOneTwo之后所做的。关于如何做到这一点more information