Решите алмазную проблему без виртуального наследования - PullRequest
0 голосов
/ 14 октября 2019

Вот ситуация:

class A
{
public:
  A();
  void actionA();
private:
  int a;
};

class B1
  : public A
{
  ...
};

class B2
  : public A
{
  ...
};

class OrthogonalFeature
  : public A
{
  void orthogonalFeature();
};

class C1
  : public B1
  , public OrthogonalFeature
{
    ...
};

class C2
  : public B2
  , public OrthogonalFeature
{
    ...
};

class User
{
public:
  void run() {
    c.actionA();
    c.orthogonalFeature();
  }
private:
  MyNeededType c; // c is either C1 or C2
};

Класс User должен владеть объектом, который может выполнять actionA() и actionB().

Поэтому я мог бы захотеть создатькласс MyNeededType, который должен наследоваться от A и (B1 или B2), и я получаю проблему с алмазом (элемент данных a является неоднозначным). Обычный способ решить эту проблему - использовать виртуальное наследование. Однако для этого проекта я НЕ МОГУ использовать виртуальное наследование по причинам производительности (пожалуйста, просто примите это за чистую монету).

Итак, у меня проблема. Что может помочь, так это то, что класс User будет работать только с двумя имеющимися у меня классами: C1 и C2. Поэтому другой вариант, о котором я подумал, состоял в том, чтобы C1 и C2 наследовали от A и переопределили orthogonalFeature внутри этих производных классов вместо наследования OrthogonalFeature. Поэтому нет бриллианта. Вот как это будет выглядеть:

class C1
  : public B1
{
   public:
     void orthogonalFeature();
};

class C2
  : public B2
{
    void orthogonalFeature();
};

Мне нравится это решение, но как я могу тогда указать тип c в User? Мне кажется, что мне нужно создать тип, что означает: этот класс C1 или C2. Но я не знаю, что это существует в C ++?

Любая идея приветствуется, спасибо!

1 Ответ

0 голосов
/ 14 октября 2019

Насколько важен для вас быстрый доступ к членам данных A? В конечном счете, виртуальное наследование дает вам легкий доступ к A :: a, но за определенную плату почти ко всему остальному в B2 и B1.

Можете ли вы получить желаемый эффект от совместного использования A :: a без использованиябриллиант? В модели COM все классы интерфейса использовали простое наследование, унаследованное от интерфейса IUnknown, но затем классу-экземпляру пришлось реализовать конечную функцию «истинного базового класса», чтобы добраться до «зарегистрированного» IUnknown. Конечно, в этой модели не было элементов данных.

Вы могли бы тривиально настроить B1: public trueA и B2: public indirectA, а затем в вашей реализации иметь шаблонную оболочку, которая "активирует" косвенный A, чтобы найти trueA. Вы также можете сделать так, чтобы ваша оболочка реализации была производной от trueA, B1, B2 и зафиксировать в ней как экземпляры косвенного A, так и B1 и B2?

Но обратите внимание, что это полагается на то, что конструкторы для B1 и B2 не ссылаются на A, так как он еще не был инициализирован.

Можно грязно взломать конструктор косвенного А, чтобы «узнать», что он на самом деле создан в С: В2, и вывести С: В1: А или С: А, и вы, вероятно, сможете получить это достаточно чисто, используя шаблонысдвинуть определения, возможно, с небольшим CRTP?

...