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 called AbstractFoo and two classes that inherits from that abstract class: Bar and Biz.
    I want to create a method that accepts FooInterface as parameter, 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. 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 the QObject.
    It's common for me to see in examples using abstract classes or interfaces inheriting from QObject but I don't see why they do that. For example, imagine that in my classes mentioned above AbstractFoo inherits from QObject, what is it for? Most of the examples I see is like that and they don't use the QObject, 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 does QObject enters?)



  • 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 from QObject but it doesn't make use of the parent thing to avoid leaking, etc.



  • @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 use const 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
      • 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

    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() or std::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.

    1. 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?
    2. 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?
    3. 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 use deleteLater with QNetworkReply in a slot, if I call reply->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 call deleteLater before 'cause I have a few if conditions and I don't want to be repeating deleteLater to every condition.


    1. All the derived classes of QObject have a setParent() method to set the parent, you don't have to do anything manually.
    2. The problem is not with the reference but with the const. const AbstractFoo& can only use const methods AbstractFoo& can use all methods
    3. as stated in http://doc.qt.io/qt-5/qobject.html#deleteLater

    The object will be deleted when control returns to the event loop.



  • @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?


  • Qt Champions 2016

    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 given QObject 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 QObjects is a rather dubious decision in many cases (QObjects 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 ...

    @Defohin

    1. 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 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


  • Qt Champions 2016

    @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 management

    It 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) than new and delete. The only thing it's good for is to be throw-safe (here I exclude the trivial forgotten delete).



  • 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).


  • Qt Champions 2016

    @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 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.

    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 ...



  • @kshegunov

    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.

  • Qt Champions 2016

    @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.



  • This is the type of flaming I was talking about!
    grabs popcorn


  • Qt Champions 2016

    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 a QObject (or derived class) instance with new (or equivalent std::make_unique, make_shared or w/e) amounts to allocating a void *, which is mighty inefficient.

    @VRonin Nice glasses!



  • I have to say, I'm enjoying it, not the flame, but the arguments, that's how we learn, experienced people discussing.



  • @kshegunov said in Interfaces, abstract, concrete classes and QObject:

    allocating a void *, which is mighty inefficient

    mighty = 32/64 bits of memory?


  • Qt Champions 2016

    @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


  • Qt Champions 2016

    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)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.