Pointer-Based Containers

Along with the STL-like containers described in the previous sections, Qt also provides an additional set of container classes. These classes were developed in the early 1990s for Qt 1.0, before the STL became part of C++, and therefore have their own particular syntax. Because these classes operate on pointers to objects, they are often referred to as pointer-based containers, in contrast to Qt's and the STL's value-based containers. In Qt 4, the pointer-based containers will continue to be available for compatibility, but it is expected that their use will be deprecated in favor of the value-based containers.

The main reason for using the pointer-based classes in newly written Qt code is that a few important functions in Qt 3 rely on them. We have already seen one example of this in Chapter 3, where we iterated over an application's top-level widgets (p.66), and another example in Chapter 6, where we iterated over an application's MDI windows (p. 156).

The main pointer-based containers are QPtrVector, QPtrList, QDict, QAsciiDict, QIntDict, and QPtrDict.

QPtrVector stores a vector of pointers. Here's how we would populate a QPtrVector with five Film objects:

QPtrVector films(5);
films.setAutoDelete(true);
films.insert(0, new Film(4812, "A Hard Day's Night", 85));
films.insert(1, new Film(5051, "Seven Days to Noon", 94));
films.insert(2, new Film(1301, "Day of Wrath", 105));
films.insert(3, new Film(9227, "A Special Day", 110));
films.insert(4, new Film(1817, "Day for Night", 116));

QPtrVector does not provide an append() function, so we must resize the vector ourselves and insert items at specific index positions. In this example, we are using the original Film class, which includes catalog IDs.

One nice feature of Qt's pointer-based containers is the "auto-delete" property. If auto-delete is enabled, Qt takes ownership of all the objects inserted into the container and deletes them when the container is deleted (or when remove() or clear() are used).

To remove an item from the vector, we can call remove() with an index:

films.remove(2);

The remove() operation does not change the size of the vector; instead, the item is set to a null pointer. If auto-delete is on, the item is automatically deleted.

To traverse a QPtrVector, we can simply use indexes:

for (int i = 0; i < (int)films.count(); ++i) {
 if (films[i])
 cerr << films[i]->title().ascii() << endl;
}

We check that the pointer at the given index is not null before using it, in case it has been erased or has never had anything assigned to it.

The QPtrList class stores a list of pointers. We can add new items to a QPtrList by calling append(), prepend(), or insert():

QPtrList films;
films.setAutoDelete(true);
films.append(new Film(4812, "A Hard Day's Night", 85));
films.append(new Film(5051, "Seven Days to Noon", 94));

Pointer lists have a "current" item, which is updated when we call traversal functions such as first(), next(), prev(), and last(). One way to iterate over a list is like this:

Film *film = films.first();
while (film) {
 cerr << film->title().ascii() << endl;
 film = films.next();
}

It's also possible to iterate over a list using at():

for (int i = 0; i < (int)films.count(); ++i)
 cerr << films.at(i)->title().ascii() << endl;

A third option is to use QPtrListIterator.

The QDict, QAsciiDict, QIntDict, and QPtrDict classes are the nearest pointer-based equivalents to map. These classes also operate on keyvalue pairs. The key can be any one of four different types (QString, const char *, int, or void *), depending on which of the four classes is used. Since all four classes provide the same functions, we will just look at QIntDict.

We will use this to store Films of the same type we used with map earlier, using catalog IDs as keys.

QIntDict films(101);
films.setAutoDelete(true);

The QIntDict constructor accepts a number. That number is used internally by the class to determine how many "buckets" it puts the data into. For good performance, that number should be a prime number a little larger than the number of items we expect to hold. A list of the prime numbers smaller than 10,000 is available at http://doc.trolltech.com/3.2/primes.html.

Inserting new items is done with insert(), which accepts a key and a value:

films.insert(4812, new Film("A Hard Day's Night", 85));
films.insert(5051, new Film("Seven Days to Noon", 94));

We can use find() or the [] operator to look up items, remove() to delete an item, and replace() to change the value associated with a given key.

If we call insert() multiple times with the same key, only the most recently inserted item will be accessible. If we call remove(), the items are removed in the reverse order in which they were inserted. To avoid multiple values under the same key, we can use replace() instead of insert().

The entire container can be traversed using an iterator:

QIntDictIterator it(films);
while (it.current()) {
 cerr << it.currentKey() << ": "
 << it.current()->title().ascii() << endl;
 ++it;
}

The iterator provides the current key with currentKey() and the current value with current(). The order in which the items appear is undefined.

Qt provides a special vector-like class, QMemArray, for storing items of basic types like int and double or of structs of basic types. Few applications use it directly; however, its two subclasses QByteArray (QMemArray) and QPointArray (QMemArray) are very common, and we have used them many times in earlier chapters.

For example, here's how to create a QByteArray:

QByteArray bytes(4);
bytes[0] = 'A';
bytes[1] = 'C';
bytes[2] = 'D';
bytes[3] = 'C';

When we create a QMemArray, we can either pass it an initial size or call resize() later. We can then access array entries using the [] operator:

for (int i = 0; i < (int)bytes.size(); ++i)
 cerr << bytes[i] << endl;

We can search for an item using QMemArray::find():

if (bytes.find('A') != -1)
 cerr << "Found" << endl;

A subtle pitfall with QMemArray and its subclasses is that they are explicitly shared. This means that when we create a copy of an object (using the class's copy constructor or its assignment operator), both the original and the copy share the same data. When we modify one of them, the other one is also modified. Explicit sharing should not be confused with implicit sharing, which does not have this problem.

The defensive way to program using QMemArray is to call copy() to force a deep copy of the container when copying it:

duplicate = bytes.copy();

This ensures that no two QMemArray objects point to the same data.

To avoid the inherent problems of explicit sharing, the QMemArray class will probably be deprecated in favor of QValueVector in Qt 4. The QByteArray and QPointArray classes will then use QValueVector as their base class.





C++ GUI Programming with Qt 3
C++ GUI Programming with Qt 3
ISBN: 0131240722
EAN: 2147483647
Year: 2006
Pages: 140
Simiral book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net