Я знаю о физическом и виртуальном наследовании в 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, включая возможность преобразования его в эти классы.
Такая компоновка невозможна в 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, на который вы не имеете права влиять.
Итак, первые вопросы будут такими:
Если вы ответите «нет» на оба вопроса, вам придется заняться составлением и реализовать шаблон прокси, где ваш составной объект является прокси для обоих ваших компонентов.