Interfaces, abstract, concrete classes and QObject
-
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!
-
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!
@kshegunov said in Interfaces, abstract, concrete classes and QObject:
allocating a void *, which is mighty inefficient
mighty = 32/64 bits of memory?
-
@kshegunov said in Interfaces, abstract, concrete classes and QObject:
allocating a void *, which is mighty inefficient
mighty = 32/64 bits of memory?
@VRonin said in Interfaces, abstract, concrete classes and QObject:
mighty = 32/64 bits of memory?
+ the heavy look-up through the heap manager. ;)
A stack (or more correctly auto-storage) allocation takes a single instruction for the whole of the memory. It basically increments (or rather decrements) the stack pointer with a single number.
PS. To expand a bit, think of it like this:
Allocating on the stack is one decrement of the stack pointer. Allocating an object with members with auto-storage on the heap is done in one go in the heap manager - one look up. If you allocate each of the members on the heap you go through so many look ups in the heap. -
@VRonin said in Interfaces, abstract, concrete classes and QObject:
mighty = 32/64 bits of memory?
+ the heavy look-up through the heap manager. ;)
A stack (or more correctly auto-storage) allocation takes a single instruction for the whole of the memory. It basically increments (or rather decrements) the stack pointer with a single number.
PS. To expand a bit, think of it like this:
Allocating on the stack is one decrement of the stack pointer. Allocating an object with members with auto-storage on the heap is done in one go in the heap manager - one look up. If you allocate each of the members on the heap you go through so many look ups in the heap.I'm still convince that that "mighty" is nothing compared to the inefficiencies I unknowingly introduce in production code because I'm not smart enough to think the O(1) algorithm; but I have to admit I'm a total ignorant as this level of technicality
-
I'm still convince that that "mighty" is nothing compared to the inefficiencies I unknowingly introduce in production code because I'm not smart enough to think the O(1) algorithm; but I have to admit I'm a total ignorant as this level of technicality
Well, I suppose that argument holds too ... :)
I've been known to run rampaging against the heap on occasion, plus arguably it shouldn't matter in most cases. I'm arguing the principles here however ... (sounding like the devil's advocate now)