C++. Множинна спадковість. Дублювання класу та його даних. Проблема ромба

Множинна спадковість. Дублювання класу та його даних. Проблема ромба


Зміст


Пошук на інших ресурсах:

1. Випадок неоднозначності у класі при множинній спадковості
Неоднозначність при множинній спадковості може виникнути у випадках, коли звертання до елементу базового класу може трактуватись по різному (двома і більше способами). У цьому випадку компілятор видає відповідне повідомлення.
Наприклад, на рисунку 1 зображено фрагмент коду та відповідна ієрархія класів які призводять до неоднозначності та дублювання даних.
C++. Множинна спадковість. Проблема ромба
Рисунок 1. Ієрархія класів, при якій звертання елементів класу A може викликати неоднозначне трактування а також дублювання даних класу
При використанні коду на рисунку 1 дані класу A дублюються, тобто використовуються два рази. Також звертання до protected-змінної з іменем value класу A, наприклад,
value
видасть помилку компіляції: неоднозначний виклик (ambiguous call).
Щоб виправити цю помилку потрібно чітко вказати, через який клас відбувається виклик. Тобто вказати наступний рядок у функції D::GetValue()
return B::value;
або
return C::value;

 

2. Уникнення дублювання коду та неоднозначності. Оголошення класу віртуальним
Щоб уникнути дублювання коду та даних класу A (дивіться рисунок 1), потрібно оголосити клас A віртуальним у класах B, C:
class B : public virtual  // ключове слово virtual робить одну копію класу A
{
  ...
}

...

class C : virtual public  // ключове слово virtual робить одну копію класу A
{
  ...
}
Тоді ієрархія класів, описана на рисунку 1, може мати інший вигляд, як показано на рисунку 2
C++. Множинна спадковість. Оголошення класу як віртуального
Рисунок 2. Ієрархія класів, якщо клас A успадкований в класах B та C як virtual
Також розв’язується проблема неоднозначності. Тепер у класі D можна спокійно звертатись до імені
A::value
або просто
value
Помилки вже не буде.

 

3. Приклад коду, що призводить до неоднозначності виклику
У прикладі наводиться код програми, що реалізує ієрархію класів, зображену на рисунку 1.  У класі D в методі Show() спроба звертання до імені a виникличе помилку неоднозначного виклику. Щоб поправити ситуацію потрібно явно вказувати базові класи
B::a
або
C::a
Також у цьому випадку відбувається дублювання коду та даних класу A.
#include <iostream>
using namespace std;

// Проблема дублювання класу при множинному успадкуванні
class A
{
protected:
  int a;

public:
  A(int a) : a(a) { }

  void Show()
  {
  }
};

class B : public A
{
protected:
  int b;

public:
  B(int a, int b) : A(a), b(b) { }
};

class C : public A
{
protected:
  int c;

public:
  C(int a, int c) : A(a), c(c) { }
};

class D : public B, public C
{
public:
  D(int a, int b, int c) : B(a, b), C(a, c) { }

  void Show()
  {
    // тут помилка, неоднозначний виклик a
    // cout << a << endl;   - потрібно явно вказувати клас

    cout << B::a << endl;  // тут працює, тому що клас B вказаний явно
    cout << C::a << endl;  // тут працює, тому що клас C вказаний явно
  }
};

int main()
{
  D obj(1, 2, 3);
  obj.Show();
}

 

4. Приклад коду, що вирішує проблему неоднозначного виклику та дублювання даних класу. Оголошення класу віртуальним
У прикладі демонструється програма яка реалізує програмний код, що зображений на рисунку 2.
// У даному коді клас A успадковується як віртуальний.
// Проблеми неоднозначності у класі D вже немає.
class A
{
public:
  int value;

public:
  A(int value) : value(value) { }
  A() : value(0) { }
};

class B : public virtual  // ключове слово virtual робить одну копію класу A
{
public:
  B(int value) : A(value) { }
};

class C : virtual public// ключове слово virtual робить одну копію класу A
{
public:
  C(int value) : A(value) { }
};

class D : public B, public C
{
public:
  // Тут немає неоднозначності, оскільки код класу A не дублюється (завдяки virtual-успадкуванню)
  int Value() { return value; }  // працює

  D(int value) : A(value), B(value), C(value) { }
  D() : B(0), C(0) { }
};

void main()
{
  A objA(5);
  D objD(10);
  cout << objD.Value() << endl;
}

 


Споріднені теми