ROOT Containers
ROOT provides a number of
objects
whose function is to hold other objects
(i.e. they are containers).
ROOT calls them collections and provides a number including arrays, lists and
trees. As these collections have a number of features
in common it is worth making some general remarks about them.
Containers: General Characteristics
- Containers hold
pointers
to
TObjects
so:-
- They can only hold objects that
inherit
from TObject.
- As explained in
Concepts: Containers
when asked for an object they return a pointer that has to be be converted
to the correct type. For example, in
loopmacro
there is the statement:-
flsdigit = (REROOT_FLSDigit *) flsdigits->At(idig);
flsdigits is a collection
(TClonesArray)
that is sent its At
message
asking it for the element number idig. It returns a TObject pointer that has
to be
cast
to the
subclass
REROOT_FLSDigit pointer.
- Collections are dynamic, they can grow in size as required.
- Collections inherit from TObject so can themselves be held in collections.
So it is possible to nest one type of collection inside another
to any level to produce structures of arbitrary complexity.
For example the
MINFDetector
object
HasA
TObjArray.
The elements of the array are themselves TObjArrays
of MINFHits.
- Containers don't own the objects that they hold for the very good reason
that the same object could be a member of more than one collection.
Object ownership is
important when it comes to object deleting; if nobody owns the object it
could end up as wasted memory
(i.e. a memory leak).
when no longer needed. If a container is
deleted, its objects are not. The user can force a container to delete its
objects, but that is the user's choice.
Determining the Class of Contained Objects
Most containers may hold heterogeneous collections of objects and then it is
left to the user to correctly cast the TObject pointer to the right class.
Casting to the wrong class will give wrong results and may
well crash the program!. So the user has to be very careful. Often a
container only contains one class of object, but if it really contains a
mixture, it is possible to ask each object about its class using the
InheritsFrom message. For example if MyObject is TObject pointer:-
if ( MyObject->InheritsFrom("REROOT_FLSDigit" ) {
printf("Object is a REROOT_FLSDigit\n");
}
As the name suggests, this test works even if the object is a
subclass
of REROOT_FLSDigit. The member function IsA can be used instead of InheritsFrom
to make the test exact.
Iterators: Processing a Collection
The concept of processing all the members of a collection is generic, i.e.
independent of any specific representation of a collection. To process each
object in a collection needs some type of cursor that is initialised and then
stepped over each member of the collection in turn. Collection objects could
provide this service but there is a snag: as there is only one collection
object per collection there would only be one cursor.
Instead, to permit the use of as many
cursors as required, they are made separate classes called an iterators. For
each type of container organisation there is an associated iterator class that
knows how to sequentially retrieve each member in turn. The relationship
between a collection and its iterator is very close and may require that the
iterator has full access to the collection
(i.e. it is a friend).
Of course specific collections e.g. arrays may have additional ways of
systematically processing every member, but the concept of iterating applies
to all.
Foundation Classes
All collections are based on the fundamental classes:
TCollection,
TIterator
and
TIter.
They provide an excellent example of the
Concept: Interfaces & Abstract Classes.
TCollection and TIterator are so generic that it is not possible to create
objects from them; they are only used as
base classes
for other classes
(i.e. they are abstract base classes).
TCollection
This class defines the minimum set of
member functions
that all collections must support. These include:-
- Add
Adds another object to the collection.
- Capacity
Returns the current size of the collection.
- Clear
Clears out the collection, but does not delete the removed objects.
- Delete
Clears out the collection, and deletes the removed objects. This should only be
used if the collection owns its objects (which is not normally the case).
- FindObject
Find an object given either its name or its address.
- GrowBy
Extend the collection.
- MakeIterator
Returns the iterator associated with the collection.
- Remove
Removes an object from the collection.
TIterator
This class defines the minimum set of member functions
that all iterators must support. These include:-
- Reset
Reset the iterator so that Next returns the first object.
- Next
Return the next member of the set or 0
(i.e. the null pointer)
if no more members.
TIter
As stated above, the TIterator class is abstract; it is not possible to create
TIterator objects. However, it should be possible to write generic code to
process all members of a collection so there is a need for a generic iterator
object. A TIter object act as generic iterator. It provides the same
Next and Reset messages as TIterator although it
has no idea how to support them! It works as follows:-
-
To create a TIter object its
constructor
must be passed an object that inherits from TCollection. The TIter
constructor sends this object its MakeIterator message to get the
appropriate iterator object that inherits from TIterator.
- When TIter is passed the Reset or Next messages it simply passes them on
to the iterator it has.
So TIter simply acts as a wrapper for an object of a
concrete class
inheriting from TIterator.
To see this working in practice, consider the TObjArray collection. Its
associated iterator is TObjArrayIter. Suppose myarray is a pointer to a
TObjArray i.e.
TObjArray *myarray;
which contains MyClass objects. To create a TIter object called myiter:-
TIter myiter(myarray);
As shown in the diagram, this results in several messages:-
Now to define a pointer for MyClass objects and set it to each member of the
of the TObjArray:-
MyClass *myobject;
while( myobject=(MyClass *)myiter.Next() {
\\ Process myobject
}
The heart of this is the myiter.Next() expression which does the following:-
Sometimes the TIter object is called next, and then instead of writing
next.Next()
which is legal, but looks rather odd, iteration is written as:-
next()
This works because the function operator () is defined for the TIter class to
be equivalent to the Next message.
ForEach Macro
The situation in which the same message has to be sent to every object in a
collection is so common, that the
C++ macro
ForEach, which is defined in the TCollection.h
header file,
which will generate all the code (although to be honest is not very readable
code!) to do it. For example to send every MyClass object the DoIt(123)
message:-
myarray->ForEach(MyClass,DoIt)(123)
ForEach takes two arguments, the class of the objects held in the container
and the message to be sent to them. The macro must be preceded by the name
of the collection with the appropriate member selection operator. So, if
myarray were a collection rather than a pointer to a collection, the line
would read:-
myarray.ForEach(MyClass,DoIt)(123)
The macro must be followed by the arguments to be supplied to the member
function.
Go Back to the
The ROOT Crib Top Page
If you have any comments about this page please send them to
Nick West