Subclassing items in a model
-
Hi all -
My app has several models, built following the example in the video on this page. They all work fine, but now one of them needs to maintain a list of (pointers to) dissimilar items.
The base class for the list members is Equipment (a struct). I've created a subclass called Vsp. I'm not sure how to go about writing the data()/setData() functions for this. I tried writing a data() routine for both the parent and subclass, as well as a dataSubclass() routine. The idea was to declare dataSubclass as a pure virtual function in the parent class, but doing so renders the class abstract, so I can't instantiate from it.
I want the parent class to do some work, as there are properties that are common to all subclasses, and I don't want to replicate the common code in all the subclasses. Before I disappear down the rabbit hole, can someone advise on whether I'm on the right track, or if there's a better way to do this that I'm not aware of.
Thanks for any suggestions...
-
@JonB thanks for the input. Along the lines of your latter suggestion, I've done this:
QVariant EquipmentModel::data(const QModelIndex &index, int role) const { QVariant qv = QVariant(); Equipment &equipment = *(*m_list)[index.row()]; qv = equipment.data(role); if (!qv.isValid()) { qv = equipment.dataSubclass(role); }where my dataSubclass() method is pure virtual. The intent was to override it in each subclass. If I don't make it pure virtual, I don't know how to get the override functions to be called.
This isn't really a necessary feature, I suppose - I could put all the roles for all the subclasses in my model. The concern there is performance -- the data() function gets called a lot in this design.
@mzimmers said in Subclassing items in a model:
where my dataSubclass() method is pure virtual. The intent was to override it in each subclass. If I don't make it pure virtual, I don't know how to get the override functions to be called.
I don't know what you mean here about requiring
pure virtual. You can make it that if you wish --- like you said, will make the class abstract --- butoverrideis just as applicable oncevirtualwhetherpureor not. You can provide a base/default implementation which does nothing, then those subclasses which choose to override it can provide an alternative as appropriate. "If I don't make it pure virtual, I don't know how to get the override functions to be called." --- you have something wrong if you find this is the case. -
Hi all -
My app has several models, built following the example in the video on this page. They all work fine, but now one of them needs to maintain a list of (pointers to) dissimilar items.
The base class for the list members is Equipment (a struct). I've created a subclass called Vsp. I'm not sure how to go about writing the data()/setData() functions for this. I tried writing a data() routine for both the parent and subclass, as well as a dataSubclass() routine. The idea was to declare dataSubclass as a pure virtual function in the parent class, but doing so renders the class abstract, so I can't instantiate from it.
I want the parent class to do some work, as there are properties that are common to all subclasses, and I don't want to replicate the common code in all the subclasses. Before I disappear down the rabbit hole, can someone advise on whether I'm on the right track, or if there's a better way to do this that I'm not aware of.
Thanks for any suggestions...
@mzimmers
I am not as expert as some here on C++, or the "right" way to do things (nowadays). For example some might tell you to approach this via templates, I don't know. But there are two obvious ways you can tackle this in a class-inheriting language:-
Define a base method which does the "common" code. Make it virtual, but document to developers that any override is required to call the base method as well as whatever else it might do.
-
Define a base method which does the common code, this time not virtual, but put into it an "extra" call to some other base method. Make that method virtual but not pure (so not making it abstract) and have the base class supply a definition which does nothing. Developers override that extra base method only to do their extras.
Qt uses both of these strategies at various points.
-
-
@mzimmers
I am not as expert as some here on C++, or the "right" way to do things (nowadays). For example some might tell you to approach this via templates, I don't know. But there are two obvious ways you can tackle this in a class-inheriting language:-
Define a base method which does the "common" code. Make it virtual, but document to developers that any override is required to call the base method as well as whatever else it might do.
-
Define a base method which does the common code, this time not virtual, but put into it an "extra" call to some other base method. Make that method virtual but not pure (so not making it abstract) and have the base class supply a definition which does nothing. Developers override that extra base method only to do their extras.
Qt uses both of these strategies at various points.
@JonB thanks for the input. Along the lines of your latter suggestion, I've done this:
QVariant EquipmentModel::data(const QModelIndex &index, int role) const { QVariant qv = QVariant(); Equipment &equipment = *(*m_list)[index.row()]; qv = equipment.data(role); if (!qv.isValid()) { qv = equipment.dataSubclass(role); }where my dataSubclass() method is pure virtual. The intent was to override it in each subclass. If I don't make it pure virtual, I don't know how to get the override functions to be called.
This isn't really a necessary feature, I suppose - I could put all the roles for all the subclasses in my model. The concern there is performance -- the data() function gets called a lot in this design.
-
-
@JonB thanks for the input. Along the lines of your latter suggestion, I've done this:
QVariant EquipmentModel::data(const QModelIndex &index, int role) const { QVariant qv = QVariant(); Equipment &equipment = *(*m_list)[index.row()]; qv = equipment.data(role); if (!qv.isValid()) { qv = equipment.dataSubclass(role); }where my dataSubclass() method is pure virtual. The intent was to override it in each subclass. If I don't make it pure virtual, I don't know how to get the override functions to be called.
This isn't really a necessary feature, I suppose - I could put all the roles for all the subclasses in my model. The concern there is performance -- the data() function gets called a lot in this design.
@mzimmers said in Subclassing items in a model:
where my dataSubclass() method is pure virtual. The intent was to override it in each subclass. If I don't make it pure virtual, I don't know how to get the override functions to be called.
I don't know what you mean here about requiring
pure virtual. You can make it that if you wish --- like you said, will make the class abstract --- butoverrideis just as applicable oncevirtualwhetherpureor not. You can provide a base/default implementation which does nothing, then those subclasses which choose to override it can provide an alternative as appropriate. "If I don't make it pure virtual, I don't know how to get the override functions to be called." --- you have something wrong if you find this is the case. -
@mzimmers said in Subclassing items in a model:
where my dataSubclass() method is pure virtual. The intent was to override it in each subclass. If I don't make it pure virtual, I don't know how to get the override functions to be called.
I don't know what you mean here about requiring
pure virtual. You can make it that if you wish --- like you said, will make the class abstract --- butoverrideis just as applicable oncevirtualwhetherpureor not. You can provide a base/default implementation which does nothing, then those subclasses which choose to override it can provide an alternative as appropriate. "If I don't make it pure virtual, I don't know how to get the override functions to be called." --- you have something wrong if you find this is the case. -
Oh, excellent! So...how does the compiler/OS know that, when I hit the equipment.dataSubclass() line above, that I want to invoke the method in the subclass, and not the (empty) method in the parent class? Is this part of the magic of polymorphism?
@mzimmers said in Subclassing items in a model:
that I want to invoke the method in the subclass, and not the (empty) method in the parent class?
That is what declaring the method
virtualdoes. Overriders can useoverride--- and I suggest they always do --- but that does not actually matter, it's a compiler check. The originalvirtualcauses the behaviour.pure virtualhas the above behaviour, but does not even supply a default implementation. It must be sub-classed and that method must be defined/overridden.C++ executes all this though class vtables and suitable compile-time optimisation to make it efficient. Other languages do whatever equivalent.
The automatic calling of the overridden derived methods comes with inheritance. So far as I know, polymorphism refers to multiple inheritance. Some O/O languages support this, some do not. C++ does. Your classes can inherit/derive from several classes. Some Qt classes are defined like this. But that is not directly relevant to
virtual, except that there are rules about which to call if more than one inherited class has the same method name, so an override is ambiguous. But that is a specific detail case. -
@mzimmers said in Subclassing items in a model:
that I want to invoke the method in the subclass, and not the (empty) method in the parent class?
That is what declaring the method
virtualdoes. Overriders can useoverride--- and I suggest they always do --- but that does not actually matter, it's a compiler check. The originalvirtualcauses the behaviour.pure virtualhas the above behaviour, but does not even supply a default implementation. It must be sub-classed and that method must be defined/overridden.C++ executes all this though class vtables and suitable compile-time optimisation to make it efficient. Other languages do whatever equivalent.
The automatic calling of the overridden derived methods comes with inheritance. So far as I know, polymorphism refers to multiple inheritance. Some O/O languages support this, some do not. C++ does. Your classes can inherit/derive from several classes. Some Qt classes are defined like this. But that is not directly relevant to
virtual, except that there are rules about which to call if more than one inherited class has the same method name, so an override is ambiguous. But that is a specific detail case.@JonB yeah, I did a little reading on the override "operator" or whatever it is - I guess it's not necessary (as you said); someone referred to it as "syntactic sugar." But I can see the value in using it here, so the compiler will catch any mistakes.
And yeah, this seems to be more a feature of inheritance than polymorphism. It's still not clear to me how the program knows not to call the base class method (which, while it does nothing, still does exist) and instead invoke the appropriate subclass method. But I guess I'll just trust it.
Thanks for the recommendation and the clarification.
-
M mzimmers has marked this topic as solved on
-
@JonB yeah, I did a little reading on the override "operator" or whatever it is - I guess it's not necessary (as you said); someone referred to it as "syntactic sugar." But I can see the value in using it here, so the compiler will catch any mistakes.
And yeah, this seems to be more a feature of inheritance than polymorphism. It's still not clear to me how the program knows not to call the base class method (which, while it does nothing, still does exist) and instead invoke the appropriate subclass method. But I guess I'll just trust it.
Thanks for the recommendation and the clarification.
@mzimmers said in Subclassing items in a model:
It's still not clear to me how the program knows not to call the base class method (which, while it does nothing, still does exist) and instead invoke the appropriate subclass method. But I guess I'll just trust it.
Have a read of just the first couple of paragraphs of Wikipedia's Virtual method table. In C++ when an object is created it gains access to a table of pointers to functions specific to its class, including dealing with virtual/overrides.