Interfaces, abstract, concrete classes and QObject
-
I know that the tittle might sound confusing, but I come from languages that isn't strongly typed as C++ and it made me a little bit confused when changed to C++.
I know also that it might have nothing to do with Qt as it's a C++ question, but Qt is made using C++ and this is the only place I know to ask such as question without people throwing stones at me (read Stack Overflow).
Imagine that I have a class calledAbstractFoo
and two classes that inherits from that abstract class:Bar
andBiz
.
I want to create a method that accepts FooInterface as parameter, I tried to useconst AbstractFoo &foo
but I got an error when I try to use a method saying that can't convertthis
toconst
. I figured I had to use pointers and it works just fine, but now it comes the problem that is planted in my mind, I have to avoid leaks, I always think that and I remember about theQObject
.
It's common for me to see in examples using abstract classes or interfaces inheriting fromQObject
but I don't see why they do that. For example, imagine that in my classes mentioned aboveAbstractFoo
inherits fromQObject
, what is it for? Most of the examples I see is like that and they don't use theQObject
, as least I think they don't. They don't use the parent thing to prevent leaking.
The real question is: How to work with interfaces, abstract classes and concrete classes, where methods can accepted the interface and don't leak the pointer in Qt? (where doesQObject
enters?) -
Honestly too descriptive to answer your question. How about providing simple sample program of your question ? This helps us answer better.
-
Honestly too descriptive to answer your question. How about providing simple sample program of your question ? This helps us answer better.
@dheerendra I don't think there's more than this:
class FooInterface { public: virtual public say() = 0; virtual public hello() = 0; }; class BarAbstract : public QObject, public FooInterface { public: virtual public hello() { qDebug() << "hello from abstract"; }; }; class Biz : public BarAbstract { public: public say() { qDebug() << "hello from biz"; }; }; class Bez : public BarAbstract { public: public say() { qDebug() << "hello from bez"; }; }; void call(FooInterface *foo) { foo->hello(); foo->say(); } Bez *bez = new Bez; call(bez);
I don't know if it's going to work, it's just an example of code.
I see a lot of codes like that, the abstract class inheriting fromQObject
but it doesn't make use of the parent thing to avoid leaking, etc. -
I know that the tittle might sound confusing, but I come from languages that isn't strongly typed as C++ and it made me a little bit confused when changed to C++.
I know also that it might have nothing to do with Qt as it's a C++ question, but Qt is made using C++ and this is the only place I know to ask such as question without people throwing stones at me (read Stack Overflow).
Imagine that I have a class calledAbstractFoo
and two classes that inherits from that abstract class:Bar
andBiz
.
I want to create a method that accepts FooInterface as parameter, I tried to useconst AbstractFoo &foo
but I got an error when I try to use a method saying that can't convertthis
toconst
. I figured I had to use pointers and it works just fine, but now it comes the problem that is planted in my mind, I have to avoid leaks, I always think that and I remember about theQObject
.
It's common for me to see in examples using abstract classes or interfaces inheriting fromQObject
but I don't see why they do that. For example, imagine that in my classes mentioned aboveAbstractFoo
inherits fromQObject
, what is it for? Most of the examples I see is like that and they don't use theQObject
, as least I think they don't. They don't use the parent thing to prevent leaking.
The real question is: How to work with interfaces, abstract classes and concrete classes, where methods can accepted the interface and don't leak the pointer in Qt? (where doesQObject
enters?)@Defohin said in Interfaces, abstract, concrete classes and QObject:
I tried to use const AbstractFoo &foo but I got an error when I try to use a method saying that can't convert this to const
The problem does not come from what you think.
const AbstractFoo &
can only useconst
methods inside AbstractFoo. changing to:class FooInterface { public: virtual public say() const = 0; virtual public hello() const = 0; };
will fix your problem. const methods are pretty important. as a general rule, if the method does not change members of the class it should be declared const
magine that in my classes mentioned above AbstractFoo inherits from QObject, what is it for?
The uses are numerous, the most important ones:
- use parent-child to handle lifecycle
- ability to use signals and slots
- ability to expose the class to QML
- QObject::tr() to mark translatable strings
don't leak the pointer
You don't leak the pointer, you leak the memory pointed too. The solution from the current standard is to use smart pointers. This is the flowchart I normally follow, it covers 99% of the cases:
- is it a QObject?
- yes
- can you give it a parent? (for example
moveToThread()
prevents the parent-child relationship)- yes: use a normal pointer and set the parent
- no: connect the
deleteLater()
slot to some signal
- can you give it a parent? (for example
- no
- can multiple places share ownership of the object or pass ownership from one another? (e.g. in containes)
- yes: use
std::shared_ptr
- no: use
std::unique_ptr
- yes: use
- can multiple places share ownership of the object or pass ownership from one another? (e.g. in containes)
- yes
If you need to use the pointer in a function that should not manage ownership but just access (read/write) the object (this covers 90% of use cases) then use a raw pointer argument (use
std::unique_ptr::get()
orstd::shared_ptr::get()
to retrieve the raw pointer from smart pointers)
References to avoid being eaten alive by C++ purists for what I just said:
Bjarne Stroustrup - A Tour of C++
Scott Meyers - Effective Modern C++
Herb Sutter - Back to the Basics! Essentials of Modern C++ Style -
@Defohin said in Interfaces, abstract, concrete classes and QObject:
I tried to use const AbstractFoo &foo but I got an error when I try to use a method saying that can't convert this to const
The problem does not come from what you think.
const AbstractFoo &
can only useconst
methods inside AbstractFoo. changing to:class FooInterface { public: virtual public say() const = 0; virtual public hello() const = 0; };
will fix your problem. const methods are pretty important. as a general rule, if the method does not change members of the class it should be declared const
magine that in my classes mentioned above AbstractFoo inherits from QObject, what is it for?
The uses are numerous, the most important ones:
- use parent-child to handle lifecycle
- ability to use signals and slots
- ability to expose the class to QML
- QObject::tr() to mark translatable strings
don't leak the pointer
You don't leak the pointer, you leak the memory pointed too. The solution from the current standard is to use smart pointers. This is the flowchart I normally follow, it covers 99% of the cases:
- is it a QObject?
- yes
- can you give it a parent? (for example
moveToThread()
prevents the parent-child relationship)- yes: use a normal pointer and set the parent
- no: connect the
deleteLater()
slot to some signal
- can you give it a parent? (for example
- no
- can multiple places share ownership of the object or pass ownership from one another? (e.g. in containes)
- yes: use
std::shared_ptr
- no: use
std::unique_ptr
- yes: use
- can multiple places share ownership of the object or pass ownership from one another? (e.g. in containes)
- yes
If you need to use the pointer in a function that should not manage ownership but just access (read/write) the object (this covers 90% of use cases) then use a raw pointer argument (use
std::unique_ptr::get()
orstd::shared_ptr::get()
to retrieve the raw pointer from smart pointers)
References to avoid being eaten alive by C++ purists for what I just said:
Bjarne Stroustrup - A Tour of C++
Scott Meyers - Effective Modern C++
Herb Sutter - Back to the Basics! Essentials of Modern C++ Style@VRonin Thank you for y our answer.
I have two questions based on your answer.- If I use the parent thing with QObject it's on the abstract class, right? What about the concrete classes? How would that look like?
- The problem with the reference is that I can't create a class with all the methods as const, then pointers is the only way out, right?
- The
deleteLater
will be "activated" when it's out of scope or right at the moment I call it? For example, it's common to usedeleteLater
withQNetworkReply
in a slot, if I callreply->deleteLater
before I do a few things in that slot, will that delete or it's going to delete only when it's out of scope? (it's common for me to calldeleteLater
before 'cause I have a fewif
conditions and I don't want to be repeatingdeleteLater
to every condition.
-
- All the derived classes of QObject have a
setParent()
method to set the parent, you don't have to do anything manually. - The problem is not with the reference but with the const.
const AbstractFoo&
can only use const methodsAbstractFoo&
can use all methods - as stated in http://doc.qt.io/qt-5/qobject.html#deleteLater
The object will be deleted when control returns to the event loop.
- All the derived classes of QObject have a
-
- All the derived classes of QObject have a
setParent()
method to set the parent, you don't have to do anything manually. - The problem is not with the reference but with the const.
const AbstractFoo&
can only use const methodsAbstractFoo&
can use all methods - as stated in http://doc.qt.io/qt-5/qobject.html#deleteLater
The object will be deleted when control returns to the event loop.
- All the derived classes of QObject have a
-
@VRonin A questions about your answer 1: It's common to see the parent thing on the constructor, imagine that the constructor with the parent thing is on the abstract class, how is the concrete class affected by that? And how to make usage of that?
I'd like to expand a bit here on @VRonin's answer:
can you give it a parent? (for example moveToThread() prevents the parent-child relationship)
moveToThread()
does not prevent the parent-child relationship. It, however, imposes the requirement (which is documented) that all objects in a givenQObject
hierarchy have the same thread affinity (this is mainly to prevent race conditions on object destruction). If one moves the root object of the tree to a given thread, then all children are moved to the same thread too.yes: use std::shared_ptr
Only a note here. "Shared pointer" here implies that the object manages its own lifetime, so that's why mixing it with
QObject
s is a rather dubious decision in many cases (QObject
s have a very well defined ownership). Also using a shared pointer does not actually share the object, it shares (as the name suggest) only the pointer to said object. There's a subtle difference when working in a threaded environment, where the pointer is safe for passing around (thread-wise) but the object is not, it's responsibility of the programmer to ensure no race conditions happen when working with the object.The solution from the current standard is to use smart pointers.
The solution to what? My solution which is by no means contradicting the standard is to use stack-based allocation, which is faster, more robust and safer. I will yet again complain that someone saying you should always use smart pointers, doesn't necessarily sum up to a rule, or even a guideline, whomever that person might be ...
- If I use the parent thing with QObject it's on the abstract class, right? What about the concrete classes? How would that look like?
C++ doesn't much distinguish here, abstract classes are still classes and have all the features regular classes have. So you can still have a class partially providing an implementation (e.g. constructors and such). The only restriction an abstract class imposes is that you can't have instances (objects) made out of it, because you have functions that are pure virtual (i.e. they don't have implementations).
A questions about your answer 1: It's common to see the parent thing on the constructor, imagine that the constructor with the parent thing is on the abstract class, how is the concrete class affected by that? And how to make usage of that?
Delegate to the parent class. Example follows:
class MyAbstractClass : public QObject { Q_OBJECT public: MyAbstractClass(QObject * parent = Q_NULLPTR) : QObject(parent) //< Will call the parent class' constructor first { } virtual void myPureVirtualMethod() = 0; }; class MyClass : public MyAbstractClass { Q_OBJECT public: MyClass(QObject * parent = Q_NULLPTR) : MyAbstractClass(parent) //< Will call the parent class' constructor first { } void myPureVirtualMethod() Q_DECL_OVERRIDE { //< We now provide implementation for that method, so we don't have an abstract class anymore } };
-
the usual way is to call the base constructor from the derived class.
in your case you have to define these constructors:
BarAbstract::BarAbstract(QObject* parent=0) :QObject(parent) // other initialisation {/*constructor body*/} Bez ::Bez (QObject* parent=0) :BarAbstract(parent) // other initialisation {/*constructor body*/}
-
I'd like to expand a bit here on @VRonin's answer:
can you give it a parent? (for example moveToThread() prevents the parent-child relationship)
moveToThread()
does not prevent the parent-child relationship. It, however, imposes the requirement (which is documented) that all objects in a givenQObject
hierarchy have the same thread affinity (this is mainly to prevent race conditions on object destruction). If one moves the root object of the tree to a given thread, then all children are moved to the same thread too.yes: use std::shared_ptr
Only a note here. "Shared pointer" here implies that the object manages its own lifetime, so that's why mixing it with
QObject
s is a rather dubious decision in many cases (QObject
s have a very well defined ownership). Also using a shared pointer does not actually share the object, it shares (as the name suggest) only the pointer to said object. There's a subtle difference when working in a threaded environment, where the pointer is safe for passing around (thread-wise) but the object is not, it's responsibility of the programmer to ensure no race conditions happen when working with the object.The solution from the current standard is to use smart pointers.
The solution to what? My solution which is by no means contradicting the standard is to use stack-based allocation, which is faster, more robust and safer. I will yet again complain that someone saying you should always use smart pointers, doesn't necessarily sum up to a rule, or even a guideline, whomever that person might be ...
- If I use the parent thing with QObject it's on the abstract class, right? What about the concrete classes? How would that look like?
C++ doesn't much distinguish here, abstract classes are still classes and have all the features regular classes have. So you can still have a class partially providing an implementation (e.g. constructors and such). The only restriction an abstract class imposes is that you can't have instances (objects) made out of it, because you have functions that are pure virtual (i.e. they don't have implementations).
A questions about your answer 1: It's common to see the parent thing on the constructor, imagine that the constructor with the parent thing is on the abstract class, how is the concrete class affected by that? And how to make usage of that?
Delegate to the parent class. Example follows:
class MyAbstractClass : public QObject { Q_OBJECT public: MyAbstractClass(QObject * parent = Q_NULLPTR) : QObject(parent) //< Will call the parent class' constructor first { } virtual void myPureVirtualMethod() = 0; }; class MyClass : public MyAbstractClass { Q_OBJECT public: MyClass(QObject * parent = Q_NULLPTR) : MyAbstractClass(parent) //< Will call the parent class' constructor first { } void myPureVirtualMethod() Q_DECL_OVERRIDE { //< We now provide implementation for that method, so we don't have an abstract class anymore } };
I was expecting a much harsher flame from you on this one, I got away easily! 😉
@kshegunov said in Interfaces, abstract, concrete classes and QObject:
Only a note here. "Shared pointer" here implies that the object manages its own lifetime, so that's why mixing it with QObjects is a rather dubious decision
That's why it ends in the "is it a QObject?" -> "no:" section
@kshegunov said in Interfaces, abstract, concrete classes and QObject:
he solution to what? My solution which is by no means contradicting the standard is to use stack-based allocation,
I did not explain myself clearly here. I meant the solution to raw pointers that act as owners of memory ending up lost. stack-based is of course a solution if you can allocate the object on the stack and arguably
std::unique_ptr
, at the end of the day, behaves like stack allocation in terms of memory management -
I was expecting a much harsher flame from you on this one, I got away easily! 😉
@kshegunov said in Interfaces, abstract, concrete classes and QObject:
Only a note here. "Shared pointer" here implies that the object manages its own lifetime, so that's why mixing it with QObjects is a rather dubious decision
That's why it ends in the "is it a QObject?" -> "no:" section
@kshegunov said in Interfaces, abstract, concrete classes and QObject:
he solution to what? My solution which is by no means contradicting the standard is to use stack-based allocation,
I did not explain myself clearly here. I meant the solution to raw pointers that act as owners of memory ending up lost. stack-based is of course a solution if you can allocate the object on the stack and arguably
std::unique_ptr
, at the end of the day, behaves like stack allocation in terms of memory management@VRonin said in Interfaces, abstract, concrete classes and QObject:
I was expecting a much harsher flame from you on this one, I got away easily! 😉
I must be loosing the magic touch then ... ;)
That's why it ends in the "is it a QObject?" -> "no:" section
Yes, I know, it was just a note, not a critique.
I did not explain myself clearly here. I meant the solution to raw pointers that act as owners of memory ending up lost. stack-based is of course a solution if you can allocate the object on the stack and arguably
std::unique_ptr
, at the end of the day, behaves like stack allocation in terms of memory managementIt is in fact a stack allocation. You create an object in the stack, to manage a piece of memory. However many times (in Qt particulary) you don't care about any of those pointers. Qt doesn't throw from the constructor (or anywhere else in fact), so you can safely use raw pointers. There is no reason whatsoever to think that
std::unique_ptr
is better (or faster) thannew
anddelete
. The only thing it's good for is to be throw-safe (here I exclude the trivial forgottendelete
). -
It might be not related to your question, but you need to specify type when you declare or define member functions:
public:
virtual public say() = 0;should become
public:
virtual void say() = 0; -
It might be not related to your question, but you need to specify type when you declare or define member functions:
public:
virtual public say() = 0;should become
public:
virtual void say() = 0;@Oleksandr-Malyushytskyy It was a typing error, but thank you.
I'll be reading your answers later tonight with attention to understand everything.
Thank you all so far.
-
QObject enters to the scene when you need signal/slots. Since QObject deletes its children you do not have worry about deleting QObjects which do have parents( unless it is required by workflow - like create dialog, show, it, delete it when closed).
Above means allocated on the stack QObject should not typically have a parent.
For everything else (including QObject instances without parent) the right answer for proper memory management is the same as for any C++ code - smart pointers (already mentioned above).
-
QObject enters to the scene when you need signal/slots. Since QObject deletes its children you do not have worry about deleting QObjects which do have parents( unless it is required by workflow - like create dialog, show, it, delete it when closed).
Above means allocated on the stack QObject should not typically have a parent.
For everything else (including QObject instances without parent) the right answer for proper memory management is the same as for any C++ code - smart pointers (already mentioned above).
@Oleksandr-Malyushytskyy said in Interfaces, abstract, concrete classes and QObject:
Above means allocated on the stack QObject should not typically have a parent.
Not true for
QWidget
s, where parent is important regardless of memory management. Dialogs without a parent have a native handle, while those that do have a parent are (ordinarily) alien widgets.the right answer for proper memory management is the same as for any C++ code - smart pointers
Sorry, but no. Smart pointers aren't proper memory management, they are a tool that might facilitate proper management. These two things aren't one and the same. You can have proper memory management without ever in your code using smart pointers. C programs can have proper memory management, and they don't even have classes ...
-
Not true for QWidgets, where parent is important regardless of memory management. Dialogs without a parent have a native handle, while those that do have a parent are (ordinarily) alien widgets.
- Any widget regardless of parent can have a native handle.
- Providing a parent to the QObject allocated on the stack may lead to deleting it twice. So QWidget which needs a parents should not be allocated on a stack.
Sorry, but no. Smart pointers aren't proper memory management, they are a tool that might facilitate proper management.
- Smartpointers in C++ is basically only a way for an application to do a proper memory management in presence of exceptions.
-
Not true for QWidgets, where parent is important regardless of memory management. Dialogs without a parent have a native handle, while those that do have a parent are (ordinarily) alien widgets.
- Any widget regardless of parent can have a native handle.
- Providing a parent to the QObject allocated on the stack may lead to deleting it twice. So QWidget which needs a parents should not be allocated on a stack.
Sorry, but no. Smart pointers aren't proper memory management, they are a tool that might facilitate proper management.
- Smartpointers in C++ is basically only a way for an application to do a proper memory management in presence of exceptions.
@Oleksandr-Malyushytskyy said in Interfaces, abstract, concrete classes and QObject:
Any widget regardless of parent can have a native handle.
They can indeed, that's why I put "ordinarily" (i.e. usually) in parenthesis - most of the time they won't if you don't pass the appropriate attribute(s).
Providing a parent to the QObject allocated on the stack may lead to deleting it twice.
Yes, if you take special care not to put them in a stack order. A stack is first-in-last-out, so they (the objects) should be allocated in this fashion - parents come before children. Example:
QObject parent; QObject child(&parent); //< Perfectly safe!
QObject child; QObject parent; child.setParent(&parent); //< Bad idea, don't do it it like this.
So QWidget which needs a parents should not be allocated on a stack.
Absolutely wrong! You can allocate them on the stack however much you like.
Smartpointers in C++ is basically only a way for an application to do a proper memory management in presence of exceptions.
Yes, that's what I wrote two posts up. And again, it's a tool, it doesn't sum up to "proper memory management". You can achieve proper memory management, albeit verbose, without smart pointers too. Plus I already noted that for Qt specifically you don't have exceptions thrown from the library.
-
@Oleksandr-Malyushytskyy said in Interfaces, abstract, concrete classes and QObject:
Any widget regardless of parent can have a native handle.
They can indeed, that's why I put "ordinarily" (i.e. usually) in parenthesis - most of the time they won't if you don't pass the appropriate attribute(s).
Providing a parent to the QObject allocated on the stack may lead to deleting it twice.
Yes, if you take special care not to put them in a stack order. A stack is first-in-last-out, so they (the objects) should be allocated in this fashion - parents come before children. Example:
QObject parent; QObject child(&parent); //< Perfectly safe!
QObject child; QObject parent; child.setParent(&parent); //< Bad idea, don't do it it like this.
So QWidget which needs a parents should not be allocated on a stack.
Absolutely wrong! You can allocate them on the stack however much you like.
Smartpointers in C++ is basically only a way for an application to do a proper memory management in presence of exceptions.
Yes, that's what I wrote two posts up. And again, it's a tool, it doesn't sum up to "proper memory management". You can achieve proper memory management, albeit verbose, without smart pointers too. Plus I already noted that for Qt specifically you don't have exceptions thrown from the library.
@kshegunov said in Interfaces, abstract, concrete classes and QObject:
QObject parent;
QObject child(&parent); //< Perfectly safe!You make an assumption that parent is allocated on a stack. Why? If it is not? If it is destroyed before child goes out of scope?
I could continue to argue about other topics too, like if Qt code does not throw exceptions, it does not mean that exception may not be thrown in your application written with Qt or even from Qt code,
but I believe this topic is not a right place for such discussion, since it not does not help person who asked a question, while my original answer I believe should have helped him. -
@kshegunov said in Interfaces, abstract, concrete classes and QObject:
QObject parent;
QObject child(&parent); //< Perfectly safe!You make an assumption that parent is allocated on a stack. Why? If it is not? If it is destroyed before child goes out of scope?
I could continue to argue about other topics too, like if Qt code does not throw exceptions, it does not mean that exception may not be thrown in your application written with Qt or even from Qt code,
but I believe this topic is not a right place for such discussion, since it not does not help person who asked a question, while my original answer I believe should have helped him.The original topic did include a question about leaking or not memory, so memory management is indeed relevant, although we are running a bit off topic I will concede.
You make an assumption that parent is allocated on a stack. Why? If it is not? If it is destroyed before child goes out of scope?
Then I'd argue the child should be a member of said parent:
class X : public QObject { X(QObject * parent) : QObject(parent), child(this) { } private: QObject child; }
it does not mean that exception may not be thrown in your application written with Qt or even from Qt code,
Yes, and I did acknowledge that. Plus I reiterate, yet again, throw-safe code does not require smart pointers, they just make it easier to handle.
I'd also like to introduce yet another consideration here:
Creating aQObject
(or derived class) instance withnew
(or equivalentstd::make_unique
, make_shared or w/e) amounts to allocating avoid *
, which is mighty inefficient.@VRonin Nice glasses!