Class properties in stack or heap?



  • There is much controversy as to where to allocate a particular object. Some say forever to give preference to the stack because allocating and releasing heap objects comes at a cost and is harder to administer. On the other hand there are those who say that the best thing is to allocate in the heap because the stack is somewhat limited.
    This really confuses me because I don't know which side is right.
    Recommendations say that in the heap should be large objects, but what is the definition of large? How much would be considerable large and how to measure it?
    Finally, applying these ideas to the properties of a class, where should its properties be allocated? In the stack?

    class Person
    {
    	private:
    		QString name;
    		QList<QString> aliases;
    		QDate birthday;
    		int children_number;
    
    	public:
    		Person(QString &name, QList<QString> &aliases, QDate &birthday, int &children_number);
    };
    

    Or in heap?

    class Person
    {
    	private:
    		QString* name;
    		QList<QString*>* aliases;
    		QDate* birthday;
    		int children_number;
    
    	public:
    		Person(QString* name, QList<QString*>* aliases, QDate* birthday, const int &children_number);
    };
    


  • int main(){
        Person person; // on stack
        Person* person2 = new Person(); // on heap
    }
    

    Whether or not the variables are on the heap or stack depends upon how the object is allocated. So all members of the class will be allocated how the object is allocated.



  • int main(){
        Person person; // on stack
        Person* person2 = new Person(); // on heap
    }
    

    Whether or not the variables are on the heap or stack depends upon how the object is allocated. So all members of the class will be allocated how the object is allocated.



  • However, there is a limit to what you can allocate on the stack. So an object allocated on the stack may not be able to be allocated if its members are large.



  • @fcarney

    Understand! In which case which of the two approaches I used as an example do you consider the best? And what is the reason?


  • Qt Champions 2018

    @fcarney But note that almost all Qt objects consist of a public and a private part (the d-pointer). The private part is always on the heap, even if the corresponding object is on the stack.

    E.g.:

        QString s = "Hello world and a bit more of chars.";
        qDebug() << "sizeof(s) =" << sizeof(s) << "length(s) =" << s.length();
    

    returns: sizeof(s) = 8 length(s) = 36 on my machine, which means it takes 8 bytes stack and (36 + 1) * 2 byte heap for the actual data.

    Regards

    Edit: fixed mistake in the second sentence.



  • @Exotic_Devel said in Class properties in stack or heap?:

    which of the two approaches I used as an example do you consider the best

    It depends upon requirements. If you have an object generator that gives you items allocated on heap then a list of pointers is fine: QList<Object*> m_objList; for example. Doing dynamic allocation for the sake of dynamic allocation is not worthwhile. Keep things simple (ie QString name;) until you have a reason not to (int bigArray[] = new int[1000000000];). Consider using smart pointers as well.



  • @aha_1980 said in Class properties in stack or heap?:

    But note that almost all Qt objects consist of a public and a private part (the d-pointer).

    Sneaky sneaky Qt! Thanks for the tip!


  • Moderators

    Just a nitpick - a QString does not use d-pointer implementation. That's something QObject derived classes do and QString is not one of them. The reason QString itself is small is that it's a variable sized container and the data it stores is resizable. You don't know the data size beforehand, so it requires dynamic reallocation.

    Anyway - there's no point to have pointer members to container classes i.e. QString* or QList<>*. It makes the code harder to read, it makes unnecessary extra dynamic allocations (which is slow) and it makes accessing the data slow because of the double indirection through the pointers. In general if the objects are small (as in couple basic types) and they do the "heavy" data allocations just keep them as direct members, not pointers.

    As for function parameters - if you don't modify the arguments simple types just pass by value i.e. just int instead of const int&. They fit in CPUs registers and adding references to them just makes it bigger and slower because of the indirection. For complex types (e.g. QString) always prefer const &. The const part is important because it conveys your function's interface i.e. clearly states that the function won't modify that parameter. As for pointer parameters - use them to express optionality - if you get a pointer you need to check if it's not null before you use it. With references you don't.

    So by example, this is how you should "read" function arguments:

    void func(Foo param) - function just takes a Foo, Foo is small (fits in registers) so it's better to pass it by value
    void func(const Foo& param) - function just takes a Foo, Foo is not so small so we pass it via pointer (yes, references are just syntactic sugar for pointers)
    void func(Foo& param) - function takes a Foo and modifies it
    void func(Foo* param) - function can take a Foo but it works without it too, it can modify that Foo
    void func(const Foo* param) - function can take a Foo but it works without it too, it won't modify that Foo


  • Qt Champions 2018

    @Chris-Kawa

    Chris Kawa Moderators about 11 hours ago

    Just a nitpick - a QString does not use d-pointer implementation.

    Then have a look here:

    https://code.woboq.org/qt5/qtbase/src/corelib/text/qstring.h.html#QString::d

    Regards


  • Moderators

    @aha_1980 said

    Then have a look here:

    Haha, nice one :) I guess that's what I get for not being more clear.
    The "d pointer" in Qt context usually refers to the Private implementation idiom (or PIMPL). QString does not use it and this is what I meant. It just happens to have a member called d becasue it refers to data.



  • @Chris-Kawa , @aha_1980
    I'd like to know the difference for passing/receiving as function parameter between const QString and const QString&? They both involve passing (64-bit) 8 bytes, they both increment the shared usage of the of string in the allocation table, neither allows modification (copy-on-write). Right?

    Actually, now I think about it, that's wrong, right? const QString& does not increment the reference count, right? And that's a big difference....


  • Moderators

    Passing by const Foo calls copy constructor. Depending on a class this might be cheap or expensive. QString (and all implicitly shared Qt classes) have a mechanism that makes this copy not so expensive - it just copies data pointer and bumps a refcount. This is cheaper than copying whole data, but still more expensive than just copying a pointer when passing via const Foo&. In short - except for small types that don't involve any "copy logic" don't copy when you don't explicitly want a copy.



  • @Chris-Kawa said in Class properties in stack or heap?:

    Anyway - there's no point to have pointer members to container classes i.e. QString* or QList<>*. It makes the code harder to read, it makes unnecessary extra dynamic allocations (which is slow) and it makes accessing the data slow because of the double indirection through the pointers.

    Hello!

    Does this apply to QMap as well?



  • @Exotic_Devel
    Yes, he's suggesting you should only bother to create QMap map variables, you don't need to go QMap *pMap = new QMap(). The QMap structure itself is small, it does not include the mapping table. This will apply to all the classes listed in https://doc.qt.io/qt-5/containers.html#the-container-classes (you'll see QMap there). Plus some others like QString, QByteArray and QVariant, but I don't know where you find a comprehensive list of those. See @Chris-Kawa's post below.


  • Moderators

    @Exotic_Devel In the link I posted is a full list of implicitly shared classes: list of classes. What I said applies to all of them and yes, QMap is one of them.


Log in to reply