Structure
I have created a diamond inheritance problem. It looks like this
I thought I understood virtual inheritance fairly well, however I now think that I have slightly missunderstood it.
It was my understanding that virtual inheritance tells the compiler to ignore any member data or functions which appear twice with the same name as a result of a diamond inheritance pattern, thus only the "non virtual" inherited components would be contained in the derived class.
However I now think this understanding of how the compiler implements inheritance is wrong.
I have 2 diamond inheritance patterns in my inheritance hierarchy. They are marked using the notes included.
I have also added some notes to show where I attempted to put virtual
to resolve the compiler errors, but a different compiler error resulted. The note briefly describes what the problem was. (See the final section of this question if you are interested)
Useage
The intended usage is a std::list<GUIObject*>
is created. All gui objects should be able to Draw
and ProcessEvent
. Not all gui objects will contain the container contained inside SingleLineBuffer
.
Buffer
and FileBuffer
inherit from SingleLineBuffer
to change how the container inside SingleLineBuffer
behaves. (FileBuffer
actually only added new file IO functions.)
One could create an instance of one of the buffers, however I don't in the context I am working with.
One cannot create an instance of any of the abstract GUI*
classes. Thinking about it, there should probably be an additional abstract base class below GUIMultilineTextEntry
which inherits from FileBuffer
.
The actual objects that the user may create an instance of are Label
, Inputbox
and Textbox
. I intend to add more in the future, such as a multiline label. This would have to inherit from a base class which inherited from Buffer
and GUITextObject
, probably.
This inheritance structure quickly became quite complicated. I wrote it as I went along, guided by what my code was instructing me to do. For example, I wrote a Textbox class, then said "the container in Textbox is essentially the same as the container in Label, therefore they should inherit from a common object". The difference was that a Textbox has file IO, an additional inheritance step is dictated, and a Textbox can contain the new line character in the container, so an additional inheritance step is dictated here too.
Questions
Can this inheritance problem be resolved?
Which classes should inherit virtually from which other classes.
My attempts
- No virtual inheritance
Compiler error: (multiple versions of)
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
is defined in SingleLineBuffer
. It is a non-virtual function, as the container exists only in SingleLineBuffer
, and therefore Size
is written to work correctly for both Buffer
and FileBuffer
.
- Break diamond 1: Put
virtual
betweenGUITextObject
andGUITextEntry
to stopSize
being "pulled down via GUITextObject" before it is overridded in Buffer. (Blue mark)
Compiler error (1):
error: no matching function for call to ‘GUITextObject::GUITextObject()’
I can fix this by calling the required constructor from GUIMultilineTextEntry
. I don't understand why this is needed. (Second Question) Fix shown below:
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
{ ...
This same fix is needed in Inputbox
and Textbox
.
However this results in a further error
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);
I believe I could resolve this by using dynamic_cast
instead of static_cast
, however I am not sure this is a good idea as I have heard that dynamic casting can slow down code significantly.
- Break diamond 1, second attempt:
I made a second attempt at resolving the problem by inheriting virtually between Buffer
and SingleLineBuffer
. (See red dot) However when I did this the compiler error changed to
error: no unique final overrider for ‘virtual void SingleLineBuffer::SetText(const string&)’ in ‘Textbox’
My guess is this is equivalent to the compiler telling me "you overrided some functions in Buffer by inheriting, but you inherited virtually, and the functions you have overridden are also present in a derived class via non-virtual inheritance, so I don't know which one should take precidence" - but this really is a guess.
- I tried similar things to break diamond 2 but encountered similar compiler errors.
Since this is now quite a long question, I will not list all the details of that attempt here.