Подтвердить что ты не робот

Наследование алмазов

Предположим, что классы D и E и F наследуют от базового класса B, а класс C наследует от D и E.

(i) Сколько экземпляров класса B отображается в классе C?

(ii) Как использование виртуального наследования изменит этот сценарий? объяснять ваш ответ.

(iii) Как Java избегает необходимости множественного наследования для многих из     ситуации, когда в С++ можно использовать множественное наследование?

Вот некоторые из моих текущих идей, но я ни в коем случае не специалист по С++!

(i) Если C наследует от D и E, которые являются подклассами B, то D и E технически являются копиями их суперкласса? Тогда, если C наследует от D и E, это означает, что в C. есть 2 копии B.

(ii) Использование virtual несколько похоже на использование Abstract в Java (я думаю). Теперь, учитывая это, это означало бы, что не было бы нескольких копий B в C, поскольку инстанцирование было бы каскадировано до уровня, в котором это необходимо. Я не уверен, как изложить свое объяснение, но сказать, что B имеет функцию print(), которая печатает "i am B", а C переопределяет эту функцию, помещая отпечатки "i am C". Если вы вызываете print() на C без виртуального, вы в конечном итоге печатаете "i am B", использование виртуального означает, что оно будет печатать "i am C".

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

4b9b3361

Ответ 1

(i) и (iii) являются правильными. По моему опыту, в большинстве случаев, в большинстве случаев на С++, когда я использовал множественное наследование, это было потому, что базами были интерфейсы (концепция, которая не поддерживает ключевые слова на С++, но это концепция, которую вы можете выполнить в любом случае).

Первое предложение (ii) верно, однако в вашем втором предложении речь идет о виртуальных функциях, которые полностью отличаются от виртуального наследования. Виртуальное наследование означает, что существует только одна копия B, а D и E обе имеют ту же копию, что и их база. Нет никакой разницы в функциях, но разница происходит в терминах переменных-членов (и базовых классов) B.

Если есть функция, которая выводит B переменную-член foo; то в случае (ii) эта функция всегда печатает одно и то же значение, потому что есть только один foo, но в случае (i) вызов этой функции из базового класса D может напечатать другое значение для вызова его из E базовый класс.

Термин "наследование бриллианта" обертывает все это двумя словами, которые служат хорошей мнемонической:)

Ответ 2

Вы, кажется, в основном пришли к правильным ответам, хотя рассуждения нуждаются в работе. Ключевой проблемой здесь является вопрос о том, "как выложить память экземпляра C, если он дважды наследует один и тот же базовый класс?"

i) Существует 2 копии базового класса B в макете памяти для объекта типа C. Приведенный пример представляет собой случай "наследования алмазов", поскольку, когда вы вытягиваете дерево зависимости/наследования, вы по существу нарисуйте алмаз. "Проблема" с наследованием бриллиантов заключается в том, чтобы спросить, как выложить объект в памяти. С++ пошел с двумя подходами: быстрым, дублирующим элементы данных и более медленным, "виртуальным наследованием". Причиной принятия не виртуального подхода является то, что если вы наследуете класс, у которого нет данных (каким будет интерфейс в Java), тогда нет проблем с "дублированием элементов данных", потому что они не существуют ( см. мою заметку внизу). Также рекомендуется использовать не виртуальное наследование, если ваш план должен использовать только одно наследование.

ii) Если у вас есть virtual class C, то это способ сказать на языке С++, что вы хотели бы, чтобы компилятор выполнял акты героизма, чтобы убедиться, что существует только одна копия любых/всех базовых классов в расположение памяти вашего производного класса; Я считаю, что это также приводит к небольшому результативному результату. Если вы теперь используете каких-либо членов "B" из экземпляра "C", он всегда будет ссылаться на одно и то же место в памяти. Обратите внимание, что виртуальное наследование не влияет на ваши виртуальные функции.

Кроме того, это также совершенно не связано с концепцией абстрактного класса. Для создания абстрактного класса в С++ задайте любое объявление метода = 0, как в void foo() = 0;; сделать это для любого метода (включая деструктор) достаточно, чтобы сделать весь класс абстрактным.

iii) Java прямо запрещает это. В Java существует только одно наследование и возможность реализовать любое количество интерфейсов. В то время как интерфейсы предоставляют вам отношения "is-a" и возможность иметь виртуальные функции, они неявно избегают проблем, которые С++ имеет с макетами данных и наложением алмазов, поскольку интерфейс не может добавлять какие-либо элементы данных, ipso facto: нет путаница в том, как разрешить любое местоположение члена данных.

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