Как я могу избежать Угрозы Смерти при использовании множественного наследования?

StackOverflow https://stackoverflow.com/questions/137282

  •  02-07-2019
  •  | 
  •  

Вопрос

http://en.wikipedia.org/wiki/Diamond_problem

Я знаю, что это значит, но какие шаги я могу предпринять, чтобы избежать этого?

Это было полезно?

Решение

Практический пример:

class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};

Обратите внимание, как класс D наследуется как от B , так и от C.Но оба B и C наследуются от A.Это приведет к тому, что 2 копии класса A будут включены в vtable.

Чтобы решить эту проблему, нам нужно виртуальное наследование.Это класс А, который должен быть виртуально унаследован.Итак, это устранит проблему:

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

Другие советы

виртуальное наследование.Вот для чего он существует.

Я бы предпочел использовать только множественное наследование интерфейсов.Хотя множественное наследование классов иногда привлекательно, оно также может сбивать с толку и причинять боль, если вы регулярно полагаетесь на него.

Наследование - это сильное, непобедимое оружие.Используйте его только тогда, когда вам это действительно нужно.В прошлом наследование алмазов было признаком того, что я зашел слишком далеко с классификацией, заявив, что пользователь является "сотрудником", но он также является "слушателем виджетов", а также ...

В таких случаях легко столкнуться с проблемами множественного наследования.

Я решил их, используя композицию и указатели обратно на владельца:

До того, как:

class Employee : public WidgetListener, public LectureAttendee
{
public:
     Employee(int x, int y)
         WidgetListener(x), LectureAttendee(y)
     {}
};

После:

class Employee
{
public:
     Employee(int x, int y)
         : listener(this, x), attendee(this, y)
     {}

     WidgetListener listener;
     LectureAttendee attendee;
};

Да, права доступа разные, но если вам может сойти с рук такой подход, без дублирования кода, это лучше, потому что он менее мощный.(Вы можете сэкономить электроэнергию на случай, если у вас нет альтернативы.)

class A {}; 
class B : public A {}; 
class C : public A {}; 
class D : public B, public C {};

При этом атрибуты класса A дважды повторяются в классе D, что увеличивает использование памяти...Таким образом, чтобы сэкономить память, мы создаем виртуальный атрибут для всех унаследованных атрибутов класса A, которые хранятся в виртуальной таблице.

Что ж, самое замечательное в Страшном Бриллианте то, что его появление является ошибкой.Лучший способ избежать этого - заранее выяснить структуру вашего наследования.Например, в одном проекте, над которым я работаю, есть Зрители и Редакторы.Редакторы являются логическими подклассами Viewers, но поскольку все Viewers являются подклассами - TextViewer, ImageViewer и т.д., Editor не является производным от Viewer, что позволяет конечным классам TextEditor, ImageEditor избегать ромба.

В тех случаях, когда ромбовидного наследования избежать невозможно, используется виртуальное наследование.Однако самое большое предостережение, связанное с виртуальными базами, заключается в том, что конструктор для виртуальной базы должен вызывается наиболее производным классом, что означает, что производный класс практически не имеет контроля над параметрами конструктора.Кроме того, наличие виртуальной базы, как правило, приводит к снижению производительности / пространства при приведении по цепочке, хотя я не думаю, что существует большой штраф за большее, чем первое.

Кроме того, вы всегда можете использовать бриллиант, если четко определитесь с тем, какую базу вы хотите использовать.Иногда это единственный выход.

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

Если нет, используйте виртуальные функции / интерфейсы.

Используйте наследование путем делегирования.Тогда оба класса будут указывать на базу A, но должны будут реализовать методы, которые перенаправляют на A.Это имеет побочный эффект превращения защищенных членов A в "частных" членов в B, C и D, но теперь вам не нужен virtual, и у вас нет бриллианта.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top