Inheritance in QT - strange problem
-
Hi,
A long time ago I wrote class, which inherits QRadioButton. So in "MyRadioButton" class, I have own
hitButton(const QPoint &pos)
,event(QEvent *e)
methods. And it works.class MyRadioButton: public QRadioButton { ... }
Now I have to write the same class like MyRadioButton, but for QCheckBox. So I can do something like that:
class MyCheckBox: public QCheckBox { ... }
But it looks bad, because of I repeat the same code in MyCheckBox and MyRadioButton ( they differs only, which they inherit )...
I tried create class, which inherits both QRadioButton and MyRadioButton ( which inherits QAbstractButton ). But there is a problem with QObject ( I inherited it two times ). So I can't compile that. I can't virtual inherits, so... what is the best way to do that? -
@JonB said in Inheritance in QT - strange problem:
So far as I know, there is no way to define these overrides directly in some single class derived from both
QRadioButton
&QCheckBox
(unless there is some clever "template"-y way of doing do, but I don't think so).You can totally do that with templates, by inheriting the template parameter :
template<typename Widget> struct EventImpl : Widget { void event() override { } };
You can use it like this :
// Instance on the stack EventImpl<QRadioButton> w1; // Instance on the heap auto w2 = new EventImpl<QCheckBox>(); // Most likely in Qt // Inheritance or alias class RealButton : public EventImpl<QRadioButton> { /*...*/ }; using RealButton2 = EventImpl<QRadioButton>;
Obviously, interfaces and implementations for each class must be compatible with each other, or you will need to write template specializations, which defeats the purpose to avoid duplication here.
If I remember correctly, Q_OBJECT macro cannot be used in a template, but as long as you don't need signals, it is not that much of an issue.
-
@TomNow99
Yes, you cannot dual-inherit fromQObject
. One possibility is to write common code in some non-QObject class they can share. Some useful functionality applicable to both is supplied in QButtonGroup Class, anything you want there? Otherwise what exactly are you doing in your "I repeat the same code in MyCheckBox and MyRadioButton "? -
-
@TomNow99
I do not seehoverIn()
orhoverLeave()
as methods of any class. Forevent()
andhitButton()
these arevirtual protected
, so you will need tooverride
them in your derived classes. You can either duplicate your code, or factor it out to shared functions in another module. So far as I know, there is no way to define these overrides directly in some single class derived from bothQRadioButton
&QCheckBox
(unless there is some clever "template"-y way of doing do, but I don't think so). -
@JonB said in Inheritance in QT - strange problem:
So far as I know, there is no way to define these overrides directly in some single class derived from both
QRadioButton
&QCheckBox
(unless there is some clever "template"-y way of doing do, but I don't think so).You can totally do that with templates, by inheriting the template parameter :
template<typename Widget> struct EventImpl : Widget { void event() override { } };
You can use it like this :
// Instance on the stack EventImpl<QRadioButton> w1; // Instance on the heap auto w2 = new EventImpl<QCheckBox>(); // Most likely in Qt // Inheritance or alias class RealButton : public EventImpl<QRadioButton> { /*...*/ }; using RealButton2 = EventImpl<QRadioButton>;
Obviously, interfaces and implementations for each class must be compatible with each other, or you will need to write template specializations, which defeats the purpose to avoid duplication here.
If I remember correctly, Q_OBJECT macro cannot be used in a template, but as long as you don't need signals, it is not that much of an issue.
-
@Tugdwal said in Inheritance in QT - strange problem:
You can totally do that with templates, by inheriting the template parameter
Hello and welcome.
I am not a template expert, I did wonder if it could maybe be done in C++ with templates, hence my comment! Thank you for informing me of how to do it, I am studying your answer to learn!
I am aware that in C++
struct
&class
are very similar. I see you have usedstruct EventImpl : Widget
, could that have beenclass EventImpl : Widget
?So that I understand: I can see that
EventImpl<QRadioButton> w1
clearly declaresw1
to be anEventImpl
, which has theevent() override
. I presume this also declaresw1
as a baseQRadioButton
?The limitation of "Q_OBJECT macro cannot be used in a template" could be a deal-breaker in certain cases, though I agree not for what the OP appears to be seeking to do here (so far at least).
-
struct
andclass
are almost identical, butstruct
is public by default, so it is shorter here. I usually use it for simpler (data) structures or in examples/test code. Withclass
, it would look something like this :template<typename Widget> class EventImpl : public Widget { protected: // Probably void event() override { } };
class EventImpl : Widget
is perfectly valid, but inherits privately, so public and protected members ofWidget
are not accessible outside ofEventImpl
.@JonB said in Inheritance in QT - strange problem:
I presume this also declares
w1
as a baseQRadioButton
?I'm not sure I understand this, what do you mean by that ?
w1
is aQRadioButton
. It was just one way to show how to use it. I've edited my previous message, to show the different ways you could use it. -
@Tugdwal said in Inheritance in QT - strange problem:
I'm not sure I understand this, what do you mean by that
All good. I am making sure I understand your template statement, given that templates is one area of C++ I am not so familiar with (yes, I started as a C programmer!).
In my "layman's" terms I wish to confirm that
EventImpl<QRadioButton> w1;
has two effects:
- It declares an object instance which has the
void event() override { }
defined inEventImpl
. I get that. - It also declares that object to be an instance of a
QRadioButton
.
That (the second item) may seem like a strange/damned obvious question for you familiar with templates, but I just want to verify my understanding!
- It declares an object instance which has the
-
To be correct, it is not an instance of
QRadioButton
then, it is an instance ofEventImpl<QRadioButton>
. But it inheritsQRadioButton
sostd::vector<QRadioButton*> v; v.push_back(&w1);
is valid for example.EventImpl<QCheckBox>
would be completely different type, that inheritsQCheckBox
(ignoring that both inherits QWidget).EventImpl
could also have members. It is not becauseEventImpl
is a template withQRadioButton
as a parameter that it is aQRadioButton
, it depends on the template implementation (which could be different for each parameter, and one could not inherit at all).The template is the "model" of a class, which literally creates a new type each time we use it with different arguments. The rest is usual classes and inheritance stuff :)
For example, following
RealButton
andRealButton2
, we could do the same without a template, and it would work similarly :class RealButton3 : public QRadioButton { void event() override { } };
-
@Tugdwal
Thank you for your further interest. I hope I am not hijacking this thread (the OP has his answer now from your response), I know this should really go to the C++ Gurus sub-forum, but we are here now and hopefully this is the last request for clarification!I have understood everything you have said, but for:
It is not because
EventImpl
is a template withQRadioButton
as a parameter that it is aQRadioButton
, it depends on the template implementationThis is the phrase I am struggling on. I believe I do understand this template, so I suspect it is a matter of your phrasing versus mine.
Given the definition of
template<typename Widget> class EventImpl : public Widget
it seems to me that it precisely is "because
EventImpl
is a template withQRadioButton
as a parameter that it is aQRadioButton
" (given the invocation ofEventImpl<QRadioButton> w1
). In the definitionWidget
is a variable/placeholder for thetypename
passed in by the caller. So it "expands to"class EventImpl : public QRadioButton
which makes it a subclass of
QRadioButton
. If you passed some other type as the parameter, or if this definition did not containclass EventImpl : public Widget
, then it would not be a subclass ofQRadioButton
.To summarise: this template does two things:
- creates a subclass of whatever class is passed in as
Widget
- it also overrides the
event()
method of the passed-in class.
Right?
Now, finally: this template could theoretically be used to subclass any specified class. But because of
void event() override { }
the class must havevirtual void event()
to override. Which would fail if given some random class. But what seems "dangerous" is that the template makes no reference toQWidget
. We might intend it to only accept aQWidget
-derived class parameter, but if we gave it some quite unrelated class which happened to have avirtual void event()
it would compile without complaint. Which seems "dangerous". Could we do anything in its definition to ensure it only accepts aQWidget
-derived class parameter?? - creates a subclass of whatever class is passed in as
-
Everything you said is correct. And yes, there is nothing restricting to a QWidget. In most cases, it won't compile as you said, at least with
override
. There are complicated ways to restrict it, but I think it is possible to use astatic_assert
withstd::is_base_of
or maybe with concepts (C++20) but I'm not familiar with it. -
@JonB @J-Hilk @Tugdwal Thank you very much!
But I have one more question. I try very simple code. I have:
- .h file
template<typename T> class TmpClass: public T { public: TmpClass(); };
- .cpp file:
template<typename T> TmpClass<T>::TmpClass(){}
and in other .cpp file I use it like this:
TmpClass<QPushButton> someObject;
I have error:
undefined reference to `TmpClass<QPushButton>::TmpClass()
. How to solve it?When I have constructor body in .h file, everything is ok.
EDIT:
https://stackoverflow.com/questions/115703/storing-c-template-function-definitions-in-a-cpp-file
So I have:
template<typename T> TmpClass<T>::TmpClass(){} template class TmpClass<QPushButton>;
And I can compile code.