Структура
Я создал проблему наследования алмазов.Выглядит это так:
![inheritance diagram](https://i.stack.imgur.com/cblnM.png)
Мне показалось, что я достаточно хорошо понимал виртуальное наследование, однако теперь мне кажется, что я его немного неправильно понял.
Насколько я понимаю, виртуальное наследование говорит компилятору игнорировать любые данные или функции-члены, которые дважды появляются с одним и тем же именем в результате паттерна наследования алмазов, таким образом, только «не виртуальные» унаследованные компоненты будутсодержаться в производном классе.
Однако теперь я думаю, что это понимание того, как компилятор реализует наследование, неверно.
У меня есть 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, но с похожими ошибками компилятора.
Поскольку это теперь довольно длинный вопрос, я не буду перечислять все детали этой попытки здесь.