Using QString Effectively

From Qt Wiki
Jump to navigation Jump to search

Written By : Girish Ramakrishnan, ForwardBias Technologies

This page explains the purpose of various QString related classes.

QLatin1String : Avoid hidden mallocs in operator "=="

Creating a QString from a C-string may involve a malloc. For example, there is possibly a hidden-malloc cost in the following code.

 if (fruit == "apple") { ... } // possibly hidden malloc

QString provides a QString::operator==(const char *) overload for comparison with C-strings. As explained in QtString, the encoding of C-strings is determined using QTextCodec::setCodecForCStrings(). When no special encoding is set, Qt performs the above comparison by providing a specialized function that compares a Unicode string (fruit) and the Latin-1 string ('apple'). This comparison is fast and requires no mallocs.

However, when QTextCodec::setCodecForCString is set, "apple" gets converted into a QString using QString::fromAscii(). This means that QString will allocate memory for the string "apple" and creates a deep copy of the C-style string before performing the comparison!

Application developers set QTextCodec::setCodecForCString() in main() without realizing that this has the side-effect of a malloc for every single C-style string comparison.

Since comparisons with Latin-1 C-strings are very common in programs, Qt provides a special class called QLatin1String that just holds a pointer to a Latin-1 encoded C-string. In addition, QString provides a QString::operator(const QLatin1String &) overload that calls the specialized function that compares the Unicode string and Latin1-string. We can make the above code assuredly fast by writing it instead as,

    if (fruit == QLatin1String("apple")) {  } // fast and mentions encoding

In Qt's own code, all C-strings comparisons use QLatin1String since the application can choose an arbitrary codec for C-strings.

QStringRef : String manipulation without the malloc

QString has various methods for string manipulations like mid(), left(), right(). All of them create a new QString and hence a malloc/deep copy of data in an existing QString. Instead, QString::midRef(), QString::leftRef() and QString::rightRef() can be used to obtain a QStringRef. A QStringRef is a reference of a portion of a QString. QString also provides many overloads like QString::operator==(const QStringRef &) for optimal use with QStringRef.

QString::reserve and QString::squeeze

It's better to call QString::reserve to allocate extra memory in advance so that every call to QString::append() does not result in a malloc. Extra memory can be reclaimed using QString::squeeze.

QStringBuilder : Fast QString concatenation

The code below requires atleast 2 mallocs. First a malloc to store the result of "(" + type. And then another malloc for appending the ")". The number of mallocs increases with each addition of operator +.

 if (foo.startsWith("(" + type + ")"))

The additional mallocs can be avoided if the length of the final QString is known in advance. Qt 4.6 introduces an internal class called QStringBuilder that "reserves" memory for a concatenation chain in a single shot. It does so by having each of the + operations above return a different class (not QString anymore). This class keeps track of the string's that are being appended and the required memory at each step. At the final step, when the concatenation operation gets converted into a QString it allocates memory in a single shot and copies all the strings in the chain one after another. This feature can be enabled by including <QtCore/QStringBuilder> and using the operator % instead of operator +. One would now write,

 if (foo.startsWith("(" % type % ")"))

operator + can be used instead of operator % by defining QT_USE_QSTRINGBUILDER. See Fast concatenation for more details.

QStringMatcher : Fast string matching