Мою ошибку компилятора алмазного наследования невозможно решить? - PullRequest
0 голосов
/ 02 июня 2018

Структура

Я создал проблему наследования алмазов.Выглядит это так:

inheritance diagram

Мне показалось, что я достаточно хорошо понимал виртуальное наследование, однако теперь мне кажется, что я его немного неправильно понял.

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

  • Однако теперь я думаю, что это понимание того, как компилятор реализует наследование, неверно.

У меня есть 2 ромбаобразцы наследования в моей иерархии наследования.Они помечены с использованием прилагаемых заметок.

Я также добавил несколько заметок, чтобы показать, где я пытался поместить virtual для устранения ошибок компилятора, но в результате возникла другая ошибка компилятора.В записке кратко описана проблема.(См. Последний раздел этого вопроса, если вы заинтересованы)

Использование

Предполагаемое использование - std::list<GUIObject*> создано.Все объекты графического интерфейса должны быть в состоянии Draw и ProcessEvent.Не все объекты графического интерфейса будут содержать контейнер, содержащийся внутри SingleLineBuffer.

Buffer и FileBuffer наследуются от SingleLineBuffer, чтобы изменить поведение контейнера внутри SingleLineBuffer.(FileBuffer фактически только добавил новые функции ввода-вывода файлов.)

Можно создать экземпляр одного из буферов, однако я не знаю, в каком контексте я работаю.

Oneне может создать экземпляр какого-либо из абстрактных GUI* классов.Думая об этом, вероятно, должен быть дополнительный абстрактный базовый класс ниже GUIMultilineTextEntry, который наследуется от FileBuffer.

Фактические объекты, для которых пользователь может создать экземпляр: Label, Inputbox иTextbox.Я намерен добавить больше в будущем, например, многострочный ярлык.Это должно было бы наследоваться от базового класса, который наследовал от Buffer и GUITextObject, вероятно.

Эта структура наследования быстро стала довольно сложной.Я написал это по ходу дела, руководствуясь тем, что мой код велел мне делать.Например, я написал класс Textbox, а затем сказал, что «контейнер в Textbox по сути такой же, как контейнер в Label, поэтому они должны наследоваться от общего объекта».Разница была в том, что в Textbox есть файл IO, продиктован дополнительный шаг наследования, а в Textbox может содержаться символ новой строки в контейнере, поэтому здесь также продиктован дополнительный шаг наследования.

Вопросы

  • Можно ли решить эту проблему наследования?

  • Какие классы должны наследоватьсяпрактически из каких других классов.

Мои попытки

  • Нет виртуального наследования

Ошибка компилятора:(несколько версий)

error: request for member ‘Size’ is ambiguous
 status_text << "Save: " << static_cast<Textbox*>(current_window._guiobject_map_.at("textbox"))->GetFilename() << ", " << static_cast<Textbox*>(current_window._guiobject_map_.at("textbox"))->Size() << " bytes";

Size определено в SingleLineBuffer.Это не виртуальная функция, так как контейнер существует только в SingleLineBuffer, и поэтому Size написано для правильной работы как Buffer, так и FileBuffer.

  • Бриллиант разрыва 1: Поместите virtual между GUITextObject и GUITextEntry, чтобы Size не "вытягивался через GUITextObject" до того, как он будет переопределен в буфере.(Синяя метка)

Ошибка компилятора (1):

error: no matching function for call to ‘GUITextObject::GUITextObject()’

Я могу это исправить, вызвав требуемый конструктор из GUIMultilineTextEntry.Я не понимаю, зачем это нужно.( Второй вопрос ) Исправление показано ниже:

GUIMultilineTextEntry(const FontTextureManager& ftm, const int width, const int height)
    : GUITextEntry(ftm, width, height)
    , GUITextObject(ftm, width, height) // also call the constructor for the class which was inherited virtual in the previous step
{ ...

Такое же исправление необходимо в Inputbox и Textbox.

Однако это приводит к дальнейшей ошибке

error: cannot convert from pointer to base class ‘GUIObject’ to pointer to derived class ‘Textbox’ via virtual base ‘GUITextObject’
 static_cast<Textbox*>(current_window._guiobject_map_.at("textbox"))->SetFilename(filename);

Полагаю, я мог бы решить эту проблему, используя dynamic_cast вместо static_cast, однако я не уверен, что это хорошая идея, поскольку я слышал, что динамическое приведение может значительно замедлить код.

  • Бриллиант 1, вторая попытка:

Я предпринял вторую попытку решения проблемы путем виртуального наследования от Buffer до SingleLineBuffer.(См. Красную точку) Однако, когда я сделал это, ошибка компилятора изменилась на

error: no unique final overrider for ‘virtual void SingleLineBuffer::SetText(const string&)’ in ‘Textbox’

Я предполагаю, что это эквивалентно тому, что компилятор сказал мне: «Вы переопределили некоторые функции в Buffer путем наследования, но вы унаследовали виртуально,и функции, которые вы переопределили, также присутствуют в производном классе с помощью не виртуального наследования, поэтому я не знаю, какой из них должен иметь точность », - но это действительно предположение.

  • Я пыталсяаналогичные вещи, чтобы сломать diamond 2, но с похожими ошибками компилятора.

Поскольку это теперь довольно длинный вопрос, я не буду перечислять все детали этой попытки здесь.

1 Ответ

0 голосов
/ 05 июня 2018

Ответ на титульный вопрос: нет .(Обычно MCVE идет в вопросе, но я предполагаю, что это действительно ответ.) Что касается подробных вопросов:

  • Наследование должно быть virtual, если оно напрямую от общего предка («вершина алмаза»), для которого вам нужна только одна копия в законченном объекте.(Так что здесь вам нужно 4 virtual с, просто посчитав сходящиеся стрелки.)
  • Вам нужно вызвать конструктор каждую виртуальную базу в каждую конкретный класс, потому что самый производный класс инициализирует их напрямую.
  • Вы действительно не можете использовать static_cast из (или, как говорится,) виртуальногобазовый, так как расположение классов варьируется между экземплярами (из-за отличия других базовых классов).Однако стоимость одной dynamic_cast за операцию с графическим интерфейсом, безусловно, неизмерима.
  • Ваш анализ ошибок «уникального окончательного переопределения», вероятно, правильный, но ответ больше cowbell virtual.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...