Как я могу избежать Угрозы Смерти при использовании множественного наследования?
-
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, и у вас нет бриллианта.