More Books
Effective C++ 55 Specific Ways to Improve Your Programs and Designs
Effective C++ Third Edition 55 Specific Ways to Improve Your Programs and Designs
Table of Contents
Copyright
Praise for Effective C++, Third Edition
Addison-Wesley Professional Computing Series
Preface
Acknowledgments
Introduction
Terminology
Chapter 1. Accustoming Yourself to C++
Item 1: View C++ as a federation of languages
Item 2: Prefer consts, enums, and inlines to #defines
Item 3: Use const whenever possible
Item 4: Make sure that objects are initialized before they're used
Chapter 2. Constructors, Destructors, and Assignment Operators
Item 5: Know what functions C++ silently writes and calls
Item 6: Explicitly disallow the use of compiler-generated functions you do not want
Item 7: Declare destructors virtual in polymorphic base classes
Item 8: Prevent exceptions from leaving destructors
Item 9: Never call virtual functions during construction or destruction
Item 10: Have assignment operators return a reference to *this
Item 11: Handle assignment to self in operator=
Item 12: Copy all parts of an object
Chapter 3. Resource Management
Item 13: Use objects to manage resources.
Item 14: Think carefully about copying behavior in resource-managing classes.
Item 15: Provide access to raw resources in resource-managing classes.
Item 16: Use the same form in corresponding uses of new and delete.
Item 17: Store newed objects in smart pointers in standalone statements.
Chapter 4. Designs and Declarations
Item 18: Make interfaces easy to use correctly and hard to use incorrectly
Item 19: Treat class design as type design
Item 20: Prefer pass-by-reference-to-const to pass-by-value
Item 21: Don't try to return a reference when you must return an object
Item 22: Declare data members private
Item 23: Prefer non-member non-friend functions to member functions
Item 24: Declare non-member functions when type conversions should apply to all parameters
Item 25: Consider support for a non-throwing swap
Chapter 5. Implementations
Item 26: Postpone variable definitions as long as possible.
Item 27: Minimize casting.
Item 28: Avoid returning "handles" to object internals.
Item29: Strive for exception-safe code.
Item 30: Understand the ins and outs of inlining.
Item31: Minimize compilation dependencies between files.
Chapter 6. Inheritance and Object-Oriented Design
Item 32: Make sure public inheritance models "is-a."
Item 33: Avoid hiding inherited names
Item 34: Differentiate between inheritance of interface and inheritance of implementation
Item 35: Consider alternatives to virtual functions
Item 36: Never redefine an inherited non-virtual function
Item 37: Never redefine a function's inherited default parameter value
Item 38: Model "has-a" or "is-implemented-in-terms-of" through composition
Item 39: Use private inheritance judiciously
Item 40: Use multiple inheritance judiciously
Chapter 7. Templates and Generic Programming
Item 41: Understand implicit interfaces and compile-time polymorphism
Item 42: Understand the two meanings of typename
Item 43: Know how to access names in templatized base classes
Item 44: Factor parameter-independent code out of templates
Item 45: Use member function templates to accept "all compatible types."
Item 46: Define non-member functions inside templates when type conversions are desired
Item 47: Use traits classes for information about types
Item 48: Be aware of template metaprogramming
Chapter 8. Customizing new and delete
Item 49: Understand the behavior of the new-handler
Item 50: Understand when it makes sense to replace new and delete
Item 51: Adhere to convention when writing new and delete
Item 52: Write placement delete if you write placement new
Chapter 9. Miscellany
Item 53: Pay attention to compiler warnings.
Item 54: Familiarize yourself with the standard library, including TR1
Item.55: Familiarize yourself with Boost.
Appendix A. Beyond Effective C++
Appendix B. Item Mappings Between Second and Third Editions
Index
index_SYMBOL
index_A
index_B
index_C
index_D
index_E
index_F
index_G
index_H
index_I
index_J
index_K
index_L
index_M
index_N
index_O
index_P
index_R
index_S
index_T
index_U
index_V
index_W
index_X
index_Z

Item 54: Familiarize yourself with the standard library, including TR1

The standard for C++ — the document defining the language and its library — was ratified in 1998. In 2003, a minor "bug-fix" update was issued. The standardization committee continues its work, however, and a "Version 2.0" C++ standard is expected around 2008 or so. The uncertainty regarding that date explains why people usually refer to the next version of C++ as "C++0x" — the 200x version of C++.

C++0x will probably include some interesting new language features, but most new C++ functionality will come in the form of additions to the standard library. We already know what some of the new library functionality will be, because it's been specified in a document known as TR1 ("Technical Report 1" from the C++ Library Working Group). The standardization committee reserves the right to modify TR1 functionality before it's officially enshrined in C++0x, but significant changes are unlikely. For all intents and purposes, TR1 heralds the beginning of a new release of C++ — what we might call standard C++ 1.1. You can't be an effective C++ programmer without being familiar with TR1 functionality, because that functionality is a boon to virtually every kind of library and application.

