наследование — ограничение совместного использования объектов виртуальными базовыми классами в переполнении стека

Я знаю о физическом и виртуальном наследовании в C ++. Тем не менее, я хотел бы знать, может ли эта модель быть достигнута с помощью каких-либо шаблонов дизайна или трюков.

Классы E и F и их базы не должны быть изменены. Представьте, что они происходят из внешней библиотеки.

Все ниже E и F открыто. Было бы хорошо ввести промежуточные вспомогательные классы, функции, не являющиеся членами, шаблоны … все, что могло бы реализовать это:

           BaseClass
/       \
/         \
A           A
/ \         / \
/   \       /   \
B     C     B     D
\   /       \   /
\ /         \ /
E           F
\         /
\       /
\     /
FinalClass

Обратите внимание, что E и F не должны совместно использовать A. FinalClass действительно должен содержать 2 A.
Моя проблема в том, что создание E или F потребовало бы, чтобы B, C и D наследовали A виртуально … однако, если A является виртуальным базовым классом, то компилятор будет создавать только один объект A в FinalClass вместо двух разных из них.

Итак, я думаю, что многие из вас рекомендуют композицию вместо наследования здесь. Тем не менее, композиция моделирует отношение «имеет» здесь, а не «есть».
В объяснении я хотел бы, чтобы FinalClass действительно вел себя как E или F, включая возможность преобразования его в эти классы.

2

Решение

Такая компоновка невозможна в C ++, даже с дополнительным классом помощи.

Тот факт, что B наследуется от A, и вы хотите разделить наследование A с C с одной стороны и с D с другой, требует, чтобы B, C и D фактически наследовали от A. Но тогда A будет совместно использоваться в обеих ветвях. вашего бриллианта.

Какие могут быть альтернативы?

  • Если вам удастся нарушить совместное использование А между левой и правой ветвями ваших бриллиантов, вы также нарушите совместное использование общей базы.

  • Если бы вы внедрили некоторые промежуточные классы А1, А2 для реализации левого и правого общего ресурса в ваших ветвях, вы бы застряли на том факте, что оба B должны наследовать либо тот, либо другой

  • Единственным выходом может быть наличие дублирующего класса для B.

Это последнее решение не соответствует вашему требованию, но будет выглядеть следующим образом:

struct Base { int x; };
struct A : public virtual Base { int a; };
struct AC : public A{};  // synonym
struct B : public virtual A { int b; };
struct BC : public virtual AC { int b;  }; // !! clone !!
struct C : public virtual AC { int c; };
struct D : public virtual A { int d; };
struct E : public BC, C { int e; };
struct F : public B, D { int f; };
struct Final : public E, F { };

А вот и доступ к членам:

Final f;
f.x = 2;    // unambiguous:  there's onely one of it
f.f = 1;
f.e = 2;
f.d = 3;
f.c = 4;
//f.b = 5;   // ambiguous:  there are two of it
f.E::b = 5;  // successful desambiguation
f.F::b = 6;  // successfuldesambiguation
//f.a = 7;   // ambiguous:  there are two of it
f.E::a = 7;  // successful desambiguation
f.F::a = 8;  // successful desambiguation

Возвращаясь к формулировке вашей проблемы: вы говорите, что не можете вмешиваться выше E и F. В этом случае ваши возможности очень ограничены:

  • вы публично наследуете
  • Вы в частном порядке наследуете через некоторые промежуточные классы

Но эффект совместного использования будет таким же, как показано в следующем коде (поверх приведенного выше):

class FI : private F  // make F private
{ public:
void set_xa(int u, int v) { x = u; a= v; }
void show_xa() { cout << "x:" << x << " a:" << a << endl; }
};
class EI : private E  // make E private
{
public:
void set_xa(int u, int v) { x = u; a = v; }
void show_xa() { cout << "x:" << x << " a:" << a << endl; }
};

struct Final3 : public EI, public FI { };

Final3 h;
h.EI::set_xa(3, 4);
h.FI::set_xa(5, 6);
h.EI::show_xa();
h.FI::show_xa();
// the shared virtually inherited memebers are still shared !

С наследованием вы полностью связаны с дизайном выше E и F, на который вы не имеете права влиять.

Итак, первые вопросы будут такими:

  • Вы не можете изменить этот дизайн в конце концов (то есть, клонирование одного B)?
  • не было бы неприемлемо иметь разделение между двумя ветвями (может быть, есть веская причина в конце концов)?

Если вы ответите «нет» на оба вопроса, вам придется заняться составлением и реализовать шаблон прокси, где ваш составной объект является прокси для обоих ваших компонентов.

2

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


По вопросам рекламы [email protected]