Состав
Я создал проблему наследования алмаза. Это выглядит так
Я думал, что достаточно хорошо понял виртуальное наследование, однако теперь я думаю, что я немного не понял его.
Насколько я понимаю, виртуальное наследование указывает компилятору игнорировать любые данные или функции-члены, которые появляются дважды с одним и тем же именем в результате шаблона наследования ромба, поэтому в производном классе будут содержаться только не виртуальные унаследованные компоненты.
Однако теперь я считаю, что такое понимание того, как компилятор реализует наследование, неверно.
У меня есть 2 ромбовидных шаблона наследования в моей иерархии наследования. Они отмечены с помощью прилагаемых примечаний.
Я также добавил несколько примечаний, чтобы показать, где я пытался поместить virtual
для устранения ошибок компилятора, но это привело к другой ошибке компилятора. В записке кратко описывается, в чем заключалась проблема. (См. Последний раздел этого вопроса, если вам интересно)
Использование
Предполагаемое использование - std::list<GUIObject*>
создано. Все объекты графического интерфейса должны поддерживать Draw
и ProcessEvent
. Не все объекты графического интерфейса будут содержать контейнер, содержащийся внутри SingleLineBuffer
.
Buffer
и FileBuffer
наследуются от SingleLineBuffer
, чтобы изменить поведение контейнера внутри SingleLineBuffer
. (FileBuffer
фактически только добавил новые функции ввода-вывода файлов.)
Можно создать экземпляр одного из буферов, но я этого не делаю в контексте, с которым работаю.
Невозможно создать экземпляр любого из абстрактных GUI*
классов. Если подумать, то, вероятно, должен быть дополнительный абстрактный базовый класс ниже GUIMultilineTextEntry
, который наследуется от FileBuffer
.
Фактические объекты, экземпляры которых пользователь может создать, - это Label
, Inputbox
и Textbox
. Я собираюсь добавить больше в будущем, например, многострочный ярлык. Вероятно, это должно быть унаследовано от базового класса, унаследованного от Buffer
и GUITextObject
.
Эта структура наследования быстро стала довольно сложной. Я писал его по ходу дела, руководствуясь тем, что мой код велел мне делать. Например, я написал класс Textbox, а затем сказал, что контейнер в Textbox по сути такой же, как контейнер в Label, поэтому они должны наследовать от общего объекта. Разница заключалась в том, что текстовое поле имеет файловый ввод-вывод, продиктован дополнительный шаг наследования, а текстовое поле может содержать символ новой строки в контейнере, поэтому здесь также продиктован дополнительный шаг наследования.
Вопросов
Можно ли решить эту проблему наследования?
Какие классы должны виртуально наследовать от других классов.
Мои попытки
- Без виртуального наследования
Ошибка компилятора: (несколько версий)
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’
Я предполагаю, что это эквивалентно тому, что компилятор говорит мне, что вы переопределили некоторые функции в буфере путем наследования, но вы унаследовали виртуально, и функции, которые вы переопределили, также присутствуют в производном классе через невиртуальное наследование, поэтому я не знаю какой из них следует принимать во внимание - но это на самом деле предположение.
- Я пробовал похожие вещи, чтобы сломать ромб 2, но столкнулся с аналогичными ошибками компилятора.
Поскольку это теперь довольно длинный вопрос, я не буду перечислять здесь все подробности этой попытки.
GUITextObject
в качестве своегоSingleLineBuffer
? - person Galik   schedule 02.06.2018SingleLineBuffer
методы? - person Galik   schedule 02.06.2018Label
вызывать методы, унаследованные отSingleLineBuffer
? - person Galik   schedule 02.06.2018Insert
,Delete
,Size
,GetText
,SetText
и т. Д. - person FreelanceConsultant   schedule 02.06.2018GUITextObject
- единственный текстовый компонент графического интерфейса, который должен наследовать отSingleLineBuffer
, и чтоSingleLineBuffer
должен быть чисто виртуальным интерфейсом. - person Galik   schedule 02.06.2018FileBuffer
следует не наследовать отBuffer
, а быть отдельным типом реализации. После этого я, вероятно, использовал бы композицию, чтобы текстовые объекты графического интерфейса содержали соответствующую производную буфера в качестве средства реализацииSingleLineBuffer
части своего интерфейса. . - person Galik   schedule 02.06.2018SingleLineBuffer
, в какой-то момент будет вынуждено предоставить реализацию. Затем вы можете решить, собираетесь ли вы использоватьBuffer
илиFileBuffer
в качестве элементов данных для выполнения этой реализации (или, возможно, переключаться между ними во время выполнения ???). - person Galik   schedule 02.06.2018GUI
классов. - person Davis Herring   schedule 02.06.2018virtual
? Я принял их за ограничения, что усугубило здесь значительную путаницу. - person Davis Herring   schedule 05.06.2018