шаблоны — Решение проекта, включающего множественное наследование и составные классы в Stack Overflow

Я боролся с этой проблемой дизайна в течение некоторого времени. Я приложу все усилия, чтобы объяснить, что я пытаюсь сделать, и различные подходы, которые я видел, что я пытаюсь и почему.

Я работаю в научной вычислительной среде, где я неоднократно имею дело с одними и теми же объектами. Представьте себе галактику, которая содержит солнечные системы, каждая солнечная система содержит планетные системы, а каждая планетная система содержит спутники. С этой целью я рассматриваю ситуацию как ситуацию «имеет», и поэтому я использовал композицию, чтобы дать галактике доступ к ее солнечным системам, а каждой солнечной системе — к планетным системам, которые имеют доступ к своим лунам: каждой категории быть своим собственным классом.

Часто бывает, что различные проблемы, над которыми я работаю, содержат разные типы данных об этих объектах. И, когда становятся доступны разные типы данных, я могу делать определенные вещи с моими объектами. Итак, когда у меня есть доступный тип данных 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>{
/* … */
};

Я получаю неоднозначность, если использую solarSystems в любом методе, определенном в GalaxyOneTwo, потому что он определяется три раза, один раз через каждую унаследованную версию от GalaxyOne и GalaxyTwo и третий раз через GalaxyOneTwo.

Я могу избавиться от этой двусмысленности, будучи конкретным и используя

PrimitiveGalaxy :: SolarSystems

каждый раз ссылаться на правильный вектор, но это нежелательно, потому что требует МНОГО дополнительной типизации и синтаксиса.

Я рассмотрел использование дружбы между каждым типом галактики и примитивной галактикой, но это требует подобного уровня многословного письма.

У меня есть предположение, что пространства имен могут упростить мое написание кода, но я не уверен, как определить пространство имен так, чтобы в пределах пространства имен, определенного для GalaxyOneTwo, любая ссылка на solarSystems была ссылкой на
PrimitiveGalaxy :: SolarSystems

Редактировать:

Обратите внимание, что единственная разница между GalaxyOne и GalaxyTwo — это НЕ тип класса, содержащегося в solarSystems. Есть много различий, потому что каждый класс имеет дело с различными данными, относящимися к галактике. Таким образом, я создаю разные классы, которые будут иметь разные переменные состояния, методы получения и установки для этих переменных состояния и методы для вычисления и печати данных. SolarSystems — это пример функции, которая доставляет мне проблемы, поэтому я описал ее здесь. Когда GalaxyOneTwo создан, он будет использовать те же данные, которые используются для GalaxyOne и для GalaxyTwo, и поэтому я хочу наследовать все их переменные и методы. И поскольку данные могут комбинироваться различными способами, мне нужно создать новые методы для этого в GalaxyOneTwo. Вот некоторые из многих различий, которые побуждают меня использовать наследование. Тем не менее, именно контейнерные переменные, которые учитывают композицию, доставляют мне проблемы. Внутри каждого класса solarSystem будет один и тот же вектор, дающий им доступ к их планетам, и так далее, и тому подобное.

Редактировать:

Для вопроса, специально посвященного моей философии дизайна здесь в целом (в отличие от этих вопросов акцент на попытке решить мою текущую попытку проектирования), смотрите следующую ссылку:
Руководство по созданию дизайна для составных классов с множественным наследованием в C ++

7

Решение

Возможно, вы могли бы изменить модели данных, чтобы реализовать что-то на основе компонентов. Каждый компонент может содержать ту же информацию о состоянии, которую вы упомянули для разных типов. И вы могли бы наследовать виртуальные функции

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

Затем вы можете заполнить контейнеры внутри разных базовых типов указателями на ваши специальные типы. Вы сможете избежать возврата к специальному типу, если вызовы виртуальной функции обрабатывают достаточно этой информации.

1

Другие решения

Я думаю, что вы должны иметь один class Galaxy, один class SolarSystemи тд GalaxyOne, GalaxyTwo SolarSystemOne, SolarSystemTwo и т. д. — это только разные объекты, полученные из этих классов.

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;

Это то, что тебе надо? Сделать это защищенным?
введите описание изображения здесь

3

Вам необходимо абстрагировать общий материал между версиями «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 — требуется, чтобы правильно работало множественное наследование.

0

Так что, если я правильно понял ваш вопрос:

  1. Есть несколько типов галактик, которые могут не иметь ничего общего между ними.
  2. То же самое касается солнечных и планетных систем.
  3. Вы хотели бы объединить коллекции этих различных типов и при этом иметь полный доступ к базовым методам для этого типа.

Это звучит как случай для повышение :: любой

Итак, теперь ваша Галактика:

#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. Вот Дополнительная информация о том, как вы можете сделать это.

0