How do QList functions tell a stack object from a heap object (so as to copy it if on stack) ?



  • Excerpt from QList class reference:

    "Internally, QList<T> is represented as an array of pointers to items of type T. If T is itself a pointer type or a basic type that is no larger than a pointer, or if T is one of Qt's shared classes, then QList<T> stores the items directly in the pointer array..."

    What if the object pointed to is on stack ? How does Qt know it is/isn' t ?



  • I'm not sure if your question is about the internals of QList or about the usage of QList, but here goes about the usage (internals comes at the end):

    QList doesn't check for where the object pointed is allocated. If the object stays in existence while the list exists, there is no problem. This is the exact same requirement for stack-allocated objects as for heap-allocated objects. If the object is destroyed before the list is, the program will fail -- crash, usually.

    The following will work:
    @MyCustomType thingy;
    {
    QList<MyCustomType *> thingies;
    thingies << &thingy;
    // ... do stuff with list
    } // list leaves scope here and is destroyed, thingy still exists.
    @

    This will fail:
    @QList<MyCustomType *> someFunction()
    {
    MyCustomType thingy;
    QList<MyCustomType *> result;
    result << &thingy;
    return result;
    } // list is copied, but thingy is destroyed (leaving scope)
    @

    On to the next part then.

    Internally, QList uses QList::Node objects. These objects have a pointer to the data, as described in the documentation, and a function that returns the type of data needed. The relevant code snippet is:

    @struct Node { void *v;
    Q_INLINE_TEMPLATE T &t()
    { return reinterpret_cast<T>(QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic
    ? v : this); }
    };
    @

    Here we see a helper class that gives some information about the type of T. This is the part that provides exactly what is needed for this approach to work. If sizeof(T) is larger than a pointer, the v pointer is used, else the this pointer is used. For this part, the Trolls had to have some knowledge of C++. This works, because the struct QList::Node starts at exactly the same location in memory as void *QList::Node::v, effectively becoming a pointer to the object.



  • If you do the following:

    • you have a type MyType
    • you create a QList<MyType *>
    • you create a MyType object on the stack
    • you put a pointer to that stack object in the list
    • your stack object goes out of scope, but your list does not

    then you have a dangling pointer in your list. Qt does not distinguish between objects created on the heap/stack!



  • Thanks to both of you for your insightful replies.
    They confirm what I surmised.

    What puzzles and bothers me is that a safe usage
    of QList seems to require repeated allocations on
    the heap with the performance penalty they induce.
    Hopefully Qt would not increment the reference
    count of this object (does it ?) which otherwise would
    never be freed when the list is destroyed (or should
    we free it once it has been appended to the list ?).

    Now, what happens when a function such as QStandardItem::setData
    expecting a "const QVariant &" is called upon an argument construction
    such as "setData(QVariant(some_const_char_pointer))" ?
    Is the created QVariant passed by value although a "const QVariant &"
    is expected or is it allocated (where ?) and copied by setData ?

    Again, thanks for your helpful replies.



  • As for my last question I had simply forgotten what I had read in
    one of Dr Agner Fog' s manuals ("Calling conventions") :

    (Applies to all OS whether x64 or not, Windows, Linux, BSD, etc...)

    "There are several different methods to transfer a parameter to a function if the parameter is a structure, class or union object. A copy of the object is always made, and this copy is transferred to the called function either in registers, on the stack, or by a pointer..."

    Now, does it occur in the case of a reference (I mean & not *) to a structure ?

    I suppose (and hope) the compiler handles this transparently.



  • It looks like you're coming from a Java background. If so, please forget everything that you learned there regarding memory management. You'll have to read up the basics of C++ on this topic. In C++ there is nothing like a "reference count" out of the box. Qt's implicitly shared classes do have one, but that's a specific topic of its own.

    Regarding your question on a method with a “const QVariant &” parameter: a reference is as reference is a reference. No copy is made in this case.

    Oh, and every alarm bell rings here during reading

    bq. What puzzles and bothers me is that a safe usage of QList seems to require repeated allocations on the heap with the performance penalty they induce.

    Premature optimization is the root of many evil. Just don't think about it, it will hardly ever hit you.



  • Shared classes are mentioned in QList Reference, whence my question
    about reference counts.

    Since a QList cannot, without a predictable crash, contain references to
    objects which went out of scope, one will quite often have to allocate these
    objects on the heap. Once appended to the list and if not directly used
    anymore should they be freed ? Because of the possibility of either a
    crash or a memory leak I think it' s a legitimate question.

    As for your remark about the “const QVariant &”, please note that if the
    object does not already exist it must be constructed (and probably allocated
    on stack) before its address can be passed, this is what I meant in my previous
    post.



  • QList always makes a copy of the object you put in. Sometimes the object is a pointer, so the pointer is copied.

    @QList<MyType> typeList; // contains copies of MyType instances. MyType needs to be copyable.

    QList<MyType *> ptrList; // contains copies of pointers to MyType instances (hopefully). MyType doesn't have to be copyable.@

    Regarding the performance penalty: Yes there is a very very slight penalty, however, QList is in general use the fastest container to use within the Qt toolkit.



  • Thank you, Franzk.

    So, it seems safe to input stack objets to QList functions.
    (But not pointers to stack objects, of course).

    In a general way:

    1. Must objects allocated to be passed as arguments to
      Qt functions always be freed when done with them ?
      (The answer being yes in the QList case, as you pointed out).

    2)Must objects returned by Qt always be freed when done with them ?
    (Assuming they are not added to some parent widget).

    Thanks for your helpful replies.



  • Depends on the case. When in doubt, check the documentation for the function. It usually states if you should delete the returned object and if so, when. If nothing is said about object deletion, you can assume (in Qt's case) that you don't have to delete the object.



  • Thanks, Fransk.



  • If you put a value type into a Qt 4 collection there usually is no need to delete the objects manually, as you (as a prorgrammer) usually operate on stack based objects and the objects' contents are copied. The container classes take care of this. The internals are, well internals, so you can just assume, that the Qt classes clean up their mess :-)

    If you put pointers into the collections, it is usually your duty to delete the objects once do not need them anymore and they have been removed from the container(s). I.e. container.remove(pointertype) does note call delete on the removed pointer!



  • Thank you for the clarification.


Log in to reply
 

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