The Standard Containers As Class Templates by Steve Donovan, From The Book C by Example: "Underc" Learning Edition Mar 22, 2002
Why do generic classes make excellent containers? This excerpt from C by Example (Que, 2001) by Steve Donovan shows some of the consequences of not having them. This article is provided courtesy of Que. This article is excerpted from C by Example (Que , 2001, ISBN: 0789726769), by Steve Donovan.
? How were things handled before templates were available The first problem with creating containers without templates is that they are not type safe Imagine if you had only list
Void Draw_shapes (TG & TG, Const List & ls)
{
List :: item Li;
For (li = ls.begin (); li! = ls.end (); li)
Static_cast
} This looks nasty, and it is nasty. There is no guarantee at runtime that this list contains only pointers to Shape. A program would inevitably decide to put something odd in the list when it was being used for some crucial operation on the other side of the planet (or, indeed, another planet altogether). It is not possible to check all the possibilities in even a moderate-sized application. Type safety is a guarantee that no surprises with odd types can happen-that they will be caught by the compiler, not the customer. Traditional object-oriented languages rely on runtime-type information. Every class derives from some polymorphic base class, usually called Object. In Delphi, for instance, deriving from Object is implicit, so the is operator can always work. Of course, this strategy works in C as well, as long as the ultimate base class Object has at least one virtual method. The class Shape will have to be derived from Object in some way. Then dynamic_cast () will work. The Previou S example becomes this: typedef list
{
List :: item Li;
For (li = ls.begin (); li! = ls.end (); Li) {
Shape * ps = Dynamic_cast
IF (PS! = NULL) PS-> DRAW (TG);
}
} There is now a proper runtime guarantee, at the cost of continuous checking. But what should you do if the object is not a Shape pointer? Surely you should raise an alarm or make a note somewhere. Although this code is safe, it COULD BE Masking An Error Somewhere Else. There Should Only Be Shape Pointers in This List; It isn't considered Particular Clever to Keep Different Kinds of Objects Together.
Note
.
So far, we have only talked about pointers. Languages such as Java and Delphi really deal only with references to objects, but they also have to deal with the common case of just wanting a list of integers or doubles. You can either get a whole number of extra container classes or wrap the simple types themselves as objects, which is what Java does (Delphi programmers have a horrible habit of trying to stuff integers into lists of pointers). This is the second problem related to life without templates. C must deal with value-oriented types such as std :: string. These types do not fit very well into a pointer-list scheme. There is an object-oriented approach to containers of such types, but it is not pretty. You define a special base class-which you could call Linkable-that contains a pointer to another Linkable object. Any class that you inherit from Linkable therefore has the ability to link to another Linkable object, rather like the parade of circus elephants holding the tails of th . Eir fellows You can then run through the list by following pointers, until some stopping condition (in the case of a circular list, the elephants keep going around the ring) But this is awkward;. It means that you have to decide, as part of your design, that your objects are going to be kept in a list There would need to be auxiliary types such as linkable strings The design of the C standard containers answers these two problems, as well as another:.. C standard containers present A Very Similar Interface To The World. with Some Care, Code Can Switch from Using List
in this case, switching to a vector makes perfect sense. The philosophy is that whether you put widgets in a list, vector, deque, map, or set, it should not be a high-level design decision. Note an important property of class templates; not every method is compiled when a template class is instantiated; only methods that are referenced in the code, or are needed to implement those methods, are compiled This can obviously save a lot of dead code (although smart linkers can strip out. such baggage). But this is crucial to the design of classes like list
Class List
...
Void Push_Back (T * P)
{Listp :: Push_back ((void *) p);
T * back ()
{RETURN (T *) listp :: back ();
...