This document was uploaded by user and they confirmed that they have the permission to share
it. If you are author or own the copyright of this book, please report to us by using this DMCA
report form. Report DMCA
Overview
Download & View Visual Cpp Template Library as PDF for free.
Visual C++ 4.2 Standard Template Library Tutorial Kalindi Sanghrajka, Rick Troemel, Nevenka Subotic-Burina, and Linda Koontz October 1996
CONTENTS 1. About This Tutorial 2. Introduction to the Standard C++ Library 3. C++ Template Overview 4. The Standard Template Library 5. Sequence Containers 6. Associative Containers 7. Iterators 8. Container Adapters 9. The C++ String Class 10. Function Objects 11. Standard Template Library Algorithms 12. Standard C++ Library Language Support 13. Exception Handling 14. Standard C++ Library Diagnostics 15. Appendix A: STL References and Further Reading 16. Appendix B: STL Container Class Definitions 17. Appendix C: STL Container Class Methods 18. Appendix D: Allocator Class
1. About This Tutorial 1.1 Objectives This tutorial will provide an overview of the Standard C++ Libraries as implemented in Microsoft® Visual C++® version 4.2, with an emphasis on the Standard Template Library. Upon completion of this tutorial, you will be able to: •
Identify the different components of the Standard C++ Library.
•
Write simple programs using Visual C++ version 4.2. that use the Standard Template Library components.
•
Understand the Standard C++ Library implementation of exception handling and language support in Visual C++ version 4.2.
1.2 Prerequisites This tutorial assumes that you understand basic C++ programming. It is also necessary to have a good understanding of C++ templates.
2. Introduction to the Standard C++ Library 2.1 Introduction Every C++ programmer has probably, at one time or another, written a linked list or set, searching and sorting routines. Most likely, the programmer has re-invented the wheel for every new user-defined data type. Design changes are not easy to implement in such cases. Maintaining such code is not very easy, either. If the common programming components were part of the C++ language, programmers would not need to "re-invent the wheel." Finally, the C++ language provides you with general purpose components for common programming tasks through the Standard C++ Library. The Standard C++ Library provides powerful and flexible containers, programmable algorithms, and other components that are efficient and extensible. The facilities provided by the Standard C++ Library are as follows: •
Language support: Provides common type definitions used throughout the library such as characteristics of predefined types, functions supporting start and termination of C++ programs, support for dynamic memory allocation, support for dynamic type identification, support for exception processing, and other runtime support.
•
Diagnostics: Includes components for reporting several kinds of exceptional conditions, components for documenting program assertions, and a global variable for error number codes.
•
General utilities: Includes components used in other elements of the Standard C++ Library. These components may also be used by any C++ programs. This category also includes components used by the Standard Template Library (STL) and function objects, dynamic memory management utilities, and date/time utilities. This category also includes memory management components from the C library.
•
Strings: Includes components for manipulating sequences of characters, where characters may be of type char, w_char, or of a type defined in a C++ program. The library provides a class template, basic_string, that defines the basic properties of strings. The string and wstring types are predefined template instantiations provided by the library.
•
Localization: Includes internationalization support for character classification and string collation; numeric, monetary, and date/time formatting and parsing; and message retrieval.
•
The Standard Template Library (STL): Provides a C++ program access to the most widely used algorithms and data structures. STL headers can be grouped into three major organizing concepts: containers, iterators, and algorithms. Containers are template classes that provide powerful and flexible ways to organize data: for example, vectors, lists, sets and maps. Iterators are the glue that pastes together algorithms and containers. STL provides a large set of programmable algorithms to handle sorting, searching, and other common tasks.
•
Numerics: Includes components to perform seminumerical operations and components for complex number types, numeric arrays, generalized numeric algorithms, and facilities included from the ISO C library.
•
Input/output: Includes components for forward declarations of iostreams, predefined iostream objects, base iostream classes, stream buffering, stream formatting and manipulators, string streams, and file streams.
The Standard C++ Library also incorporates the Standard C Library.
2.2 Using the Standard C++ Libraries in Visual C++ 4.2 Microsoft® Visual C++® version 4.2 provides the Standard C++ Library facilities through included files and associated static and dynamic libraries. A C++ program can use the different components of the Standard C++ Library by including the required header and linking with the appropriate static or dynamic library.
Tables 1 and 2 list all the Standard C++ Library headers and the associated static and dynamic libraries provided by Visual C++ 4.2. Table 1. The Standard C++ Library Headers ALGORITHM
BITSET
CASSERT
CCTYPE
CERRNO
CFLOAT
CISO646
CLIMITS
CLOCALE
CMATH
COMPLEX
CSETJMP
CSIGNAL
CSTDARG
CSTDDEF
CSTDIO
CSTDLIB
CSTRING
CTIME
CWCHAR
CWCTYPE
DEQUE
EXCEPTION
FSTREAM
FUNCTIONAL IOMANIP
IOS
IOSFWD
IOSTREAM
ISTREAM
ITERATOR
LIMITS
LIST
LOCALE
MAP
MEMORY
NEW
NUMERIC
OSTREAM
QUEUE
SET
SSTREAM
STACK
STDEXCEPT
STREAMBUF
STRING
STRSTREAM
TYPEINFO
UTILITY
VALARRAY
VECTOR
XIOSBASE
XLOCALE
XLOCINFO
XLOCMON
XLOCNUM
XLOCTIME
XMEMORY
XSTDDEF
XSTRING
XTREE
XUTILITY
Note The Standard C++ Library headers do not have an “.h” extension. This is in accordance with the latest C++ working papers. Visual C++ 4.2 includes the following static and dynamic libraries (in addition to the Microsoft Class Library [MFC]): •
Basic C run-time library
•
Standard C++ Library from Plum Hall
•
Old iostream library
Note With Visual C++ 4.2, the iostream support has been pulled out of the C run-time library and exists as an independent entity. Now Visual C++ has the following libraries: Table 2. Static and Dynamic Libraries Included with Microsoft Visual C++ 4.2 Library types and related compiler switches
Basic C run-time library
Standard C++ Library
Old iostream library
Single Threaded (ML)
LIBC.LIB
LIBCP.LIB
LIBCI.LIB
Multithreaded (MT)
LIBCMT.LIB
LIBCPMT.LIB
LIBCIMT.LIB
Multithreaded DLL version (MD)
MSVCRT.LIB (import library for MSVCRT.DLL)
MSVCPRT.LIB (also uses MSVCRT.DLL)
MSVCIRT.LIB (import library for MSVCIRT.DLL)
Debug Single Threaded (MLd)
LIBCD.LIB
LIBCPD.LIB
LIBCID.LIB
Debug Multithreaded (MTd)
LIBCMTD.LIB
LIBCPMTD.LIB
LIBCIMTD.LIB
Debug Multithreaded DLL (MDd)
MSVCRTD.LIB (import library for MSVCRTD.DLL)
MSVCPRTD.LIB (also uses MSVCRTD.DLL)
MSVCIRTD.LIB (import library for MSVCIRTD.DLL)
Case 1. Consider the following sample C++ program where test.cpp uses the Standard C++ Library iostream to print "Hello World". // test.cpp #include void main() { cout << "Hello World" << endl ; }
Building test.cpp using
Will cause test.cpp to link with
cl /ML /GX test.cpp
LIBC.LIB, LIBCP.LIB
cl /MLd /GX test.cpp
LIBCD.LIB, LIBCPD.LIB
cl /MT /GX test.cpp
LIBCMT.LIB, LIBCPMT.LIB
cl /MTd /GX test.cpp
LIBCMTD.LIB, LIBCPMTD.LIB
cl /MD /GX test.cpp
MSVCRT.LIB, MSVCPRT.LIB
cl /MDd /GX test.cpp
MSVCRTD.LIB, MSVCPRTD.LIB
In Case 1, test.cpp used the Standard C++ Library input/output component to print "Hello World." The program just includes the Standard C++ Library header . When compiling the program, specify a run-time library option: /ML[d],/MT[d], or /MD[d]. The program will then link with a basic run-time library (for example, LIBC.LIB with the /ML option) and a Standard C++ Library (for example, LIBCP.LIB with the /ML option). The /GX option enables exception handling. Exception handling must be enabled for any programs that use the Standard C++ Library. It is important to remember that starting with Visual C++ 4.2, a C++ program, depending on the run-time library compiler option specified (/ML[d],/MT[d], or /MD[d]), will always link with one Basic C run-time library and, depending on headers included, will link with either a Standard C++ Library (as in the case 1), an old iostream library (as in Case 3), or neither (as in Case 2). Case 2. Consider the following sample program: // test.cpp void main() { }
Building test.cpp using
Will cause test.cpp to link with
cl /ML test.cpp
LIBC.LIB
cl /MLd test.cpp
LIBCD.LIB
cl /MT test.cpp
LIBCMT.LIB
cl /MTd test.cpp
LIBCMTD.LIB
cl /MD test.cpp
MSVCRT.LIB
cl /MDd test.cpp
MSVCRTD.LIB
Case 3. Consider the following sample program: // test.cpp #include void main() { }
Building test.cpp using
Will cause test.cpp to link with
cl /ML test.cpp
LIBC.LIB, LIBCI.LIB
cl /MLd test.cpp
LIBCD.LIB, LIBCID.LIB
cl /MT test.cpp
LIBCMT.LIB, LIBCIMT.LIB
cl /MTd test.cpp
LIBCMTD.LIB, LIBCIMTD.LIB
cl /MD test.cpp
MSVCRT.LIB, MSVCIRT.LIB
cl /MDd test.cpp
MSVCRTD.LIB, MSVCIRTD.LIB
3. C++ Template Overview 3.1 Introduction Many C++ programs use common data structures such as stacks, queues, and lists. Imagine a program that requires a queue of customers and a queue of messages. You could easily implement a queue of customers, and then take the existing code and implement a queue of messages. If the program grows and there is a need for a queue of orders you could take the queue of messages and convert it to a queue of orders. But what if you need to make some changes to the queue implementation? This would not be a very easy task because the code has been duplicated in many places. Re-inventing source code is not an intelligent approach in an object-oriented environment that encourages reusability. It seems to make more sense to implement a queue that can contain any arbitrary type rather than duplicating code. How does one do that? The answer is to use Type Parameterization, more commonly referred to as Templates. C++ templates allow one to implement a generic Queue template that has a T type parameter. T can be replaced with actual types. For example, if the type parameter is , C++ will generate the class Queue. Therefore, changing the implementation of the Queue becomes relatively simple. In our example, once the changes are implemented in the template Queue, they are immediately reflected in the classes Queue, Queue<Messages>, and Queue. Templates are very useful when implementing generic constructs such as vectors, stacks, lists, and queues that can be used with any arbitrary type. C++ templates provide a way to reuse source code, as opposed to inheritance and composition, which provide a way to reuse object code. C++ provides two types of templates: class templates and function templates. Use function templates to write generic functions: for example, searching and sorting routines that can be used with arbitrary types. The Standard Template Library generic algorithms have been implemented as function templates and the containers have been implemented as class templates.
3.2 Class Templates 3.2.1 Implementing a class template A class template definition looks like a regular class definition, except it is prefixed by the keyword template. For example, here is the definition of a class template for a stack: template class Stack { public:
Stack(int = 10) ; ~Stack() { delete [] stackPtr ; } int push(const T&); int pop(T&) ; int isEmpty()const { return top == -1 ; } int isFull() const { return top == size - 1 ; }
private:
} ;
int size ; // number of elements on Stack. int top ; T* stackPtr ;
T is any type parameter, for example, Stack, where Token is a user defined class. T does not have to be a class type as implied by the keyword class. For example, Stack and Stack<Message*> are valid instantiations, even though int and Message* are not classes. 3.2.2 Implementing class template member functions Implementing template member functions is somewhat different than implementing the regular class member functions. The declarations and definitions of the class-template member functions should all be in the same header file. Why do the declarations and definitions need to be in the same header file? Consider the following: //B.h template class b { public: b() ; ~b() ; } ;
//main.cpp #include “B.h” void main() { b bi ; b bf ; }
When compiling B.cpp, the compiler has both the declarations and the definitions available. At this point, the compiler does not need to generate any definitions for template classes, since there are no instantiations. When the compiler compiles main.cpp, there are two instantiations: template classes B and B. At this point, the compiler has the declarations but no definitions! While implementing class-template member functions, the definitions are prefixed by the keyword template. Here is the complete implementation of the Stack class template: //stack.h #pragma once template class Stack { public:
Stack(int = 10) ; ~Stack() { delete [] stackPtr ; } int push(const T&); int pop(T&) ; // pop an element off the stack int isEmpty()const { return top == -1 ; } int isFull() const { return top == size - 1 ; }
private:
} ;
int size ; // Number of elements on Stack int top ; T* stackPtr ;
//Constructor with the default size 10 template Stack::Stack(int s) {
}
size = s > 0 && s < 1000 ? s : 10 ; top = -1 ; // initialize stack stackPtr = new T[size] ;
//Push an element onto the Stack. template int Stack::push(const T& item) { if (!isFull()) { stackPtr[++top] = item ; return 1 ; // push successful } return 0 ; // push unsuccessful
} //Pop an element off the Stack. template int Stack::pop(T& popValue) {
}
if (!isEmpty()) { popValue = stackPtr[top--] ; return 1 ; // pop successful } return 0 ; // pop unsuccessful
3.2.3 Using a class template Using a class template is very easy. Create the required classes by plugging in the actual type for the type parameters. This process is commonly known as instantiating a class. Here is a sample driver class that uses the Stack class template: #include #include “stack.h” void main() { typedef Stack FloatStack ; typedef Stack IntStack ; FloatStack fs(5) ; float f = 1.1 ; cout << "Pushing elements onto fs" << endl ; while (fs.push(f)) { cout << f << ' ' ; f += 1.1 ; } cout << endl << "Stack Full." << endl << endl << "Popping elements from fs" << endl ; while (fs.pop(f)) cout << f << ' ' ; cout << endl << "Stack Empty" << endl ; cout << endl ; IntStack is ; int i = 1.1 ; cout << "Pushing elements onto is" << endl ; while (is.push(i)) { cout << i << ' ' ; i += 1 ; } cout << endl << "Stack Full" << endl << endl << "Popping elements from is" << endl ;
while (is.pop(i)) cout << i << ' ' ; cout << endl << "Stack Empty" << endl ;
}
Here is the output: Pushing elements onto 1.1 2.2 3.3 4.4 5.5 Stack Full. Popping elements from 5.5 4.4 3.3 2.2 1.1 Stack Empty Pushing elements onto 1 2 3 4 5 6 7 8 9 10 Stack Full Popping elements from 10 9 8 7 6 5 4 3 2 1 Stack Empty
fs fs is is
In the above example we defined a class template Stack. In the driver program we instantiated a Stack of float (FloatStack) and a Stack of int(IntStack). Once the template classes are instantiated, you can instantiate objects of that type (for example, fs and is.) A very good programming practice is to use typedef while instantiating template classes. Then throughout the program, one can use the typedef name. There are two advantages: •
typedefs are very useful when “templates of templates” are used. For example, when instantiating an int STL vector, you could use:
• •
typedef vector > INTVECTOR ;
If the template definition changes, simply change the typedef definition.
This practice is especially helpful when using STL components. There are many implementations of STL available, some of which are incompatible. The implementation in Visual C++ 4.2 may change in future versions. For example, currently the definition of the vector required you to specify an allocator parameter: typedef vector > INTVECTOR ; INTVECTOR vi1 ;
In a future version, the second parameter may not be required, for example, typedef vector INTVECTOR ; INTVECTOR vi1 ;
Imagine how many changes would be required if there was no typedef!
3.3 Sharing Data Using Static Members
Each instantiation of a class template has it’s own static members that are shared with other objects of that instantiation. This is true of static data members and static member functions.
3.4 Function Templates To perform identical operation for each type of data compactly and conveniently, use function templates. You can write a single function template definition. Based on the argument types provided in calls to the function, the compiler automatically instantiates separate object code functions to handle each type of call appropriately. The STL algorithms are implemented as function templates. Function templates are implemented like regular functions, except they are prefixed with the keyword template. Using function templates is very easy; just use them like regular functions. Here is a sample with a function template: #include //max returns the maximum of the two elements template T max(T a, T b) { return a > b ? a : b ; } void main() { cout << "max(10, 15) = " << max(10, 15) << endl ; cout << "max('k', 's') = " << max('k', 's') << endl ; cout << "max(10.1, 15.2) = " << max(10.1, 15.2) << endl ; }
Here is the output: max(10, 15) = 15 max('k', 's') = s max(10.1, 15.2) = 15.2
3.5 Template Specialization In some cases it is possible to override the template-generated code by providing special definitions for specific types. This is called template specialization. For example: #include //max returns the maximum of the two elements of type T, where T is a //class or data type for which operator> is defined. template T max(T a, T b) { return a > b ? a : b ; } void main()
Here is the output: max(10, 15) = 15 max('k', 's') = s max(10.1, 15.2) = 15.2 max("Aladdin", "Jasmine") = Aladdin
Not quite the expected results! Why did that happen? The function call max(“Aladdin”, “Jasmine”) causes the compiler to generate code for max(char*, char*), which compares the addresses of the strings! To correct special cases like these or to provide more efficient implementations for certain types, one can use template specializations. The above example can be rewritten with specialization as follows: #include #include <string.h> //max returns the maximum of the two elements template T max(T a, T b) { return a > b ? a : b ; } // Specialization of max for char* char* max(char* a, char* b) { return strcmp(a, b) > 0 ? a : b ; } void main() { cout << "max(10, 15) = " << max(10, 15) << endl ; cout << "max('k', 's') = " << max('k', 's') << endl ; cout << "max(10.1, 15.2) = " << max(10.1, 15.2) << endl ; cout << "max(\"Aladdin\", \"Jasmine\") = " << max("Aladdin", "Jasmine") << endl ; }
Here is the output: max(10, 15) = 15 max('k', 's') = s max(10.1, 15.2) = 15.2 max("Aladdin", "Jasmine") = Jasmine
3.6 Templates and Friends
Friendship can be established between a class template and a global function, a member function of another class (possibly a template class), or even an entire class (possibly a template class). Table 3 lists the results of declaring different kinds of friends of a class: Table 3. Friend Declarations Class Template
Friend declaration in class template X
Result of giving friendship
template class class friend void f1() ; X
makes f1() a friend of all instantiations of template X. For example, f1() is a friend of X, X, and X.