[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-devel
Subject:    QString Tutorial (was: another question about QStrings)
From:       Johannes Sixt <Johannes.Sixt () telecom ! at>
Date:       1997-12-19 21:47:17
[Download RAW message or body]

Hi!

This mail also answers your question about when to use new QString().

On Fri, 19 Dec 1997, Preston Brown wrote:
>when I am making a new QString, and say I am getting the test from a
>QLabel, should I do it like this:
>
>QString tmpString(myLabel->text());
>
>or
>
>QString tmpString((const char *) myLabel->text());
>
>IF the text in myLabel might change in the future, and I don't want the
>QString changing?  I read the documentation to say that the first way will
>just do a shallow copy (i.e. the pointer) and thus if the text of myLabel
>changes, so will the text of the tmpString.  Correct?!?
>
>Basically my concern is this.  I am building up a data structure that has
>a bunch of QStrings as members of it, and I am getting the data from
>QLabels, listboxes, etc. etc.  I don't want my new data structure to
>change if the data in those widgets changes.  I also want the data
>structure to be persistent at the end of the function call where I build
>it.

You are thinkin too much about how a QString is implemented than
about how to use it. You can consider a QString like a basic
type, such as int (but see below). Then your questions are answered
immediately:

Should I use "new QString" to return a string? No! You wouldn't use
"new int" to return an int, would you? (The QString implementation
creates a copy of the local variable that is returned, then destroys
the local variable - exactly the same happens with an int- so the
value doesn't get destroyed).

Should I use "QString x = otherString" or "QString x = otherString.data()"?
No! Not necessary. A copy is made. (But see below.) The QString
documentation is much too detailed. It shouldn't tell you that only a
shallow copy is made; that's an implementation detail. The purpose
of that statement is to tell you that the copy of the QString is done in
a FAST way. (That means that actually the bytes are not copied, but
LOGICALLY you get a copy - it's done through reference counting.)

This means, that you can modify the copy and it will not modify the
original, or vice versa. (The implementation will create a deep copy
of the string only when necessary, namely when you are about to
modify the string-bytes.)

Here's the "below" point. The QString class has a misconception
(call it a bug) that does not always create the deep copy when it
is modified. It manifest in the code (if I remember correctly):

QString x = "abc";
QString y = x;
y += "rst";

that now both x an y contain "abcrst", which shouldn't happen
(x should still be "abc", and y should be "abcrst").

The essence of all this is: Write your code first as if a QString
were an int (like the above). Then go over it again and work
around the QString-misconception:

QString x = "abc";
QString y = x.data();    // force deep copy
y += "rst";

You can and should return QStrings by value:

QString f() {
  QString x = "abc";
  return x;
}

Doing it so is very fast, and it doesn't depend on the
length of the string! You MUST NOT return a const char*:

const char* f() {
  QString x = "abc";
  return x;              // this will fail miserably!
}

(I just mention this for completeness.)

If you have a class that has a QString member and an access
function for it, you have three options:

class X {
  QString x;
public:
  QString text1() { return x; }
  const QString& text2() { return x; }
  const char* text3() { return x; }    // implicit cast
};

I recommend version text2(): It's as fast as text3(), but
faster than text1() (text1 creates a copy, text2 doesn't)
and it provides the client the whole functionality
of a QString, most importantly the length() method to get the
length of the string (which is much faster than strlen(), which the
client would need if only text3() were available).

Some experts among you will argue: Use text3() because it protects
against the QString-misconception; clients can't change my data
inadvertedly.
My argument against that is: It's not class X's fault that QString is
buggy. Everyone who is using QStrings must know about that problem,
so the client can work around it by immediately calling QString::data():

X x;
QString unchanged = x.text2();
QString changed = x.text2().data();
changed += "abc";

Moreover, assume that QString gets fixed finally: Then in the above
code fragment text2() doesn't result in _any_ additional overhead,
whereas text3() triggers an unnecessary deep copy for the variable
"unchanged"!

I want to close with a plea to all you developers: Please write

QString x = foo()->bar(baz).text();

instead of

QString x(foo()->bar(baz).text());

because the first one is MUCH more readable than the second one
and it has EXACTLY the same semantics if foo()->bar(baz).text() is
a QString or reference thereof or a const char*!

So, that was more than I inteded to write...
Thanks for reading this far! I hope it helps to write safe and
efficient code!
-- Hannes

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic