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); };
-
-
@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.
-
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*
orQList<>*
. 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 ofconst 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 preferconst &
. Theconst
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 -
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
-
@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 betweenconst QString
andconst 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.... -
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 viaconst 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 createQMap map
variables, you don't need to goQMap *pMap = new QMap()
. TheQMap
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 seeQMap
there). Plus some others likeQString
,QByteArray
andQVariant
,but I don't know where you find a comprehensive list of those.See @Chris-Kawa's post below. -
@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.