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 15: Provide access to raw resources in resource-managing classes.

Resource-managing classes are wonderful. They're your bulwark against resource leaks, the absence of such leaks being a fundamental characteristic of well-designed systems. In a perfect world, you'd rely on such classes for all your interactions with resources, never sullying your hands with direct access to raw resources. But the world is not perfect. Many APIs refer to resources directly, so unless you plan to foreswear use of such APIs (something that's rarely practical), you'll have to bypass resource-managing objects and deal with raw resources from time to time.

For example, Item 13 introduces the idea of using smart pointers like auto_ptr or TR1::shared_ptr to hold the result of a call to a factory function like createInvestment:


std::tr1::shared_ptr<Investment> pInv(createInvestment());  // from Item 13


Suppose that a function you'd like to use when working with Investment objects is this:


int daysHeld(const Investment *pi);        // return number of days

                                           // investment has been held


You'd like to call it like this,


int days = daysHeld(pInv);                // error!


but the code won't compile: daysHeld wants a raw Investment* pointer, but you're passing an object of type TR1::shared_ptr<Investment>.

You need a way to convert an object of the RAII class (in this case, tr1::shared_ptr) into the raw resource it contains (e.g., the underlying Investment*). There are two general ways to do it: explicit conversion and implicit conversion.

tr1::shared_ptr and auto_ptr both offer a get member function to perform an explicit conversion, i.e., to return (a copy of) the raw pointer inside the smart pointer object:


int days = daysHeld(pInv.get());            // fine, passes the raw pointer

                                            // in pInv to daysHeld


Like virtually all smart pointer classes, TR1::shared_ptr and auto_ptr also overload the pointer dereferencing operators (operator-> and operator*), and this allows implicit conversion to the underlying raw pointers:


class Investment {                         // root class for a hierarchy

public:                                    // of investment types

  bool isTaxFree() const;

  ...

};

Investment* createInvestment();                    // factory function



std::tr1::shared_ptr<Investment>                   // have tr1::shared_ptr

  pi1(createInvestment());                         // manage a resource



bool taxable1 = !(pi1->isTaxFree());               // access resource

                                                   // via operator->

...

std::auto_ptr<Investment> pi2(createInvestment()); // have auto_ptr

                                                   // manage a

                                                   // resource



bool taxable2 = !((*pi2).isTaxFree());             // access resource

                                                   // via operator*

...


Because it is sometimes necessary to get at the raw resource inside an RAII object, some RAII class designers grease the skids by offering an implicit conversion function. For example, consider this RAII class for fonts that are native to a C API:


FontHandle getFont();               // from C API—params omitted

                                    // for simplicity



void releaseFont(FontHandle fh);    // from the same C API

class Font {                           // RAII class

public:

  explicit Font(FontHandle fh)         // acquire resource;

  : f(fh)                              // use pass-by-value, because the

  {}                                   // C API does



  ~Font() { releaseFont(f); }          // release resource



private:

  FontHandle f;                        // the raw font resource

};


Assuming there's a large font-related C API that deals entirely with FontHandles, there will be a frequent need to convert from Font objects to FontHandles. The Font class could offer an explicit conversion function such as get:


class Font {

public:

  ...

  FontHandle get() const { return f; }  // explicit conversion function

  ...

};


Unfortunately, this would require that clients call get every time they want to communicate with the API:


void changeFontSize(FontHandle f, int newSize);     // from the C API



Font f(getFont());

int newFontSize;



...



changeFontSize(f.get(), newFontSize);               // explicitly convert

                                                    // Font to FontHandle


Some programmers might find the need to explicitly request such conversions off-putting enough to avoid using the class. That, in turn, would increase the chances of leaking fonts, the very thing the Font class is designed to prevent.

The alternative is to have Font offer an implicit conversion function to its FontHandle:


class Font {

public:

  ...

  operator FontHandle() const { return f; }        // implicit conversion function

  

  ...

};


That makes calling into the C API easy and natural:


Font f(getFont());

int newFontSize;



...



changeFontSize(f, newFontSize);     // implicitly convert Font

                                    // to FontHandle


The downside is that implicit conversions increase the chance of errors. For example, a client might accidently create a FontHandle when a Font was intended:


Font f1(getFont());



...



FontHandle f2 = f1;                 // oops! meant to copy a Font

                                    // object, but instead implicitly

                                    // converted f1 into its underlying

                                    // FontHandle, then copied that


Now the program has a FontHandle being managed by the Font object f1, but the FontHandle is also available for direct use as f2. That's almost never good. For example, when f1 is destroyed, the font will be released, and f2 will dangle.

The decision about whether to offer explicit conversion from an RAII class to its underlying resource (e.g., via a get member function) or whether to allow implicit conversion is one that depends on the specific task the RAII class is designed to perform and the circumstances in which it is intended to be used. The best design is likely to be the one that adheres to Item 18's advice to make interfaces easy to use correctly and hard to use incorrectly. Often, an explicit conversion function like get is the preferable path, because it minimizes the chances of unintended type conversions. Sometime, however, the naturalness of use arising from implicit type conversions will tip the scales in that direction.

It may have occurred to you that functions returning the raw resource inside an RAII class are contrary to encapsulation. That's true, but it's not the design disaster it may at first appear. RAII classes don't exist to encapsulate something; they exist to ensure that a particular action—resource release—takes place. If desired, encapsulation of the resource can be layered on top of this primary functionality, but it's not necessary. Furthermore, some RAII classes combine true encapsulation of implementation with very loose encapsulation of the underlying resource. For example, tr1::shared_ptr encapsulates all its reference-counting machinery, but it still offers easy access to the raw pointer it contains. Like most well-designed classes, it hides what clients don't need to see, but it makes available those things that clients honestly need to access.

Things to Remember

  • APIs often require access to raw resources, so each RAII class should offer a way to get at the resource it manages.

  • Access may be via explicit conversion or implicit conversion. In general, explicit conversion is safer, but implicit conversion is more convenient for clients.