Before surveying what's in TR1, it's worth reviewing the major parts of the standard C++ library specified by C++98:

  • The Standard Template Library (STL), including containers (vector, string, map, etc.); iterators; algorithms (find, sort, TRansform, etc.); function objects (less, greater, etc.); and various container and function object adapters (stack, priority_queue, mem_fun, not1, etc.).

  • Iostreams, including support for user-defined buffering, internationalized IO, and the predefined objects cin, cout, cerr, and clog.

  • Support for internationalization, including the ability to have multiple active locales. Types like wchar_t (usually 16 bits/char) and wstring (strings of wchar_ts) facilitate working with Unicode.

  • Support for numeric processing, including templates for complex numbers (complex) and arrays of pure values (valarray).

  • An exception hierarchy, including the base class exception, its derived classes logic_error and runtime_error, and various classes that inherit from those.

  • C89's standard library. Everything in the 1989 C standard library is also in C++.

If any of the above is unfamiliar to you, I suggest you schedule some quality time with your favorite C++ reference to rectify the situation.

TR1 specifies 14 new components (i.e., pieces of library functionality). All are in the std namespace, more precisely, in the nested namespace tr1. The full name of the TR1 component shared_ptr (see below) is thus std::tr1::shared_ptr. In this book, I customarily omit the std:: when discussing components of the standard library, but I always prefix TR1 components with tr1::.

This book shows examples of the following TR1 components:

  • The smart pointers TR1::shared_ptr and tr1::weak_ptr. TR1::shared_ptrs act like built-in pointers, but they keep track of how many tr1::shared_ptrs point to an object. This is known as reference counting. When the last such pointer is destroyed (i.e., when the reference count for an object becomes zero), the object is automatically deleted. This works well in preventing resource leaks in acyclic data structures, but if two or more objects contain tr1::shared_ptrs such that a cycle is formed, the cycle may keep each object's reference count above zero, even when all external pointers to the cycle have been destroyed (i.e., when the group of objects as a whole is unreachable). That's where TR1::weak_ptrs come in. TR1::weak_ptrs are designed to act as cycle-inducing pointers in otherwise acyclic tr1::shared_ptr-based data structures. tr1::weak_ptrs don't participate in reference counting. When the last tr1::shared_ptr to an object is destroyed, the object is deleted, even if tr1::weak_ptrs continue to point there. Such tr1::weak_ptrs are automatically marked as invalid, however.

    tr1::shared_ptr may be the most widely useful component in TR1. I use it many times in this book, including in Item 13, where I explain why it's so important. (The book contains no uses of tr1::weak_ptr, sorry.)

  • tr1::function, which makes it possible to represent any callable entity (i.e., any function or function object) whose signature is consistent with a target signature. If we wanted to make it possible to register callback functions that take an int and return a string, we could do this:

    
    void registerCallback(std::string func(int));    // param type is a function
    
                                                     // taking an int and
    
                                                     // returning a string
    
    

    The parameter name func is optional, so registerCallback could be declared this way, instead:

    
    void registerCallback(std::string (int));       // same as above; param
    
                                                    // name is omitted
    
    

    Note here that "std::string (int)" is a function signature.

    tr1::function makes it possible to make registerCallback much more flexible, accepting as its argument any callable entity that takes an int or anything convertible to an int and that returns a string or anything convertible to a string. TR1::function takes as a template parameter its target function signature:

    
    void registerCallback(std::tr1::function<std::string (int)> func);
    
                                                     // the param "func" will
    
                                                     // take any callable entity
    
                                                     // with a sig consistent
    
                                                     // with "std::string (int)"
    
    

    This kind of flexibility is astonishingly useful, something I do my best to demonstrate in Item 35.

  • tr1::bind, which does everything the STL binders bind1st and bind2nd do, plus much more. Unlike the pre-TR1 binders, tr1::bind works with both const and non-const member functions. Unlike the pre-TR1 binders, TR1::bind works with by-reference parameters. Unlike the pre-TR1 binders, TR1::bind handles function pointers without help, so there's no need to mess with ptr_fun, mem_fun, or mem_fun_ref before calling TR1::bind. Simply put, TR1::bind is a second-generation binding facility that is significantly better than its predecessor. I show an example of its use in Item 35.

