PDA

View Full Version : QString, QStringList and garbage collection...



PaladinKnight
2nd April 2010, 02:59
Hi!

Are QString and QStringList garbage collected garbage collected?

I need to manipulate a QString using both QString methods and a QStringList and I'm wondering if I have to do any cleanup after I processed the QString passed as a parameter...

Thank you!

Nick

Lykurg
2nd April 2010, 06:52
If you create it on the heap, then you have to care about. But since they are implicit shared, simple create them on the stack and all is fine:
void Class::function()
{
QString str;
str = "foo";
myFunctionTakingAString(str);
// or
emit mySignal(str);
}

PaladinKnight
2nd April 2010, 19:08
Hi!

Thank you for your help!


If you create it on the heap, then you have to care about. But since they are implicit shared, simple create them on the stack and all is fine:

I am also a newbie in C++ so I want to make sure I correctly understood what you said.

(I am however familiar with C, Java and assembly language and I am trying to adjust to C++ and Qt using the concepts I learned in these other languages).

OK, from what I've understood creating it on the heap would mean using a "new" like so:



QString test = new QString("This is a test");


Creating it on the stack would be like this:



QString test = "This is a test";


But then I have also seen this, what does this do?:



QString test = QString("This is a test");


(Since there is no new I would assume it does the same thing as the previous one and some people seem to prefer declaring it that way.)

The method I am calling looks something like this (I've simplified it from what it actually is):



QString makeTest(QString str)

// Do some substitution..
str.replace("*", QString("!"));

// Split at the # and trim what's between them
QStringList sl = str.split('#');
QStringList::iterator it;

for (it = sl.begin(); it != sl.end(); ++it)
{
(*it) = (*it).trimmed();
}

// rejoin everything and put it back in the original string
str = sl.join(" ");

// process the string further once it's merged back
// (it's not exactly like that but it does give an idea
// of the kind of further processing done here... Yep, mapping
// the @ back to the separator used above is done on purpose...)

str.replace(QString("@"), QString("#"));

return text;
}


(BTW, from what I have read QStringList is also implicitly shared,)

I don't want to pass the QString by reference as I don't want the processing I'm doing to interfere with the rest of the treatment.

If I understood your reply correctly the method I have shown above (probably) only uses the stack so it should free all of the memory it uses when it terminates, right?? What about the return parameter, is it freed when the calling method terminates?

There is something about implicit sharing I'm not sure I understand though... Should it be able to free the memory allocated even if I had done a "new QString" since it apparently maintains a count of how many referrences there is to the object ?

Thank you very much for your help!

Nick

Lykurg
2nd April 2010, 20:57
OK, from what I've understood creating it on the heap would mean using a "new" like so:



QString test = new QString("This is a test");


Creating it on the stack would be like this:



QString test = "This is a test";


But then I have also seen this, what does this do?:



QString test = QString("This is a test");


(Since there is no new I would assume it does the same thing as the previous one and some people seem to prefer declaring it that way.)
Right. The difference is that one time Qt automatically converts char* to a QString and in the later you just call the constructor direct. You can run into trouble when setting QT_NO_CAST_FROM_ASCII. Then the first will not work. But if you don't set it, no difference at all.



The method I am calling looks something like this (I've simplified it from what it actually is):



QString makeTest(QString str)

// Do some substitution..
str.replace("*", QString("!"));

// Split at the # and trim what's between them
QStringList sl = str.split('#');
QStringList::iterator it;

for (it = sl.begin(); it != sl.end(); ++it)
{
(*it) = (*it).trimmed();
}

// rejoin everything and put it back in the original string
str = sl.join(" ");

// process the string further once it's merged back
// (it's not exactly like that but it does give an idea
// of the kind of further processing done here... Yep, mapping
// the @ back to the separator used above is done on purpose...)

str.replace(QString("@"), QString("#"));

return text;
}


(BTW, from what I have read QStringList is also implicitly shared,)

I don't want to pass the QString by reference as I don't want the processing I'm doing to interfere with the rest of the treatment.

If I understood your reply correctly the method I have shown above (probably) only uses the stack so it should free all of the memory it uses when it terminates, right?? What about the return parameter, is it freed when the calling method terminates?
Yes and no... The memory will (normally) be freed later:



QString makeTest(QString str)
{
QString text; // here memory is allocated
// do something
return text; // Memory is not freed in normal case, because:
}

//...
QString returnValue = makeTest("foo"); // returnValue is taking over the memory of "text"





There is something about implicit sharing I'm not sure I understand though... Should it be able to free the memory allocated even if I had done a "new QString" since it apparently maintains a count of how many referrences there is to the object ?
Ok, my answer was misleading. Implicit sharing (IS) has nothing to do with gc. if you use new you have to delete it. IS means only that different QStrings share data between and deep copies will only be made if really necessary. This is the reason why it is "safe" to pass QString as parameters to function because it is not really "by value". (Only if you change that string in your function of course)


So hopefully to be a little bit clearer now: Qt is pure C++ after all and has no direct gc. Only if you have Classes with QObjects, then child items will be deleted if the parent is deleted:

QObject *parent = new QObject;
QWidget *child1 = new QWidget(parent);
QPushButton *child2 = new QPushButton(parent);
delete parent; // child1 and child2 will be deleted automatically! For all other cases: if you use new, you have to delete them (as you do with parent). If you create any type on the stack, it will be deleted when the scope reaches the end. (and here IS comes into play: it is only deeply deleted if no other (e.g.) QString points to the shared memory.)


Hope it is a little bit clearer now...

Lykurg

PaladinKnight
5th April 2010, 16:22
Hi!


Right. The difference is that one time Qt automatically converts char* to a QString and in the later you just call the constructor direct. You can run into trouble when setting QT_NO_CAST_FROM_ASCII. Then the first will not work. But if you don't set it, no difference at all.


Yes and no... The memory will (normally) be freed later:



QString makeTest(QString str)
{
QString text; // here memory is allocated
// do something
return text; // Memory is not freed in normal case, because:
}

//...
QString returnValue = makeTest("foo"); // returnValue is taking over the memory of "text"




Oops, that's weird... Until I tried to reply I couldn't see your code example... ???

OK, even in this case the memory would eventually get freed though, right?


Ok, my answer was misleading. Implicit sharing (IS) has nothing to do with gc. if you use new you have to delete it. IS means only that different QStrings share data between and deep copies will only be made if really necessary. This is the reason why it is "safe" to pass QString as parameters to function because it is not really "by value". (Only if you change that string in your function of course)

OK...



So hopefully to be a little bit clearer now: Qt is pure C++ after all and has no direct gc.

Yep, I had read that C++ had no gc so that's why I was concerned about it... It's more or less like in good old C when you have/had to "free" memory you had allocated (using malloc, calloc, etc...).


Only if you have Classes with QObjects, then child items will be deleted if the parent is deleted:

QObject *parent = new QObject;
QWidget *child1 = new QWidget(parent);
QPushButton *child2 = new QPushButton(parent);
delete parent; // child1 and child2 will be deleted automatically!

OK, thanks!


For all other cases: if you use new, you have to delete them (as you do with parent). If you create any type on the stack, it will be deleted when the scope reaches the end. (and here IS comes into play: it is only deeply deleted if no other (e.g.) QString points to the shared memory.)

OK, so that's why I had read IS had some influence in it...



Hope it is a little bit clearer now...

Yes, thank you very much!

Have a nice day!

Nick