Value containers are containers of uniform (same-typed) values, for example, QString, byte, int, float, etc. Pointer containers are containers of pointers to (polymorphic commonly typed) objects. They can be managed or unmanaged.
Both kinds of containers can grow at runtime by allocating additional heap memory as needed. This is always done in an exception-safe way, so you don't need to worry about possible memory leaks.
In the case of pointer containers to heap objects, however, one must decide which class is responsible for managing the heap objects. UML diagrams can distinguish between managed and unmanaged containers by using composite (filled diamond) and aggregate (empty diamond) connectors, as shown in Figure 10.2.
Figure 10.2. Aggregates and compositions
In general, we can say that a managed container is a composite, because the container manages its pointed-to objects. In other words, when a composite is destroyed, it destroys (cleans up) its entire self (because the smaller objects are part of its composition).
When one object embeds another as a sub-object, it is also considered a composition.
In Figure 10.2, there are two kinds of Customer containers, CustomerList and CustDb. CustDb and CustomerList both reuse template containers. CustomerList objects are aggregatestemporary structures to hold the results of a query, or a user selection. CustDb, on the other hand, is a singleton composite that manages all of the Customer objects that exist.
In the case of the Customer and Address relationship, this diagram indicates that one or more Address objects should be associated with a particular Customer. When the Customer object is destroyed, it is reasonable to destroy all of its Address objects at the same time. Thus, the Customer object manages its Addresses, which gives us another example of a composite relationship.
This suggested design does impose some limitations on possible use of Address; in particular, there is no easy way to find all customers at a particular address. If Address and Customer were independently managed, then we could form bidirectional relationships between the classes. |
Typically, a managed container deletes any heap objects it "owns" when the container itself is destroyed. With a Qt container of pointers, one can use qDeleteAll(container), an algorithm that calls delete on each element in the container.
Copying a managed container can be defined in a number of ways:
When a container only provides an indexing or reference navigation mechanism to its objects we call it an aggregate container.
In this case, the container does not manage its objects, it only provides a convenient way to access them. When an aggregate container is copied, only references to the collected objects are copied. When an aggregate container is deleted, only the references are removed. There is no impact on the underlying objects in the container.
A managed container is a composition, and an unmanaged container of objects is usually (but not always) represented in a UML diagram as aggregation. |
Exercise: Managed Containers, Compositions and Aggregates
This exercise involves designing some data types to represent a deck and a hand of cards. The following UML diagram suggests one way of representing them.
Here are some hints:
srandom(time(0));
Example 10.6. src/cardgame/datastructure/cardgame-client.cpp
#include "carddeck.h" #include using namespace qstd; <-- 1 int main() { CardDeck deck; CardHand hand; int handSize, playerScore, progScore; cout << "How many cards in a hand? " << flush; handSize = promptInt(); do { hand = deck.deal(handSize); cout << "Here is your hand:" << endl; cout << hand.toString() << endl; playerScore = hand.getValue(); cout << QString("Your score is: %1 points.") .arg(playerScore) << endl; // Now a hand for the dealer: hand = deck.deal(handSize); progScore = hand.getValue(); cout << "Here is my hand:" << endl; cout << hand.toString() << endl; cout << QString("My score is: %1 points.") .arg(progScore) << endl; cout << QString("%1 win!!") .arg((playerScore > progScore)?"You":"I") << endl; } while(more("hand")); }
|
Part I: Introduction to C++ and Qt 4
C++ Introduction
Classes
Introduction to Qt
Lists
Functions
Inheritance and Polymorphism
Part II: Higher-Level Programming
Libraries
Introduction to Design Patterns
QObject
Generics and Containers
Qt GUI Widgets
Concurrency
Validation and Regular Expressions
Parsing XML
Meta Objects, Properties, and Reflective Programming
More Design Patterns
Models and Views
Qt SQL Classes
Part III: C++ Language Reference
Types and Expressions
Scope and Storage Class
Statements and Control Structures
Memory Access
Chapter Summary
Inheritance in Detail
Miscellaneous Topics
Part IV: Programming Assignments
MP3 Jukebox Assignments
Part V: Appendices
MP3 Jukebox Assignments
Bibliography
MP3 Jukebox Assignments