Effective C++ Part1 share === # Chapter 1. Accustoming Yourself to C++ ## Item 1: View C++ as a federation of languages * Today’s C++ is a multiparadigm programming language: procedural, objectoriented, functional, generic, and metaprogramming. * Recongnize C++ as 4 primary sub-languages * **C**: Basic syntax of C++. No templates, no exceptions, no overloading. * **Object-Oriented C++**: classes, encapsulation, inheritance, polymorphism, virtual functions. * **Template C++**: The generic programming part of C++. Also provides capability of meta-programming. * **The STL**: A template library, which has particular ways of doing things (iterator, function object, ...). * Rules for effective C++ programming vary, depending on the part of C++ you are using. * For example, pass-by-value is generally more efficient than pass-by-reference for built-in types, but when you move from C part of C++ to Object-Oriented C++, pass-by-reference-to-const is usually better when user defined constructors and destructors are involved. ### Things to Remember❗❗❗ ✦ Rules for effective C++ programming vary, depending on the part of C++ you are using. ## Item 2: Prefer consts, enums, and inlines to #defines * This item could be realized as "Prefer the compiler to the preprocessor". ### Disadvantages of #defines * Hard to debug if compilation failed. * Example: `#define ASPECT_RATIO 1.653` The name ASPECT_RATIO may not be in symbol table. It's hard to track the number `1.653` when you get error message from compiler. * Solution: Replace macro with a constant. `const double AspectRatio = 1.653;` *Modern C++: `constexpr double AspectRatio = 1.653;` Ref: https://tjsw.medium.com/%E6%BD%AE-c-constexpr-ac1bb2bdc5e2 C++ 11 **constexpr** example: ```C++ int sq (int N) { return N * N; } const int N = 123; const int SQ_N = sq(N); // replace with constexpr constexpr int sq (int n) { return n * n; } constexpr int N = 123; constexpr int N_SQ = sq(N); ``` The calculation is during compile time. * Special case: const pointer to const char `const char * const authorName = "Scott Meyers";` or `const std::string authorName("Scott Meyers");` * Special case: class-specific constants ```C++ // For integral types // in header file class GamePlayer { private: static const int NumTurns = 5; // constant declaration int scores[NumTurns]; }; // If you take the address of a class constant // in implementation file const int GamePlayer::NumTurns; // definition of NumTurns ``` Usually, C++ requires that you provide a definition for anything you use, but class-specific constants that are static and of integral type are an exception. As long as you don’t take their address, you can declare them and use them without providing a definition. If you do take the address of a class constant, or if your compiler incorrectly insists on a definition even if you don’t take the address, you provide a separate definition like this: `const int GamePlayer::NumTurns;` in an implemntation file. ```C++ // For non-integral types // in header file class CostEstimate { private: static const double FudgeFactor; // declaration of static class constant }; // in implementation file const double CostEstimate::FudgeFactor = 1.35; ``` ```C++ // constexpr class CostEstimate { private: constexpr static double FudgeFactor = 1.35; }; ``` * The enum hack This technique hacks more like macro does. It's legal to take the address of a const, but it's not legal to take the address of an enum. ``` class GamePlayer { private: enum { NumTurns = 5; } int scores[NumTurns]; }; ``` * Don't respect scope. * Once a macro is defined, it's in force for the rest of the compilation. Which means that not only can’t #defines be used for class-specific constants, they also can’t be used to provide any kind of encapsulation. * Invisible side effect when using function-like macro ```C++ #define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b)) int a = 5, b = 0; CALL_WITH_MAX(++a, b); // a is incremented twice CALL_WITH_MAX(++a, b+10); // a is incremented once ``` * Solution: inline template function ```C++ template<typename T> inline void callWithMax(const T& a, const T& b) { f(a > b ? a : b); } ``` Given the availability of consts, enums, and inlines, your need for the preprocessor (especially #define) is reduced, but it’s not eliminated. `#include` remains essential, and `#ifdef/#ifndef` continue to play important roles in controlling compilation. ### Things to Remember❗❗❗ ✦ For simple constants, prefer const objects or enums to #defines. ✦ For function-like macros, prefer inline functions to #defines. ## Item 3: Use const whenever possible * The wonderful thing about `const` is that it allows you to specify a semantic constraint — a particular object should not be modified and compilers will enforce that constraint. * It allows you to communicate to both compilers and other programmers that a value should remain invariant. ```cpp= char greeting[] = "Hello"; char *p = greeting; // non-const pointer, // non-const data const char *p = greeting; // non-const pointer, // const data char * const p = greeting; // const pointer, // non-const data const char * const p = greeting; // const pointer, // const data ``` If the word `const` appears to the left of the asterisk, what’s pointed to is constant; If the word `const` appears to the right of the asterisk, the pointer itself is constant; If `const` appears on both sides, both are constant; * Because both forms exist in real code, you should accustom yourself to both of them. ```cpp= void f1(const Widget *pw); // f1 takes a pointer to a // constant Widget object void f2(Widget const *pw); // so does f2 ``` STL iterators are modeled on pointers, so an iterator acts much like a T* pointer. Declaring an iterator const is like declaring a pointer const (i.e., declaring a T* const pointer): the iterator isn’t allowed to point to something different, but the thing it points to may be modified. If you want an iterator that points to something that can’t be modified (i.e., the STL analogue of a const T* pointer), you want a const_iterator: ```cpp= std::vector<int> vec; ... const std::vector<int>::iterator iter = // iter acts like a T* const vec.begin(); *iter = 10; // OK, changes what iter points to ++iter; // error! iter is const std::vector<int>::const_iterator cIter = // cIter acts like a const T* vec.begin(); *cIter = 10; // error! *cIter is const ++cIter; // fine, changes cIter ``` Having a function return a constant value is generally inappropriate, but sometimes doing so can reduce the incidence of client errors without giving up safety or efficiency. ```cpp= class Rational { ... }; const Rational operator*(const Rational& lhs, const Rational& rhs); ``` ``` Rational a, b, c; ... (a * b) = c; // invoke operator= on the // result of a*b! if (a * b = c) ... // oops, meant to do a comparison! ``` Declaring operator *’s return value const prevents it. * const Member Functions The purpose of const on member functions is to identify which member functions may be invoked on const objects. Such member functions are important for two reasons. * First, they make the interface of a class easier to understand. It’s important to know which functions may modify an object and which may not. * Second, they make it possible to work with const objects. That’s a critical aspect of writing efficient code, because, as Item 20 explains, one of the fundamental ways to improve a C++ program’s performance is to pass objects by reference-to-const. That technique is viable only if there are const member functions with which to manipulate the resulting const-qualified objects. ```cpp= class TextBlock { public: ... const char& operator[](std::size_t position) const // operator[] for { return text[position]; } // const objects char& operator[](std::size_t position) // operator[] for { return text[position]; } // non-const objects private: std::string text; }; ``` TextBlock’s operator[]s can be used like this: ```cpp= TextBlock tb("Hello"); std::cout << tb[0]; // calls non-const // TextBlock::operator[] const TextBlock ctb("World"); std::cout << ctb[0]; // calls const TextBlock::operator[] ``` ```cpp= std::cout << tb[0]; // fine — reading a // non-const TextBlock tb[0] = ’x’; // fine — writing a // non-const TextBlock std::cout << ctb[0]; // fine — reading a // const TextBlock ctb[0] = ’x’; // error! — writing a // const TextBlock ``` The error arises out of an attempt to make an assignment to a const char& . What does it mean for a member function to be const? There are two prevailing notions: bitwise constness (also known as physical constness) and logical constness. The bitwise const camp believes that a member function is const if and only if it doesn’t modify any of the object’s data members (excluding those that are static) In fact, bitwise constness is C++’s definition of constness, and a const member function isn’t allowed to modify any of the non-static data members of the object on which it is invoked. ```cpp= class CTextBlock { public: ... char& operator[](std::size_t position) const // inappropriate (but bitwise { return pText[position]; } // const) declaration of // operator[] private: char *pText; }; ``` Note that operator[]’s implementation doesn’t modify pText in any way. As a result, compilers will happily generate code for operator[]; ```cpp= const CTextBlock cctb("Hello"); // declare constant object char *pc = &cctb[0]; // call the const operator[] to get a // pointer to cctb’s data *pc = ’J’; // cctb now has the value “Jello” ``` This leads to the notion of logical constness. ```cpp= class CTextBlock { public: ... std::size_t length() const; private: char *pText; std::size_t textLength; // last calculated length of textblock bool lengthIsValid; // whether length is currently valid }; std::size_t CTextBlock::length() const { if (!lengthIsValid) { textLength = std::strlen(pText); // error! can’t assign to textLength lengthIsValid = true; // and lengthIsValid in a const } // member function return textLength; } ``` This implementation of length is certainly not bitwise const — both textLength and lengthIsValid may be modified — yet it seems as though it should be valid for const CTextBlock objects. Compilers disagree. They insist on bitwise constness. What to do? The solution is simple: take advantage of C++’s const-related wiggle room known as mutable. `mutable` frees non-static data members from the constraints of bitwise constness: ```cpp= class CTextBlock { public: ... std::size_t length() const; private: char *pText; mutable std::size_t textLength; // these data members may mutable bool lengthIsValid; // always be modified, even in }; // const member functions std::size_t CTextBlock::length() const { if (!lengthIsValid) { textLength = std::strlen(pText); // now fine lengthIsValid = true; // also fine } return textLength; } ``` * Avoiding Duplication in const and Non-const Member Functions ```cpp= class TextBlock { public: ... const char& operator[](std::size_t position) const { ... // do bounds checking ... // log access data ... // verify data integrity return text[position]; } char& operator[](std::size_t position) { ... // do bounds checking ... // log access data ... // verify data integrity return text[position]; } private: std::string text; }; ``` ```cpp= class TextBlock { public: ... const char& operator[](std::size_t position) const // same as before { ... ... ... return text[position]; } char& operator[](std::size_t position) // now just calls const op[] { return const_cast<char&>( // cast away const on // op[]’s return type; static_cast<const TextBlock&>(*this) // add const to *this’s type; [position] // call const version of op[] ); } ... }; ``` ### Things to Remember❗❗❗ ✦ Declaring something const helps compilers detect usage errors. const can be applied to objects at any scope, to function parameters and return types, and to member functions as a whole. ✦ Compilers enforce bitwise constness, but you should program using logical constness. ✦ When const and non-const member functions have essentially identical implementations, code duplication can be avoided by having the non-const version call the const version. ## Item 4: Make sure that objects are initialized before they're used Reading uninitialized values yields undefined behavior. If you’re in the C part of C++ and initialization would probably incur a runtime cost, it’s not guaranteed to take place. If you cross into the non-C parts of C++, things sometimes change. This explains why an array (from the C part of C++) isn’t necessarily guaranteed to have its contents initialized, but a vector (from the STL part of C++) is. So ==Always initialize your objects before you use them!== ```cpp= int x = 0; // manual initialization of an int const char * text = "A C-style string"; // manual initialization of a // pointer (see also Item3) double d; // “initialization” by reading from std::cin >> d; // an input stream ``` * Make sure that all constructors initialize everything in the object. Example for initialization: ```cpp= //assignment ABEntry::ABEntry(const std::string& name,const std::string& address, const std::list<PhoneNumber>& phones){ theName = name; theAddress = address; thePhones = phones; numTimesConsulted = 0; } //initilization ABEntry::ABEntry(const std::string& name,const std::string& address, const std::list<PhoneNumber>& phones) :theName(name), theAddress(address), thePhones(phones), numTimesConsult(0) { } ``` A better way to write the ABEntry constructor is to ==use the **member initialization list** instead of assignments== Arguments in the initialization list are used as constructor arguments for the various data members **True member initialization (via an initialization list)** is preferable to pseudo initialization via assignment. --- The order of initialization of non-local static objects defined in different translation units. #### Static object * Exists from the time it’s constructed until the end of the program * Static objects inside functions are known as *local static objects* (because they’re local to a function), and the other kinds of static objects are known as *non-local static objects*. * Static objects are destroyed when the program exits, i.e., their destructors are called when main finishes executing. ### Problem If initialization of a non-local static object in one translation unit uses a non-local static object in a different translation unit, the object it uses could be uninitialized, because **the relative order of initialization of nonlocal static objects defined in different translation units is undefined.** ```cpp= class FileSystem { // from your library’s header file public: ... std::size_t numDisks() const; // one of many member functions ... }; extern FileSystem tfs; // declare object for clients to use // (“tfs” = “the file system” ); definition // is in some .cpp file in your library class Directory { // created by library client public: Directory( params ); ... }; Directory::Directory( params ) { ... std::size_t disks = tfs.numDisks(); // use the tfs object ... } // tfs and tempDir were created by // different people at different times in different source files // they’re non-local static objects defined in different translation units ``` ### Solution Move each non-local static object into its own function, where it’s declared static. Non-local static objects are replaced with local static objects. C++’s guarantee that local static objects are initialized when the object’s definition is first encountered during a call to that function. ### Singleton pattern Ref: https://skyyen999.gitbooks.io/-study-design-pattern-in-java/content/singleton.html ```cpp= public class Singleton { private static Singleton* instance; private Singleton(){ // 這裡面跑很了多code,建立物件需要花費很多資源 } public static Singleton getInstance(){ // 第一次被呼叫的時候再建立物件 if(instance == null){ instance = new Singleton(); } return instance; } } ``` ```cpp= class FileSystem { ... }; // as before FileSystem& tfs() // this replaces the tfs object; it could be { // static in the FileSystem class static FileSystem fs; // define and initialize a local static object return fs; // return a reference to it } class Directory { ... }; // as before Directory::Directory( params ) // as before, except references to tfs are { // now to tfs() ... std::size_t disks = tfs().numDisks(); ... } Directory& tempDir() // this replaces the tempDir object; it { // could be static in the Directory class static Directory td( params ); // define/initialize local static object return td; // return reference to it } ``` ### Avoid using objects before they’re initialized 1. Manually initialize non-member objects of built-in types 2. Use member initialization lists to initialize all parts of an object 3. Design around the initialization order uncertainty that afflicts non-local static objects defined in separate translation units. ### Things to Remember❗❗❗ ✦ Manually initialize objects of built-in type, because C++ only sometimes initializes them itself. ✦ In a constructor, prefer use of the **member initialization list** to assignment inside the body of the constructor. List data members in the initialization list in the same order they’re declared in the class. ✦ Avoid initialization order problems across translation units by **replacing non-local static objects with local static objects**. # Chapter 2. Constructors, Destructors, and Assignment Operators ## Item 5: Know what functions C++ silently writes and calls Compilers may implicitly generate a class’s default constructor, copy constructor, copy assignment operator, and destructor. ```cpp= class Empty{}; //Sames as below class Empty { public: Empty() { ... } // default constructor Empty(const Empty& rhs) { ... } // copy constructor ~Empty() { ... } // destructor — see below // for whether it’s virtual Empty& operator=(const Empty& rhs) { ... } // copy assignment operator }; ``` --- ```cpp= template<typename T> class NamedObject { public: NamedObject(const char *name, const T& value); NamedObject(const std::string& name, const T& value); ... private: std::string nameValue; T objectValue; }; ``` Because a constructor is declared in NamedObject, compilers won’t generate a default constructor. ```cpp= NamedObject<int> no1("Smallest Prime Number", 2); NamedObject<int> no2(no1); // calls copy constructor ``` ```cpp= template<typename T> class NamedObject { public: // this ctor no longer takes a const name, because nameValue // is now a reference-to-non-const string. The char* constructor // is gone, because we must have a string to refer to. NamedObject(std::string& name, const T& value); ... // as above, assume no // operator= is declared private: std::string& nameValue; // this is now a reference const T objectValue; // this is now const }; ``` ```cpp= std::string newDog("Persephone"); std::string oldDog("Satch"); NamedObject<int> p(newDog, 2); // when I originally wrote this, our // dog Persephone was about to // have her second birthday NamedObject<int> s(oldDog, 36); // the family dog Satch (from my // childhood) would be 36 if she // were still alive p = s; // what should happen to // the data members in p? ``` C++ doesn’t provide a way to make a reference refer to a different object If you want to support copy assignment in a **class containing a reference member**, you must define the copy assignment operator yourself. Compilers behave similarly for **classes containing const members** ## Item 6: Explicitly disallow the use of compiler-generated functions you do not want As we know, there are several compiler-generated functions, such as: default constructor, copy constructor, copy assignment operator, and destructor. Below is an example that we don't want the generated copy constructior and copy assignment. Every property is unique — no two are exactly alike. ```cpp= HomeForSale h1; HomeForSale h2; HomeForSale h3(h1); // attempt to copy h1 — should not compile! h1 = h2; // attempt to copy h2 — should not compile! ``` To prevent these functions from being generated, you must declare them yourself, but there is nothing that requires that you declare them public. ```cpp= class HomeForSale { public: ... private: ... HomeForSale(const HomeForSale&); // declarations only HomeForSale& operator=(const HomeForSale&); }; ``` The scheme isn’t foolproof, because member and friend functions can still call your private functions. Unless, that is, you are clever enough not to define them. Then if somebody inadvertently calls one, they’ll get an error at link-time. However, it’s possible to move the link-time error up to compile time. To declare the copy constructor and copy assignment operator private not in HomeForSale itself, but in a base class specifically designed to prevent copying. The base class is simplicity itself: ```cpp= class Uncopyable { protected: // allow construction Uncopyable() {} // and destruction of ~Uncopyable() {} // derived objects... private: Uncopyable(const Uncopyable&); // ...but prevent copying Uncopyable& operator=(const Uncopyable&); }; ``` To keep HomeForSale objects from being copied, all we have to do now is inherit from Uncopyable: ```cpp= class HomeForSale: private Uncopyable { // class no longer ... // declares copy ctor or }; // copy assign. operator ``` ## Item 7: Declare destructors virtual in polymorphic base classes Below is an example to demostrate the issue for non-virtual destructor in polymorphic base class. ```cpp= class TimeKeeper { public: TimeKeeper(); ~TimeKeeper(); ... }; class AtomicClock: public TimeKeeper { ... }; class WaterClock: public TimeKeeper { ... }; class WristWatch: public TimeKeeper { ... }; ``` Clients will want access to the time without worrying about the details of how it’s calculated. ```cpp= TimeKeeper* getTimeKeeper(); // returns a pointer to a dynamically // allocated object of a class derived from TimeKeeper ``` ```cpp= TimeKeeper *ptk = getTimeKeeper(); // get dynamically allocated object from TimeKeeper hierarchy ... // use it delete ptk; // release it to avoid resource leak ``` The problem is that getTimeKeeper returns a pointer to a derived class object, that object is being deleted via a base class pointer, and the base class has a non-virtual destructor. The base class part typically would be destroyed, while derived class part is not. This would cause a curious “partially destroyed” object. To eliminate the problem: ```cpp= class TimeKeeper { public: TimeKeeper(); virtual ~TimeKeeper(); ... }; TimeKeeper *ptk = getTimeKeeper(); delete ptk; // now behaves correctly ``` Not a good idea to have a virtual destructor for non-base class. ```cpp= class Point { // a 2D point public: Point(int xCoord, int yCoord); virtual ~Point(); //waste memory to carry vtbl private: int x, y; }; ``` Bad idea to inherit std::container. ```cpp= class SpecialString: public std::string { // bad idea! std::string has a ... // non-virtual destructor }; ``` Pure virtual destructor: ```cpp= class AWOV { // AWOV = “Abstract w/o Virtuals” public: virtual ~AWOV() = 0; // declare pure virtual destructor }; ``` Need to provide the defination: ```cpp= AWOV::~AWOV() {} // definition of pure virtual dtor ``` ## Item 8: Prevent exceptions from leaving destructors ### Problem When destructors emit exceptions: ```cpp= class Widget { public: ... ~Widget() { ... } // assume this might emit an exception }; void doSomething() { std::vector<Widget> v; ... } // v is automatically destroyed here ``` If an exception is thrown during destruction of the first one, the other nine Widgets still have to be destroyed (otherwise memory leak occurs). If a second Widget destructor throws an exception, there would be 2 active exceptions simultaneously, program execution either terminates or yields undefined behavior. C++ does NOT like destructors that emit exceptions! ### Problem What should you do if your destructor needs to perform an operation that may fail by throwing an exception? See below example: ```cpp= class DBConnection { public: ... static DBConnection create(); // function to return DBConnection objects void close(); //close connection; throw an exception if closing fails } ``` Resource managing class to ensure that the clients don't forget to call close(). ```cpp= class DBConn { // class to manage DBConnection objects public: ... ~DBConn() { // make sure database connections are always closed db.close(); } private: DBConnection db; } ``` Client could program like: ```cpp= { // open a block DBConn dbc(DBConnection::create()); // create DBConnection object and turn it over to a DBConn object to manage ... } // the DBCon object is destroyed, thus automatically calling close on the DBConnection object ``` If the close() call yields an exception, DBConn's destructor will propagate that exception, allow it to leave the destructor. ### Solution 1 If the program cannot continue after an error is encountered during destruction, terminate the probram (typically by calling abort). ```cpp= DBConn::~DBConn() { try { db.close(); } catch (...) { make log entry that the call to close failed; std::abort(); } } ``` ### Solution 2 Swallow the exception. In general, this is not a good idea. But sometimes, swallowing exceptions is preferable to running the risk of premature program termination or undefined behavior. ```cpp= DBConn::~DBConn() { try { db.close(); } catch (...) { make log entry that the call to close failed; } } ``` Neither of the two solutions above can react to the expection in the first place. ### Better Solution DBConn could offer a close function itself to allow the client to handle exceptions arising from that operation. ```cpp= class DBConn { public: ... void close() { // new function for client use db.close(); closed = true; } ~DBConn() { if (!closed) { try { // close the connection db.close(); // if the client didn't } catch (...) { // if closing fails, make log entry that call to close failed; // note that and ... // terminate or swallow } } } private: DBConnection db; bool closed; }; ``` ### Conclusion 1. Destructors should never emit exceptions. If functions called in a destructor may throw, the destructor should catch any exceptions, then swallow them or terminate the program. 2. If class clients need to be able to react to exceptions thrown during an operation, the class should provide a regular (i.e., non-destructor) function that performs the operation. ## Item 9: Never call virtual functions during construction or destruction You shouldn't call virtual functions during construction or destruction. See below code: ```cpp= class Transaction { // base class for all public: Transaction(); virtual void logTransaction() const = 0; // make type-dependent log entry ... }; Transaction::Transaction() { // implementation of base class ctor ... logTransaction(); // as final action, log this transaction } class BuyTransaction: public Transaction { // derived class public: virtual void logTransaction() const; // how to log transactions of this type ... }; class SellTransaction: public Transaction { // derived class public: virtual void logTransaction() const; // how to log transactions of this type ... }; ``` What would happen when this code is executed? ```cpp= BuyTransaction b; ``` Base class parts of derived class objects are constructed before derived class parts are. During the construction of the base class parts, the object is treated as base class. This is also true for constructor. ### Solution for above example Let derived class constructors pass the necessary log information to the Transaction constructor. ```cpp= class Transaction { public: explicit Transaction(const std::string& logInfo); void logTransaction(const std::string& logInfo) const; // now a non-virtual func ... }; Transaction::Transaction(const std::string& logInfo) { ... logTransaction(logInfo); // now a non-virtual call } class BuyTransaction: public Transaction { public: BuyTransaction( parameters ) : Transaction(createLogString( parameters )) // pass log info to base class constructor { ... } ... private: static std::string createLogString( parameters ); }; ``` Noted that by making the createLogString function static, there's no danger of accidentally referring to the nascent BuyTransaction object's as-yet-uninitialized data members. ### Conclusion Don't call virtual functions during construction or destruction, because such calls will never go to a more derived class than that of the currently executing constructor or destructor. ## Item 10: Have assignment operators return a reference to *this One of the interesting things about assignments is that you can chain them together: ``` int x, y, z; x = y = z = 15; // chain of assignments ``` Also interesting is that assignment is right-associative, so the above assignment chain is parsed like this: ``` x = (y = (z = 15)); ``` Write it in an equivalent functional form: ``` x.operator=(y.operator=(z.operator=(15))); ``` Now, for user-defined objects, it is recommended to write assginments operator ``` class Node { public: ... Node& operator=(const Node& rhs) { ... return *this; // return the left-hand object } ... }; ``` The very common case is that ``` std::cout<< "Hello" << "World" << "!"; ``` But assignment operator can have many kinds ``` #1 Node operator=(const Node& rhs) { ... return *this; } #2 Const& Node operator=(const Node& rhs) { ... return *this; } ``` If we use the #1, it will calls assignment operator twice, calls copy constructor twice, calls destructor type to delete the temporary values! Very wasteful and nothing gained! For the #2 type, this is about your behavior for this object assignment. Let's see ``` class Vector { public: ... Vector& operator=(const Vector& rhs) { ... return *this; // return the left-hand object } const Vector& operator[](int idx) { ... } // or Vector& operator[](int idx) { ... } }; v[1] = 2; ``` ### Conclusion - Have assignment operators return a reference to *this - However, the convention is followed by all the built-in types as well as by all the types in the standard library (e.g., string, vector, complex, tr1::shared_ptr, etc.). Unless you have a good reason for doing things differently, don’t. ## Item 11: Handle assignment to self in operator= An assignment to self occurs when an object is assigned to itself: ``` class Widget { ... }; Widget w; ... w = w; // assignment to self ``` This looks silly, but it’s legal. We can find some common cases: ``` a[i] = a[j] // i = j *px = *py // px and py point to the same ``` Therefore, when you design assignment, you must consider this situation. Especially under inheritance, they don’t have to be the same type. ``` class Base { ... }; class Derived: public Base { ... }; void doSomething(const Base& rb, Derived* pd);// rb and *pd might actually be ``` For a example ``` class Bitmap { ... }; class Widget { ... private: Bitmap *pb; // ptr to a heap-allocated object }; Widget& Widget::operator=(const Widget& rhs) / { delete pb; // stop using current bitmap pb = new Bitmap(*rhs.pb); // start using a copy of rhs’s bitmap return *this; // see Item 10 } ``` The self-assignment problem here is that inside operator=, *this (the target of the assignment) and rhs could be the same object. When they are, the delete not only destroys the bitmap for the current object, it destroys the bitmap for rhs, too. ### Solution The traditional way to prevent this error is to check for assignment to self via an identity test at the top ``` Widget& Widget::operator=(const Widget& rhs) { if (this == &rhs) return *this; // identity test: if a self-assignment, // do nothing delete pb; pb = new Bitmap(*rhs.pb); return *this; } ``` This works, but I mentioned above that the previous version of operator= wasn’t just self-assignment-unsafe, it was also exception-unsafe, and this version continues to have exception trouble. The “new Bitmap” expression may yield an exception (there is insufficient memory for the allocation). ``` Widget& Widget::operator=(const Widget& rhs) { Bitmap *pOrig = pb; // remember original pb pb = new Bitmap(*rhs.pb); // point pb to a copy of rhs’s bitmap delete pOrig; // delete the original pb return *this; } ``` If you’re concerned about efficiency, you could put the identity test at the top of the function (I don't think so) ``` class Widget { ... void swap(Widget& rhs); // exchange *this’s and rhs’s data; ... }; Widget& Widget::operator=(const Widget& rhs) { Widget temp(rhs); // make a copy of rhs’s data swap(temp); // swap *this’s data with the copy’s return *this; } ``` ### Conclusion - Make sure operator= is well-behaved when an object is assigned to itself. Techniques include comparing addresses of source and target objects, careful statement ordering, and ~~copy-and-swap~~. - Make sure that any function operating on more than one object behaves correctly if two or more of the objects are the same. ## Item 12: Copy all parts of an object Consider a class representing customers, where the copying functions have been manually written so that calls to them are logged: ``` void logCall(const std::string& funcName); // make a log entry class Customer { public: ... Customer(const Customer& rhs); Customer& operator=(const Customer& rhs); ... private: std::string name; }; Customer::Customer(const Customer& rhs) : name(rhs.name) // copy rhs’s data { logCall("Customer copy constructor"); } Customer& Customer::operator=(const Customer& rhs) { logCall("Customer copy assignment operator"); name = rhs.name; // copy rhs’s data return *this; } ``` Everything here looks fine, and in fact everything is fine — until another data member is added to Customer: ``` class Date { ... }; // for dates in time class Customer { public: ... // as before private: std::string name; Date lastTransaction; }; ``` You should write new code for this data member in the copy constructor ``` Customer& Customer::operator=(const Customer& rhs) { logCall("Customer copy assignment operator"); name = rhs.name; lastTransaction = rhs.lastTransaction return *this; } ``` That is the compiler revenge for your writing the copying functions yourself. You reject the copying functions they’d write, so they don’t tell you if your code is incomplete. One of the most insidious ways this issue can arise is through inheritance. Consider: ``` class PriorityCustomer: public Customer { public: ... PriorityCustomer(const PriorityCustomer& rhs); PriorityCustomer& operator=(const PriorityCustomer& rhs); ... private: int priority; }; PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs): priority(rhs.priority) { logCall("PriorityCustomer copy constructor"); } PriorityCustomer&PriorityCustomer::operator=(const PriorityCustomer& rhs) { logCall("PriorityCustomer copy assignment operator"); priority = rhs.priority; return *this; } ``` PriorityCustomer’s copying functions look like they’re copying everything in PriorityCustomer, but every PriorityCustomer also contains a copy of the data members it inherits from Customer, and those data members are not being copied at all. ``` PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs): Customer(rhs), // invoke base class copy ctor priority(rhs.priority) { logCall("PriorityCustomer copy constructor"); } PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs) { logCall("PriorityCustomer copy assignment operator"); Customer::operator=(rhs); // assign base class parts priority = rhs.priority; return *this; } ``` It makes no sense to have the copy assignment operator call the copy constructor, because you’d be trying to construct an object that already exists. ### Conclusion - Copying functions should be sure to copy all of an object’s data members and all of its base class parts. - Don’t try to implement one of the copying functions in terms of the other. Instead, put common functionality in a third function that both call. # Chapter 3. Resource Management ## Item 13: Use objects to manage resources. ```c++ Investment* createInvestment(); // return ptr to dynamically allocated // object in the Investment hierarchy; // the caller must delete it // (parameters omitted for simplicity) void f() { Investment *pInv = createInvestment(); // call factory function ... // use pInv delete pInv; // release object } ``` This looks okay, but there are several ways f could fail to delete the investment object it gets from createInvestment. There might be a premature return statement somewhere inside the “...” part of the function. Or, some statement inside the “...” might throw an exception. ### Solution: Use auto_ptr Many resources are used only within a single block or function, and should be released when control leaves that block or function. The standard library’s **auto_ptr** is tailormade for this kind of situation. ```c++ void f() { std::auto_ptr<Investment> pInv(createInvestment()); // call factory function ... // use pInv as before } // automatically delete pInv via auto_ptr’s dtor ``` * **Resources are acquired and immediately turned over to resource-managing objects.** The idea of using objects to manage resources is often called Resource Acquisition Is Initialization (RAII), because it’s so common to acquire a resource and initialize a resource-managing object in the same statement. * Resource-managing objects use their destructors to ensure that resources are released. **Note: auto_ptr is deprecated, because it doesn't support move semetics. You should use unique_ptr or shared_ptr instead.** ### An alternative to auto_ptr is a reference-counting smart pointer (RCSP). An RCSP is a smart pointer that keeps track of how many objects point to a particular resource and automatically deletes the resource when nobody is pointing to it any longer. ```c++ void f() { ... std::shared_ptr<Investment> // pInv1 points to the pInv1(createInvestment()); // object returned from createInvestment std::shared_ptr<Investment> pInv2(pInv1); // both pInv1 and pInv2 now // point to the object pInv1 = pInv2; // ditto — nothing has changed ... } // pInv1 and pInv2 are destroyed, // and the object they point to is automatically deleted ``` ### Don't do this when using smart pointer Both auto_ptr and tr1::shared_ptr use delete in their destructors, not delete []. That means that using auto_ptr or tr1::shared_ptr with dynamically allocated arrays is a bad idea. ```c++ std::auto_ptr<std::string> aps(new std::string[10]); // bad idea! the wrong // delete form will be used std::tr1::shared_ptr<int> spi(new int[1024]); // same problem ``` ### Things to Remember * To prevent resource leaks, use RAII objects that acquire resources in their constructors and release them in their destructors. * Two commonly useful RAII classes are tr1::shared_ptr and auto_ptr. tr1::shared_ptr is usually the better choice, because its behavior when copied is intuitive. Copying an auto_ptr sets it to null. ## Item 14: Think carefully about copying behavior in resource-managing classes. ```c++ class Lock { public: explicit Lock(Mutex *pm) : mutexPtr(pm) { lock(mutexPtr); } // acquire resource ~Lock() { unlock(mutexPtr); } // release resource private: Mutex *mutexPtr; }; // Case 1 Mutex m; // define the mutex you need to use ... { // create block to define critical section Lock ml(&m); // lock the mutex ... // perform critical section operations } // automatically unlock mutex at end of block // Case 2 Lock ml1(&m); // lock m Lock ml2(ml1); // copy ml1 to ml2 — what should happen here? ``` ### Possible situations * **Prohibit copying** : In many cases, it makes no sense to allow RAII objects to be copied. e.g. Lock ```c++ class Lock: private Uncopyable { // prohibit copying — see public: // Item 6 ... // as before }; ``` * **Reference-count the underlying resource** : Sometimes it’s desirable to hold on to a resource until the last object using it has been destroyed. ```c++ class Lock { public: explicit Lock(Mutex *pm) // init shared_ptr with the Mutex to point to : mutexPtr(pm, unlock) // and the unlock func as the deleter { lock(mutexPtr.get()); // see Item15 for info on “get” } private: std::shared_ptr<Mutex> mutexPtr; // use shared_ptr instead of raw pointer }; ``` * **Copy the underlying resource** : That is, Copying a resource-managing object performs a “deep copy.” e.g. string * **Transfer ownership of the underlying resource** : On rare occasion, you may wish to make sure that only one RAII object refers to a raw resource and that when the RAII object is copied. ### Things to Remember * Copying an RAII object entails copying the resource it manages, so the copying behavior of the resource determines the copying behavior of the RAII object. * Common RAII class copying behaviors are disallowing copying and performing reference counting, but other behaviors are possible. ## Item 15: Provide access to raw resources in resource-managing classes. 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. ```c++ std::shared_ptr<Investment> pInv(createInvestment()); // from Item13 // A function you’d like to use int daysHeld(const Investment *pi); // return number of days investment has been held ``` ### Two ways to convert an object of the RAII class into the raw resource it contains ```c++ // Explicit conversion int days = daysHeld(pInv.get()); // fine, passes the raw pointer in pInv to daysHeld // Implicit conversion : dereferencing operators (operator-> and operator*) class Investment { // root class for a hierarchy public: // of investment types bool isTaxFree() const; ... }; Investment* createInvestment(); // factory function std::tr1::shared_ptr<Investment> pi1(createInvestment()); // have tr1::shared_ptr 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* ... ``` To facilitate the using of raw resources, some RAII class designers offer an implicit conversion function. ```c++ 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 handle copying (see Item14) private: FontHandle f; // the raw font resource }; ``` **Use case 1 : Font class provide explicit conversion** ```c++ class Font { public: ... FontHandle get() const { return f; } // explicit conversion function ... }; // Usage void changeFontSize(FontHandle f, int newSize); // from the C API Font f(getFont()); int newFontSize; ... changeFontSize(f.get(), newFontSize); // explicitly convert Font to FontHandle // user get annoyed by conversion ``` **Use case 2 : implicit conversion** ```c++ class Font { public: ... operator FontHandle() const // implicit conversion function { return f; } ... }; // Usage Font f(getFont()); int newFontSize; ... changeFontSize(f, newFontSize); // implicitly convert ``` **Drawback of use case 2** ```c++ 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 ``` ### 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. ### ## Item 16: Use the same form in corresponding uses of new and delete. What’s wrong with this? ```c++ std::string *stringArray = new std::string[100]; ... delete stringArray; ``` The new is matched with a delete. Still, something is quite wrong. The program’s behavior is undefined. At the very least, 99 of the 100 string objects pointed to by stringArray are unlikely to be properly destroyed, because their destructors will probably never be called. The big question for delete is this: how many objects reside in the memory being deleted? The answer to that determines how many destructors must be called. ![](https://i.imgur.com/cuBPdS6.png) ```c++ std::string *stringPtr1 = new std::string; std::string *stringPtr2 = new std::string[100]; ... delete stringPtr1; // delete an object delete [] stringPtr2; // delete an array of objects ``` What would happen if you used the “[]” form on stringPtr1? The result is undefined, but it’s unlikely to be pretty. What would happen if you didn’t use the “[]” form on stringPtr2? Well, that’s undefined too, but you can see how it would lead to too few destructors being called. The rule is simple: if you use [] in a new expression, you must use [] in the corresponding delete expression. If you don’t use [] in a new expression, don’t use [] in the matching delete expression. ```c++ typedef std::string AddressLines[4]; // a person’s address has 4 lines, // each of which is a string ``` Because AddressLines is an array, this use of new, ```c++ std::string *pal = new AddressLines; // note that “new AddressLines” // returns a string*, just like // “new string[4]” would ``` must be matched with the array form of delete: ``` c++ delete pal; // undefined! delete [] pal; // fine ``` Things to Remember * If you use [] in a new expression, you must use [] in the corresponding delete expression. If you don’t use [] in a new expression, you mustn’t use [] in the corresponding delete expression. ## Item 17: Store newed objects in smart pointers in standalone statements. Suppose we have a function to reveal our processing priority and a second function to do some processing on a dynamically allocated Widget in accord with a priority: ```c++ int priority(); void processWidget(std::tr1::shared_ptr<Widget> pw, int priority); ``` Mindful of the wisdom of using objects to manage resources (see Item 13), processWidget uses a smart pointer (here, a tr1::shared_ptr) for the dynamically allocated Widget it processes. Consider now a call to processWidget: ``` c++ processWidget(new Widget, priority()); ``` Wait, don’t consider that call. It won’t compile. tr1::shared_ptr’s constructor taking a raw pointer is explicit, so there’s no implicit conversion from the raw pointer returned by the expression “new Widget” to the tr1::shared_ptr required by processWidget. The following code, however, will compile: ``` c++ processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority()); ``` Surprisingly, although we’re using object-managing resources everywhere here, this call may leak resources. It’s illuminating to see how. Before compilers can generate a call to processWidget, they have to evaluate the arguments being passed as its parameters. The second argument is just a call to the function priority, but the first argument, (“std::tr1::shared_ptr<Widget>(new Widget)”) consists of two parts: * Execution of the expression “new Widget”. * A call to the tr1::shared_ptr constructor Before processWidget can be called, then, compilers must generate code to do these three things: * Call priority. * Execute “new Widget”. * Call the tr1::shared_ptr constructor. The “new Widget” expression must be executed before the tr1::shared_ptr constructor can be called, because the result of the expression is passed as an argument to the tr1::shared_ptr constructor, but the call to priority can be performed first, second, or third. If compilers choose to perform it second (something that may allow them to generate more efficient code), we end up with this sequence of operations: 1. Execute “new Widget”. 2. Call priority. 3. Call the tr1::shared_ptr constructor. ``` Widget* pw = new Widget; int p = priority(); std::shared_ptr<Widget> s_pw = std::shared_ptr<Widget>(pw); ``` But consider what will happen if the call to priority yields an exception. In that case, the pointer returned from “new Widget” will be lost, because it won’t have been stored in the tr1::shared_ptr we were expecting would guard against resource leaks. The way to avoid problems like this is simple: use a separate statement to create the Widget and store it in a smart pointer, then pass the smart pointer to processWidget: ``` c++ std::tr1::shared_ptr<Widget> pw(new Widget); // store newed object // in a smart pointer in a // standalone statement processWidget(pw, priority()); // this call won’t leak ``` Things to Remember * Store newed objects in smart pointers in standalone statements. Failure to do this can lead to subtle resource leaks when exceptions are thrown. # Chapter 4. Designs and Declarations ## Item 18: Make interfaces easy to use correctly and hard to use incorrectly Developing interfaces that are easy to use correctly and hard to use incorrectly requires that you consider the kinds of mistakes that clients might make. For example, suppose you’re designing the constructor for a class representing dates in time: ``` c++ class Date { public: Date(int month, int day, int year); ... }; ``` At first glance, this interface may seem reasonable (at least in the USA), but there are at least two errors that clients might easily make. First, they might pass parameters in the wrong order: ``` c++ Date d(30, 3, 1995); // Oops! Should be “3, 30” , not “30, 3 ``` Second, they might pass an invalid month or day number: ``` c++ Date d(3, 40, 1995); // Oops! Should be “3, 30” , not “3, 40” ``` Many client errors can be prevented by the introduction of new types. Indeed, the type system is your primary ally in preventing undesirable code from compiling. ``` c++ struct Day { struct Month { struct Year { explicit Day(int d) explicit Month(int m) explicit Year(int y) : val(d) {} : val(m) {} : val(y){} int val; int val; int val; }; }; }; class Date { public: Date(const Month& m, const Day& d, const Year& y); ... }; Date d(30, 3, 1995); // error! wrong types Date d(Day(30), Month(3), Year(1995)); // error! wrong types Date d(Month(3), Day(30), Year(1995)); // okay, types are correct ``` Once the right types are in place, it can sometimes be reasonable to restrict the values of those types. For example, there are only 12 valid month values, so the Month type should reflect that. One way to do this would be to use an enum to represent the month, but enums are not as type-safe as we might like. For example, enums can be used like ints (see Item 2). A safer solution is to predefine the set of all valid Months: ``` c++ class Month { public: static Month Jan() { return Month(1); } // functions returning all valid static Month Feb() { return Month(2); } // Month values; see below for ... // why these are functions, not static Month Dec() { return Month(12); } // objects ... // other member functions private: explicit Month(int m); // prevent creation of new // Month values ... // month-specific data }; Date d(Month::Mar(), Day(30), Year(1995)); ``` A common way to impose restrictions is to add const. For example, Item 3 explains how const-qualifying the return type from operator* can prevent clients from making this error for userdefined types: ``` c++ if (a * b = c) ... // oops, meant to do a comparison! ``` In fact, this is just a manifestation of another general guideline for making types easy to use correctly and hard to use incorrectly: unless there’s a good reason not to, have your types behave consistently with the built-in types. Clients already know how types like int behave, so you should strive to have your types behave the same way whenever reasonable. For example, assignment to a*b isn’t legal if a and b are ints, so unless there’s a good reason to diverge from this behavior, it should be illegal for your types, too. When in doubt, do as the ints do. ``` c++ Investment* createInvestment(); // from Item13; parameters omitted // for simplicity ``` To avoid resource leaks, the pointers returned from createInvestment must eventually be deleted, but that creates an opportunity for at least two types of client errors: failure to delete a pointer, and deletion of the same pointer more than once. Item 13 shows how clients can store createInvestment’s return value in a smart pointer like auto_ptr or tr1::shared_ptr, thus turning over to the smart pointer the responsibility for using delete. But what if clients forget to use the smart pointer? In many cases, a better interface decision would be to preempt the problem by having the factory function return a smart pointer in the first place: ``` c++ std::tr1::shared_ptr<Investment> createInvestment(); ``` ``` c++ std::tr1::shared_ptr<Investment> // attempt to create a null pInv(0, getRidOfInvestment); // shared_ptr with a custom deleter; // this won’t compile ``` ``` c++ std::tr1::shared_ptr<Investment> // create a null shared_ptr with pInv( static_cast<Investment*>(0), // getRidOfInvestment as its getRidOfInvestment); // deleter; see Item27 for info on // static_cast ``` This means that the code for implementing createInvestment to return a tr1::shared_ptr with getRidOfInvestment as its deleter would look something like this: ``` c++ std::tr1::shared_ptr<Investment> createInvestment() { std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(0), getRidOfInvestment); ... // make retVal point to the // correct object return retVal; } ``` An especially nice feature of tr1::shared_ptr is that it automatically uses its per-pointer deleter to eliminate another potential client error, the “cross-DLL problem.” This problem crops up when an object is created using new in one dynamically linked library (DLL) but is deleted in a different DLL. On many platforms, such cross-DLL new/delete pairs lead to runtime errors. tr1::shared_ptr avoids the problem, because its default deleter uses delete from the same DLL where the tr1::shared_ptr is created. This means, for example, that if Stock is a class derived from Investment and createInvestment is implemented like this ``` c++ std::tr1::shared_ptr<Investment> createInvestment() { return std::tr1::shared_ptr<Investment>(new Stock); } ``` the returned tr1::shared_ptr can be passed among DLLs without concern for the cross-DLL problem. The tr1::shared_ptrs pointing to the Stock keep track of which DLL’s delete should be used when the reference count for the Stock becomes zero. Things to Remember * Good interfaces are easy to use correctly and hard to use incorrectly. You should strive for these characteristics in all your interfaces. * Ways to facilitate correct use include consistency in interfaces and behavioral compatibility with built-in types. * Ways to prevent errors include creating new types, restricting operations on types, constraining object values, and eliminating client resource management responsibilities. * tr1::shared_ptr supports custom deleters. This prevents the cross- DLL problem, can be used to automatically unlock mutexes (see Item 14), etc. ## Item 19: Treat class design as type design You’re not just a class designer, you’re a **type designer**. Good types have a natural syntax, intuitive semantics, and one or more efficient implementations. ### How should objects of your new type be created and destroyed? * This will influences the design of your class’s constructors and destructor, as well as its memory llocation and deallocation functions ### How should object initialization differ from object assignment? * True member initialization (via an initialization list) is preferable to pseudo-initialization via assignment. * Understand behavior of and the differences between your constructors and your assignment operators. ### What does it mean for objects of your new type to be passed by value? * Remember, the copy constructor defines how pass-by-value is implemented for a type. ### What are the restrictions on legal values for your new type? * sually, **only some combinations of values for a class’s data members are valid**. Those combinations determine the **invariants your class will have to maintain**. The invariants determine the error checking you’ll have to do inside your member functions, especially your constructors, assignment operators, and “setter” functions. ### Does your new type fit into an inheritance graph? * If you inherit from existing classes, you are constrained by the design of those classes, particularly by whether their **functions are virtual or nonvirtual**. * If you wish to allow other classes to inherit from your class, that affects whether the functions you declare are virtual, **especially your destructor** (Item 7: Declare destructors virtual in polymorphic base classes) ### What kind of type conversions are allowed for your new type? * If you wish to allow objects of type T1 to be implicitly converted into objects of type T2, you will want to write either a **type conversion function in class T1 (e.g., operator T2)** or a **non-explicit constructor in class T2** that can be called with a single argument. * If you wish to allow explicit conversions only, you’ll want to **write functions to perform the conversions**, but you’ll need to avoid making them type conversion operators or non-explicit constructors that can be called with one argument. (For an example of both implicit and explicit conversion functions, see Item 15.) ### What operators and functions make sense for the new type? * The answer to this question determines which functions you’ll declare for your class. Some functions will be member functions, but some will not ### What standard functions should be disallowed? * Those are the ones you’ll need to declare ```private``` ### Who should have access to the members of your new type? * This question helps you determine which members are public,which are protected, and which are private. It also helps you determine which classes and/or functions should be friends, as wellas whether it makes sense to nest one class inside another. ### What is the “undeclared interface” of your new type? * What kind of guarantees does it offer with respect to **performance**, **exception safety** (see Item 29), and **resource usage** (e.g., locks and dynamic memory)? The guarantees you offer in these areas will **impose constraints on your class implementation**. ### How general is your new type? * Perhaps you’re not really defining a new type. Perhaps you’re **defining a whole family of types**. If so, you don’t want to define a new class, you want to **define a new class template**. ### Is a new type really what you need? * If you’re defining a new derived class only so you can add functionality to an existing class, perhaps you’d better achieve your goals by **simply defining one or more non-member functions or templates.** ### Things to Remember * Class design is type design. Before defining a new type, be sure to consider all the issues discussed in this Item. ## Item 20: Prefer pass-by-reference-to-const to pass-by-value By default, C++ passes objects to and from functions by value (a characteristic it inherits from C). ```cpp= class Person { public: Person(); // parameters omitted for simplicity virtual ~Person(); // see Item 7 for why this is virtual ... private: std::string name; std::string address; }; class Student: public Person { public: Student(); // parameters again omitted virtual ~Student(); ... private: std::string schoolName; std::string schoolAddress; }; ``` ```cpp= bool validateStudent(Student s); // function taking a Student // by value Student plato; // Plato studied under Socrates bool platoIsOK = validateStudent(plato); // call the function ``` Clearly, the Student copy constructor is called to initialize the parameter ```s``` from plato. Equally clearly, ```s``` is destroyed when validateStudent returns. So the parameter-passing cost of this function is one call to the Student copy constructor and one call to the Student destructor. Overall cost of passing a Student y value is *six constructors and six destructors! (two string objects within it)* After all, you want all your objects to be reliably initialized and destroyed. Still, it would be nice if there were a way to bypass all those constructions and destructions. ==Solution: pass by reference-to-const==: ```cpp= bool validateStudent(const Student& s); ``` No constructors or destructors are called because no new objects are being created. Student is being passed by reference, it’s necessary to also declare it ```const```, because otherwise callers would have to worry about validateStudent making changes to the Student they passed in. Passing parameters by reference also avoids the [**slicing problem**](https://www.youtube.com/watch?v=f29xDhRNPfU). When a derived class object is passed by value as a base class object,the base class copy constructor is called, and the specialized features that make the object behave like a derived class object are “sliced” off. ```cpp= class Window { public: ... std::string name() const; // return name of window virtual void display() const; // draw window and contents }; class WindowWithScrollBars: public Window { public: ... virtual void display() const; }; ``` The fact that display is virtual tells you that the way in which simple base class Window objects are displayed is apt to differ from the way in which the fancier Window-WithScrollBars objects are displayed. ❌ Will always call ```Window::display``` ```cpp= void printNameAndDisplay(Window w) // incorrect! parameter { // may be sliced! std::cout << w.name(); w.display(); } ``` ```cpp= WindowWithScrollBars wwsb; printNameAndDisplay(wwsb); ``` Will always call ```Window::display```, never ```WindowWithScrollBars::display```. ✔ Pass ```w``` by reference-to-const ```cpp= void printNameAndDisplay(const Window& w) // fine, parameter won’t { // be sliced std::cout << w.name(); w.display(); } ``` Now ```w``` will act like whatever kind of window is actually passed in. If you have an object of a built-in type (e.g., an int), it’s often more efficient to pass it by value than by reference. This same advice applies to iterators and function objects in the STL, because, by convention, they are designed to be passed by value. ### Things to Remember * Prefer **pass-by-reference-to-const** over pass-by-value. It’s typically **more efficient and it avoids the slicing problem**. * The rule doesn’t apply to **built-in types** and **STL iterator** and **function object types**. For them, pass-by-value is usually appropriate. ## Item 21: Don't try to return a reference when you must return an object **Fatal mistake**: Pass references to objects that don’t exist ```cpp= class Rational { public: Rational(int numerator = 0, // see Item 24 for why this int denominator = 1); // ctor isn’t declared explicit ... private: int n, d; // numerator and denominator friend const Rational // see Item 3 for why the operator*(const Rational& lhs, // return type is const const Rational& rhs); }; ``` This version of ``operator*`` is returning its result object by value, and you’d be shirking your professional duties if you failed to worry about the *cost of that object’s construction and destruction*. In the case of ``operator*``, if the function is to return a reference, it **must return a reference to some Rational object that already exists** and that contains the product of the two objects that are to be multiplied together. ```cpp= Rational a(1, 2); // a = 1/2 Rational b(3, 5); // b = 3/5 Rational c = a * b; // c should be 3/10 ``` If ``operator*`` is to return a reference to such a number, it must create that number object itself. ### Three wrong ways to write a function 1. **Create a new object on the stack**: Defining a local variable ```cpp= const Rational& operator*(const Rational& lhs, // warning! bad code! const Rational& rhs) { Rational result(lhs.n * rhs.n, lhs.d * rhs.d); return result; } ``` This function returns a reference to result, but result is a *local object, and local objects are destroyed when the function exits*. Any caller so much as glancing at this function’s return value would instantly enter the realm of **undefined behavior**. 2. **Create a new object on the heap**: Use of new ```cpp= const Rational& operator*(const Rational& lhs, // warning! more bad const Rational& rhs) // code! { Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); return *result; } ``` Still have to pay for a constructor call, because the memory allocated by ``new`` is initialized by calling an appropriate constructor Who will apply ``delete`` to the object conjured up by your use of new? ```cpp= Rational w, x, y, z; w = x * y * z; // same as operator*(operator*(x, y), z) ``` There are two calls to ``operator*`` in the same statement, hence two uses of ``new`` that need to be undone with uses of ``delete``. 3. Returning a reference to a **static** Avoid such constructor invocations. Perhaps you think you know a way to avoid all but one constructor call. Perhaps the following implementation occurs to you, an implementation based on ``operator*`` returning a reference to a ```static``` Rational object, one defined inside the function: ```cpp= const Rational& operator*(const Rational& lhs, // warning! yet more const Rational& rhs) // bad code! { static Rational result; // static object to which a // reference will be returned result = ... ; // multiply lhs by rhs and put the // product inside result return result; } ``` Like all designs employing the use of static objects, this one immediately raises our thread-safety hackles, but that’s its more obvious weakness. ```cpp= bool operator==(const Rational& lhs, // an operator== const Rational& rhs); // for Rationals Rational a, b, c, d; ... if ((a * b) == (c * d)) { do whatever’s appropriate when the products are equal; } else { do whatever’s appropriate when they’re not; } ``` Always evaluate to ``true`` ```cpp= if (operator==(operator*(a, b), operator*(c, d))) ``` Notice that when ```operator==``` is called, there will already be two active calls to ``operator*``, each of which will *return a reference to the static Rational object inside operator*. ### Right way to write a function that must return a new object Have that function return a new object. For Rational’s ``operator*``, that means either the following code or something essentially equivalent ```cpp= inline const Rational operator*(const Rational& lhs, const Rational& rhs) { return Rational(lhs.n * rhs.n, lhs.d * rhs.d); } ``` Sure, you may incur the cost of constructing and destructing ``operator*``’s return value, but in the long run, that’s a small price to pay for correct behavior. ### Things to Remember * Never return a pointer or reference to a local stack object, a reference to a heap-allocated object, or a pointer or reference to a local static object if there is a chance that more than one such object will be needed. ## Item 22: Declare data members private So, public data members. Why not? 1. ***Syntactic consistency*** If everything in the public interface is a function, clients won’t have to remember whether to use ==parentheses== when they want to access a member of the class. 2. ***More precise access control*** If you make a data member public, everybody has read-write access to it, but if you use functions to get or set its value, you can implement ==no access==, ==read-only access==, and ==read-write access==. ```cpp= class AccessLevels { public: ... int getReadOnly() const { return readOnly; } void setReadWrite(int value) { readWrite = value; } int getReadWrite() const { return readWrite; } void setWriteOnly(int value) { writeOnly = value; } private: int noAccess; // no access to this int int readOnly; // read-only access to this int int readWrite; // read-write access to this int int writeOnly; // write-only access to this int }; ``` 3. ***Encapsulation*** If you implement access to a data member through a function, you can later replace the data member with a computation, and nobody using your class will be any the wiser. ```cpp= class SpeedDataCollection { ... public: void addValue(int speed); // add a new data value double averageSoFar() const; // return average speed ... }; ``` There are two approach to return the average speed, and have there pros and cons. Hiding data members behind functional interfaces can offer all kinds of implementation ==flexibility==: I. Easy to notify other objects when data members are read or written. II. Verify class invariants and function pre- and postconditions. III. Perform synchronization in threaded environments. Furthermore, you reserve the right to change your implementation decisions later. Public means unencapsulated, and unencapsulated means unchangeable. Last, the argument against ==protected data== members is similar. The reasoning about syntactic consistency and fine-grained access control is clearly as applicable to protected data as to public. Protected data members are unencapsulated as public ones, because in both cases, if the data members are changed, an unknowably large amount of client code is broken. ### Things to Remember * Declare data members private. It gives clients syntactically uniform access to data, affords fine-grained access control, allows invariants to be enforced, and offers class authors implementation flexibility. * **protected** is no more encapsulated than public. ## Item 23: Prefer non-member non-friend functions to member functions Imagine a class for representing web browsers. ```cpp= class WebBrowser { public: ... void clearCache(); void clearHistory(); void removeCookies(); ... }; ``` Many users will want to perform all these actions together, so WebBrowser might also offer a function to do just that: ```cpp= class WebBrowser { public: ... void clearEverything(); // calls clearCache, clearHistory, // and removeCookies ... }; ``` Of course, this functionality could also be provided by a ==non-member== function that calls the appropriate member functions: ```cpp= void clearBrowser(WebBrowser& wb) { wb.clearCache(); wb.clearHistory(); wb.removeCookies(); } ``` So which is better, the member function clearEverything or the nonmember function clearBrowser? Object-oriented principles dictate that data should be as ==encapsulated== as possible. Counterintuitively, the member function **clearEverything** actually yields less encapsulation than the non-member **clearBrowser**. The non-member approach is thus better than a member function in many ways. More something is encapsulated -> the fewer things can see it -> the greater flexibility and ability to change it **Item 22** explains that data members should be private, because if they’re not, an unlimited number of functions can access them. From an encapsulation point of view, the choice isn’t between member and non-member functions, it’s between ==member functions and non-member non-friend functions==. Encapsulation dictate that a function be a non-member of one class doesn’t mean it can’t be a member of another class. For example, we could make **clearBrowser** a static member function of some utility class. In C++, a more natural approach would be to make clearBrowser a nonmember function in the same namespace as **WebBrowser**: ```cpp= namespace WebBrowserStuff { class WebBrowser { ... }; void clearBrowser(WebBrowser& wb); ... } ``` Unlike classes, namespace can be spread across multiple source files. A class like **WebBrowser** might have a large number of convenience functions: ```cpp= // header “webbrowser.h” — header for class WebBrowser itself // as well as “core” WebBrowser-related functionality namespace WebBrowserStuff { class WebBrowser { ... }; ... // “core” related functionality, e.g. // non-member functions almost // all clients need } // header “webbrowserbookmarks.h” namespace WebBrowserStuff { ... // bookmark-related convenience } // functions // header “webbrowsercookies.h” namespace WebBrowserStuff { ... // cookie-related convenience } // functions ... ``` Note that this is exactly how the standard C++ library is organized. Rather than having a single monolithic <C++StandardLibrary> header containing everything in the **std** namespace Clients can easily extend the set of convenience functions by adding a function to the namespace, while class cannot offer. A class definitions are closed to extension by clients. ### Things to Remember * Prefer non-member non-friend functions to member functions. Doing so increases encapsulation, packaging flexibility, and functional extensibility. ## Item 24: Declare non-member functions when type conversions should apply to all parameters As mentioned before, having classes support implicit type conversion is generally a bad idea. However, there are exceptions. For example, allowing a class which represent rational numbers to perform implicit conversions from integers to rationals. ```cpp= class Rational { public: Rational(int numerator = 0, // NOT explicit; int denominator = 1); // allows implicit int-to-Rational conversions int numerator() const; int denominator() const; private: ... } ``` To support arithmetic operations, you might think of making operator* a member function: ```cpp= class Rational { public: ... const Rational operator*(const Rational& rhs) const; private: ... } ``` ### Problem: not able to support mixed-mode operations. ```cpp= Rational oneEighth(1, 8); Rational oneHalf(1, 2); Rational result = oneEighth * oneHalf; // fine result = oneHalf * 2; // fine result = 2 * oneHalf; // error! ``` Above code is equivalent to: ```cpp= result = oneHalf.operator*(2); result = 2.operator*(oneHalf); // error! no associated class for integer 2 ``` For the call: ```cpp= result = 2 * oneHalf; ``` Compilers will also look for non-member operator* (i.e., ones at namespace or global scope) that can be called like this: ```cpp= result = operator* (2, oneHalf); // error! no non-member operator* taking an // int and a Rational ``` ### Solution: non-member operator* and implicit conversion ```cpp= class Rational { ... // contains no operator* }; const Rational operator*(const Rational& lhr, // non-member function const Rational& rhs) { return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator()); } Rational oneFourth(1, 4); Rational result; result = oneFourth * 2; // fine result = 2 * oneFourth; // also fine } ``` When crossing the line from Object-Oriented C++ into Template C++. There are new issues to consider, new ways to resolve them. (See Item46) ### Conclusion If you need type conversions on all parameters to a function, the function must be a non-member. ## Item 25: Consider support for a non-throwing swap Typical implementation of standard swap algorithm. ```cpp= namespace std { template<typename T> void swap(T& a, T& b) // swap a's and b's value { T temp(a); // copy constructor a = b; // copy assignment operator b = temp; // copy assignment operator } } ``` ### Problem: ```cpp= class WidgetImpl { public: ... private: int a, b, c; // possibly lots of data std::vector<double> v; // expensive to copy ... }; class Widget { // class using the pimpl idiom public: Widget (const Widget& rhs); Widget& operator=(const Widget& rhs) // to copy a Widget, copy its { // WidgetImpl object ... *pImpl = *(rhs.pImpl); ... } private: WidgetImpl *pImpl; // ptr to this Widget's data }; ``` The default swap algorithm copy 3 Wigdtes and 3 WidgetImpl objects. To swap the value of two Widget, all we really need to do is swap their pImpl pointers. How to tell std::swap the right way to perform swap for specific object? ### Solution: total template specialization We are not permitted to alter the contents of std namespace, but we are allowed to ==totaly specialize== standard templates for types of our own creation. ```cpp= namespace std { template<> // this is a specialized version of std::swap void swap<Widget>(Widget& a, // for T is Widget Widget& b) { swap(a.pImpl, b.pImpl); // to swap Widgets, just swap their pImpl ptr // won't compile, accessing private member } } ``` We could declare the specialization a friend, but the convention is to have Widget declare a ==public member swap function==, then specialize std::swap to call the member function. ```cpp= class Widget { // same as above, expect for the additional public: // swap mem func ... void swap(Widget& other) { using std::swap; // explained later in this item swap(pImpl, other.pImpl); } }; namespace std { template<> // revised specialized of std::swap void swap<Widget>(Widget& a, Widget& b) { a.swap(b); // to swap Widgets, call their swap mem func } } ``` ### Problem: partially specialized Suppose Widget and WidgetImp were ==class templates== instead of classes. ```cpp= template<typename T> class WidgetImpl { ... }; template<typename T> class Widget {...}; ``` To make a specialization for std::swap, you might want to right: ```cpp= namespace std { template <typename T> void swap<Widget<T>> (Widget<T>& a, // error! illegal code! Widget<T>& b) { a.wap(b); } } ``` We run into trouble with the specialization for std:: swap. C++ allows ==partial specialization== fo class templates, but does NOT allow it for function templates. ### Solution: overloading Use ==overloading== when you want to "partially specialize" a function template. ```cpp= namespace std { template <typename T> void swap(Widget<T>& a, // an overloading of std::swap Widget<T>& b) // error! still invalid! { a.wap(b); } } ``` You are not allowed to add new templates (or classes or function ...) into std namespace. Therefore, we shouldn't be overloading std::swap. For example: ```cpp= namespace WidgetStuff { ... // templatized WidgetImpl, etc. template<typename T> // as before, including swap mem func class Widget {...} template <typename T> void swap(Widget<T>& a, // non-member swap function Widget<T>& b) // not part of the std namespace { a.wap(b); } } ``` It will still work by not using namespace in the above code. But why messing up the global namespace? ### From client perspective If you are a client, by writting the below code, which swap is being called? 1. The general one in std? (which exsist) 2. a specialization of general one in std? (which may or may not exist) 3. T-specific one? (which may or may not exist) ```cpp= template<typename T> void doSomething(T& obj1, T& obj2) { ... swap(obj1, obj2); ... } ``` As a user, your desire is to call a T-specific version if there is one, but fall back on general version in std if there's not. How to fulfill this desire? ```cpp= template<typename T> void doSomething(T& obj1, T& obj2) { using std::swap; // make std::swap available in this function ... swap(obj1, obj2); // call the best swap for type T ... } ``` When compiler see the call to swap, C++'s name lookup rules ensure that: 1. It will find any T-specific swap at global scope or in the same namespace first. 2. If not found, use the swap in std. ```cpp= std::swap(obj1, obj2); // wrong way to call swap ``` ### Conclusion 1. Provide a swap member function when std::swap would be inefficient for your type. 2. If you offer a member swap, also offer a non-member swap that calls the member. 3. When calling swap, employ a using declaration for std::swap, then call swap without namesapce qualification. # Chapter 5. Implementations ## Item 26: Postpone variable definitions as long as possible. This rule is applicable in any programming language. On the one hand, it can **avoid useless constructions** to make the program more efficient, and on the other hand, the **narrowing of the scope** can make the program **clearer**. Consider the following function, which returns an encrypted version of a password, provided the password is long enough. ``` string encryptPassword(const string& password){ string encrypted; if (password.length() < MinimumPasswordLength) { throw logic_error("Password is too short"); } encrypted = password; encrypt(encrypted); return encrypted; } ``` When encryptPassword **throws an exception**, **encrypted** is **useless** and there is no need to construct it at all. So a better way is to postpone the encrypted construction: ``` string encryptPassword(const string& password){ if (password.length() < MinimumPasswordLength) { throw logic_error("Password is too short"); } string encrypted; encrypted = password; encrypt(encrypted); return encrypted; } ``` In this way, we can improve the execution efficiency when an exception is thrown. In addition, **Item 4: Ensure that the initialization of the variable mentions**. It is better to initialize it with a value to construct an object and then assign it", so the above code has room for improvement: directly use password to initialize encrypted: ``` string encryptPassword(const string& password){ if (password.length() < MinimumPasswordLength) { throw logic_error("Password is too short"); } string encrypted(password); encrypt(encrypted); return encrypted; } ``` The definition of variables in **loops** is also a common point of contention. For example, we have two ways of writing: - Declare before loop ``` Widget w; for (int i = 0; i < n; ++i){ w = something dependent on i; ... } ``` - Declare in loop ``` for (int i = 0; i < n; ++i) { Widget w(something dependent on i); ... } ``` For the operation of Widget, it is the **cost** of the two types - Declare before loop: 1 constructor, 1 destructor, n assignment operators - Declare in loop: n constructors, n destructors Maybe just write it like this ``` { Widget w; for (int i = 0; i < n; ++i){ w = something dependent on i; ... } } ``` Declarin variable before loop which is not **conducive** to the **understanding** and **maintenance** of the program. The other makes it **easy to understand** when the variable is **used** and The lifetime of a variable is **easy to handle**, so it is recommended to use , **decalring in loop**, to achieve this. unless: - This code is the key to performance - Assignment is cheaper than a pair of construction/destruction ### New Feature in C++17 ``` std::map<int, std::string> m; ... auto itr = m.find(10); if ( itr != m.end() ) { return it->second; } ``` Use init-statement together with if ``` std::map<int, std::string> m; ... if (auto it = m.find(10); it != m.end()) { return it->second; } ``` ### Things to Remember 1. Postpone variable definitions as long as possible. It increases program clarity and improves program efficiency. ## Item 27: Minimize casting. C++ type checking is only performed at **compile time**, and there is no concept of **type errors at runtime**. In theory, as long as your code can be compiled, there will be no unsafe operations about type when running. But C++ allows type conversion, and it is type conversion that destroys the theoretical type system. There are three ways of type conversion in C++ 1. C-style type conversion: ``` (T) expression ``` 2. Function style type conversion ``` T(expression) ``` 3. C++ style type conversion, it is also called **named casting**. - `const_cast<T>(expression)`: Converts between types with different **cv-qualification**. - `static_cast<T>(expression)`: Converts between types using a combination of implicit and user-defined conversions. - `dynamic_cast<T>(expression)`: **Safely converts** pointers and references to classes up, down, and sideways along the inheritance hierarchy. Some forms of dynamic_cast rely on **runtime type identification (RTII)**. - `reinterpret_cast<T>(expression)`: Converts between types by reinterpreting the underlying **bit pattern**. ### Why use C++ style ? There is no difference between **C style** and **function style**, except that the position of the brackets is different. **C++ style type conversion** is more **clear** (the compiler will do more detailed checks) and it is not easy to be **misused**, and it is also easier to **find places** in the code. So try to use C++ style conversion, such as the following code, the latter is a better habit: ``` class Widget { public: explicit Widget(int size); ... }; void doSomeWork(const Widget& w); doSomeWork(Widget(15)); doSomeWork(static_cast<Widget>(15)); ``` ### What does type conversion do ? Many people think that type conversion just tells the compiler to treat it as a certain type. In fact, this is not the case, such as the most common numeric type conversion: ``` int x, y; double d = static_cast<double>(x)/y; ``` The above type conversion must have produced **more binary code**, because the underlying representations of **int and double on most platforms are not the same**. Here is another example: ``` Derived d; // Derived pointer Base *pb = &d; // Parent pointer ``` Sometimes the derived pointer and the parent pointer of the same object are not at the same address (depending on the **compiler and platform**), and the runtime code needs to calculate this **offset**. This is a unique phenomenon in C++, so don't make any assumptions about the memory layout of C++ objects. ![](https://i.imgur.com/sycNwNr.png) ### Need to conversion ? ``` class Window { // base class public: virtual void onResize() { ... } }; class SpecialWindow: public Window { // derived class public: virtual void onResize() { static_cast<Window>(*this).onResize(); ... } ... }; ``` The above code does not call the Window::onResize by the **current object**. the cast create a **new**, **temporary copy** of the base class part of **this**, then invokes onResize on the copy ! It is same as: ``` Window w = *this; w.onResize(); ``` The solution is to eliminate the cast. You should call the base class version of onResize of the current object. That's it: ``` class SpecialWindow: public Window{ public: virtual void onResize(){ Window::onResize(); ... } }; ``` Alright, you can do this same, using static_cast also, but you've to do this way: ``` static_cast<Window&>(*this).onResize(); ``` ### Dynamic_cast performance issues In general implementation, dynamic_cast **compares class name** level by level. For example, in a 4-level inheritance structure, dynamic_cast<Base> will call **strcmp 4 times** to determine the derived type. So try to avoid dynamic_cast in the **performance-sensitive** code. There are two general ways to avoid this problem. #### 1. Use the container of the derived class instead of the container of the parent class ``` vector<Window> v; dynamic_cast<SpecialWindow>(v[0]).blink(); vector<SpecialWindow> v; v[0].blink(); ``` This approach won’t allow you to store pointers to all possible Window derivatives in the same container. To work with different window types, you might need multiple type-safe containers. #### 2. Provide a unified parent interface through virtual functions. ``` class Window{ public: virtual void blink(); ... }; class SpecialWindow: public Window{ public: virtual void blink(); ... }; vector<Window> v; v[0].blink(); ``` These two methods do not solve all problems, but if you can solve your problem, you should use them to avoid type conversion. It depends on your usage scenario, but it is certain that continuous dynamic_cast must be avoided, such as this ``` for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) { if (SpecialWindow1 *psw1 = dynamic_cast<SpecialWindow1*>(iter->get())) { ... } else if (SpecialWindow2 *psw2 = dynamic_cast<SpecialWindow2*>(iter->get())) { ... } else if (SpecialWindow3 *psw3 = dynamic_cast<SpecialWindow3*>(iter->get())) { ... } ... } ``` Such C++ generates code that is large and slow, plus it’s brittle, because every time the Window class hierarchy changes, all such code has to be examined to see if it needs to be updated. ### Things to Remember 1. Avoid casts whenever practical, especially dynamic_casts in performance-sensitive code. If a design requires casting, try to develop a cast-free alternative. 2. When casting is necessary, try to hide it inside a function. Clients can then call the function instead of putting casts in their own code. 3. Prefer C++-style casts to old-style casts. They are easier to see, and they are more specific about what they do. ## Item 28: Avoid returning "handles" to object internals. The handle includes **reference**, **pointer**, and **iterator**. This can increase the **encapsulation of the class**, make the **const function** more **const**, and avoid the creation of **dangling handle**. ### Return the handle of private member ``` class Point { public: Point(int x, int y); ... void setX(int newVal); void setY(int newVal); ... }; struct RectData { Point ulhc; Point lrhc; }; class Rectangle { public: Point& upperLeft() const { return pData->ulhc; } Point& lowerRight() const { return pData->lrhc; } ... private: std::tr1::shared_ptr<RectData> pData; }; ``` This can be compiled, but it is **wrong**. Although upperLeft and lowerRight are declared as const, both functions **return references** to private internal data. The caller can use these references to modify internal data ! ``` Point coord1(0, 0); Point coord2(100, 100); const Rectangle rec(coord1, coord2); rec.upperLeft().setX(50); ``` **Decarling function as const** are defined as **bitwise constness**, as long as the **current object** has **not** been **modified**, it is regarded as a constant. So if we store data **outside the class**, the return value type check of the constant method will be invalid. The problems we single out in these functions can be eliminated by simply using **const** for their **return types**: ``` class Rectangle { public: ... const Point& upperLeft() const { return pData->ulhc; } const Point& lowerRight() const { return pData->lrhc; } ... }; ``` ### Issue of dangling handle Consider a function that returns the bounding box for a GUI object in the form of a rectangle: ``` class GUIObject { ... }; const Rectangle boundingBox(const GUIObject& obj); ``` Now consider how a client might use this function: ``` GUIObject *pgo; const Point *pUpperLeft = &(boundingBox(*pgo).upperLeft()); ``` pUpperLeft is dangling, it does **not maintain** any value ! Because the object returned by boundingBox() is **temporary object**. The temporary object is **destroyed immediately** after the statement is executed, and the reference to that any member will also become **invalid**. ### Things to Remember - Avoid returning handles (references, pointers, or iterators) to object internals. Not returning handles increases encapsulation, helps const member functions act const, and minimizes the creation of dangling handles.