объектная ориентация, наследование и композиция в c ++ - PullRequest
1 голос
/ 19 августа 2011

Существует 4 класса: A, B, C и D. Класс A "HAS A" B и класс C "HAS A" D. Как это происходит, C "IS A" A, а D "IS A «Б. Как лучше всего моделировать эти отношения в C ++?

B, который имеет A, это тот же D, что и C.

Пример: у здания есть вход, а у дома - дверь. Дом - это здание, а дверь - это вход.

Ответы [ 4 ]

3 голосов
/ 19 августа 2011
class B {
};
class A {
  B b;  // A "has a" B
};
class C : public A  { // C "is a" A
  D d;  // C "has a" D
};
class D : public B { // D "is a" B
};
1 голос
/ 19 августа 2011

У здания есть вход, а у дома - дверь. Дом - это здание, а дверь - это вход.

struct Entrance        {};
struct Door : Entrance {};

struct Building {
   virtual Entrance& getEntrance() = 0;
};

struct House : Building {
   virtual Entrance& getEntrance() {
      return entrance;
   }

   private:
   Door entrance;
};

typedef Building A;
typedef Entrance B;
typedef House    C;
typedef Door     D;

Это ближайшее приближение, использующее полиморфизм. В частности, обратите внимание, что экземпляр Building не может быть создан, потому что он «абстрактный».

В противном случае вы не можете иметь A has-a B и C has-a D, и имеют как B, так и D быть тем же объектом.


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

struct Entrance        {};
struct Door : Entrance {};

struct Building {
   Building(Entrance& e) : e(e) {}
   Entrance& e;
};

struct House : Building {
   House() : Building(e) {}
   Door e;
};

typedef Building A;
typedef Entrance B;
typedef House    C;
typedef Door     D;

Поскольку конструктор House Building вызывается до его создания Door, в лучшем случае сомнительно (а в худшем не определено. Мне придется поискать его) .


Люк прав в том, что это моделирование некорректно.

1 голос
/ 19 августа 2011

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

struct Entrance {};
struct Door : public Entrance {};

struct Building
{
    Entrance * entrance; // raw pointer just for the sake of clarity
};

struct House : public Building
{
    // Building::entrance must always be a door
};

Этого можно добиться, используя методы доступа (getter / setter) к entrance и проверяя тип, выбрасывая исключение, если вход не дверь, но это нарушит принцип замены Лискова : вы не сможет манипулировать House с, как если бы они были Building с:

struct Drawbridge : public Entrance {};

House house;
Drawbridge bridge;

Building & building = house;
building.setEntrance(bridge); 
// Oups, I'm trying to install a drawbridge on my house!

Некоторые библиотеки выполняют такого рода ограничения (например, ReadOnlyCollection<T> выдает исключение при попытке изменить его содержимое), но, на мой взгляд, это не чистый дизайн. Если интерфейс коллекции утверждает, что я могу добавить элементы в коллекцию, то коллекция только для чтения - это , а не коллекция (поскольку она не поддерживает добавление элементов).

Здесь можно применить те же аргументы: a House - это , а не a Building, поскольку он не может содержать все виды Entrance s.

0 голосов
/ 19 августа 2011

C расширяет A, а D расширяет B. A содержит объект B в своем определении, а C содержит объект D. [Has-a] [Is-a]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...