I divide the remaining TR1 components into two sets. The first group offers fairly discrete standalone functionality:

  • Hash tables used to implement sets, multisets, maps, and multimaps. Each new container has an interface modeled on that of its pre-TR1 counterpart. The most surprising thing about TR1's hash tables are their names: TR1::unordered_set, tr1::unordered_multiset, tr1::unordered_map, and tr1::unordered_multimap. These names emphasize that, unlike the contents of a set, multiset, map, or multimap, the elements in a TR1 hash-based container are not in any predictable order.

  • Regular expressions, including the ability to do regular expression-based search and replace operations on strings, to iterate through strings from match to match, etc.

  • Tuples, a nifty generalization of the pair template that's already in the standard library. Whereas pair objects can hold only two objects, however, tr1::tuple objects can hold an arbitrary number. Expat Python and Eiffel programmers, rejoice! A little piece of your former homeland is now part of C++.

  • tr1::array, essentially an "STLified" array, i.e., an array supporting member functions like begin and end. The size of a tr1::array is fixed during compilation; the object uses no dynamic memory.

  • tr1::mem_fn, a syntactically uniform way of adapting member function pointers. Just as tr1::bind subsumes and extends the capabilities of C++98's bind1st and bind2nd, tr1::mem_fn subsumes and extends the capabilities of C++98's mem_fun and mem_fun_ref.

  • tr1::reference_wrapper, a facility to make references act a bit more like objects. Among other things, this makes it possible to create containers that act as if they hold references. (In reality, containers can hold only objects or pointers.)

  • Random number generation facilities that are vastly superior to the rand function that C++ inherited from C's standard library.

  • Mathematical special functions, including Laguerre polynomials, Bessel functions, complete elliptic integrals, and many more.

  • C99 compatibility extensions, a collection of functions and templates designed to bring many new C99 library features to C++.

The second set of TR1 components consists of support technology for more sophisticated template programming techniques, including template metaprogramming (seeItem 48):

  • Type traits, a set of traits classes (see Item 47) to provide compile-time information about types. Given a type T, TR1's type traits can reveal whether T is a built-in type, offers a virtual destructor, is an empty class (see Item 39), is implicitly convertible to some other type U, and much more. TR1's type traits can also reveal the proper alignment for a type, a crucial piece of information for programmers writing custom memory allocation functions (see Item 50).

  • tr1::result_of, a template to deduce the return types of function calls. When writing templates, it's often important to be able to refer to the type of object returned from a call to a function (template), but the return type can depend on the function's parameter types in complex ways. TR1::result_of makes referring to function return types easy. TR1::result_of is used in several places in TR1 itself.

Although the capabilities of some pieces of TR1 (notably TR1::bind and TR1::mem_fn) subsume those of some pre-TR1 components, TR1 is a pure addition to the standard library. No TR1 component replaces an existing component, so legacy code written with pre-TR1 constructs continues to be valid.

TR1 itself is just a document.[] To take advantage of the functionality it specifies, you need access to code that implements it. Eventually, that code will come bundled with compilers, but as I write this in 2005, there is a good chance that if you look for TR1 components in your standard library implementations, at least some will be missing. Fortunately, there is someplace else to look: 10 of the 14 components in TR1 are based on libraries freely available from Boost (see Item 55), so that's an excellent resource for TR1-like functionality. I say "TR1-like," because, though much TR1 functionality is based on Boost libraries, there are places where Boost functionality is currently not an exact match for the TR1 specification. It's possible that by the time you read this, Boost not only will have TR1-conformant implementations for the TR1 components that evolved from Boost libraries, it will also offer implementations of the four TR1 components that were not based on Boost work.

[] As I write this in early 2005, the document has not been finalized, and its URL is subject to change. I therefore suggest you consult the Effective C++ TR1 Information Page, http://aristeia.com/EC3E/TR1_info.html. That URL will remain stable.

If you'd like to use Boost's TR1-like libraries as a stopgap until compilers ship with their own TR1 implementations, you may want to avail yourself of a namespace trick. All Boost components are in the namespace boost, but TR1 components are supposed to be in std::tr1. You can tell your compilers to treat references to std::tr1 the same as references to boost. This is how:


namespace std {

 namespace tr1 = ::boost;           // namespace std::tr1 is an alias

}                                   // for namespace boost


Technically, this puts you in the realm of undefined behavior, because, as Item 25 explains, you're not allowed to add anything to the std namespace. In practice, you're unlikely to run into any trouble. When your compilers provide their own TR1 implementations, all you'll need to do is eliminate the above namespace alias; code referring to std::tr1 will continue to be valid.

Probably the most important part of TR1 not based on Boost libraries is hash tables, but hash tables have been available for many years from several sources under the names hash_set, hash_multiset, hash_map, and hash_multimap. There is a good chance that the libraries shipping with your compilers already contain these templates. If not, fire up your favorite search engine and search for these names (as well as their TR1 appellations), because you're sure to find several sources for them, both commercial and freeware.

Things to Remember

  • The primary standard C++ library functionality consists of the STL, iostreams, and locales. The C99 standard library is also included.

  • TR1 adds support for smart pointers (e.g., tr1::shared_ptr), generalized function pointers (tr1::function), hash-based containers, regular expressions, and 10 other components.

  • TR1 itself is only a specification. To take advantage of TR1, you need an implementation. One source for implementations of TR1 components is Boost.