Как я могу избежать Алмаза Смерти при использовании множественного наследования? - PullRequest
52 голосов
/ 26 сентября 2008

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

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

Ответы [ 8 ]

64 голосов
/ 26 сентября 2008

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

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 {};
14 голосов
/ 26 сентября 2008

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

12 голосов
/ 26 сентября 2008

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

6 голосов
/ 27 сентября 2008

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

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

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

До:

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;
};

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

3 голосов
/ 15 июля 2012
class A {}; 
class B : public A {}; 
class C : public A {}; 
class D : public B, public C {};

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

1 голос
/ 26 сентября 2008

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

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

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

0 голосов
/ 12 февраля 2011

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

0 голосов
/ 26 сентября 2008

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...