Google
 

Tuesday, February 15, 2011

C++ Primer, Fourth Edition:Notes Chapter 14. Overloaded Operations and Conversions

  • C++ lets us redefine the meaning of the operators when applied to objects of class type. It also lets us define conversion operations for class types. Class-type conversions are used like the built-in conversions to implicitly convert an object of one type to another type when needed
  • Operator overloading allows the programmer to define versions of the operators for operands of class type
  • Through operator overloading, we can redefine most of the operators from Chapter 5 to work on objects of class type. Judicious use of operator overloading can make class types as intuitive to use as the built-in types
  • Allowing programs to use expressions rather than named functions can make the programs much easier to write and read.

Section 14.1 Defining an Overloaded Operator

  • Overloaded operators are functions with special names: the keyword operator followed by the symbol for the operator being defined. Like any other function, an overloaded operator has a return type and a parameter list
  • With the exception of the function-call operator, an overloaded operator has the same number of parameters (including the implicit this pointer for member functions) as the operator has operands. The function-call operator takes any number of operands
  • Overloaded Operator Names

      Table 14.1. Overloadable Operators

      +

      -

      *

      /

      %

      ^

      &

      |

      ~

      !

      ,

      =

      <

      >

      <=

      >=

      ++

      --

      <<

      >>

      ==

      !=

      &&

      ||

      +=

      -=

      /=

      %=

      ^=

      &=

      |=

      *=

      <<=

      >>=

      []

      ()

      ->

      ->*

      new

      new []

      delete

      delete[]


      Table 14.2. Operators That Cannot Be Overloaded

      ::

      .*

      .

      ?:


    • New operators may not be created by concatenating other legal symbols
  • Overloaded Operators Must Have an Operand of Class Type
    • The meaning of an operator for the built-in types may not be changed.Nor may additional operators be defined for the built-in data types
    • An overloaded operator must have at least one operand of class or enumeration (Section 2.7, p. 62) type. This rule enforces the requirement that an overloaded operator may not redefine the meaning of the operators when applied to objects of built-in type
  • Precedence and Associativity Are Fixed
    • The precedence (Section 5.10.1, p. 168), associativity, or number of operands of an operator cannot be changed
    • Four symbols (+, -, *, and &) serve as both unary and binary operators. Either or both of these operators can be overloaded. Which operator is being defined is controlled by the number of operands. Default arguments for overloaded operators are illegal, except for operator(), the function-call operator
  • Short-Ciruit Evaluation Is Not Preserved
    • Overloaded operators make no guarantees about the order in which operands are evaluated. In particular, the operand-evaluation guarantees of the built-in logical AND, logical OR (Section 5.2, p. 152), and comma (Section 5.9, p. 168) operators are not preserved. Both operands to an overloaded version of && or || are always evaluated. The order in which those operands are evaluated is not stipulated. The order in which the operands to the comma are evaluated is also not defined. For this reason, it is usually a bad idea to overload &&, ||, or the comma operator.
  • Class Member versus Nonmember
    • Most overloaded operators may be defined as ordinary nonmember functions or as class member functions
    • Overloaded functions that are members of a class may appear to have one less parameter than the number of operands. Operators that are member functions have an implicit this parameter that is bound to the first operand
  • Operator Overloading and Friendship
    • When operators are defined as nonmember functions, they often must be made friends (Section 12.5, p. 465) of the class(es) on which they operate
  • Using Overloaded Operators
    • We can use an overloaded operator in the same way that we'd use the operator on operands of built-in type
    • We also can call an overloaded operator function in the same way that we call an ordinary function: We name the function and pass an appropriate number of arguments of the appropriate type
      • cout << item1 + item2 << endl;
         
        // equivalent direct call to nonmember operator function
        // This call has the same effect as the expression that added item1 and item2.
        cout << operator+(item1, item2) << endl;

    • We call a member operator function the same way we call any other member function: We name an object on which to run the function and then use the dot or arrow operator to fetch the function we wish to call passing the required number and type of arguments. In the case of a binary member operator function, we must pass a single operand


        item1 += item2;            // expression based "call"
        item1.operator+=(item2);   // equivalent call to member operator function


  • 14.1.1. Overloaded Operator Design

    • Don't Overload Operators with Built-in Meanings

      • The assignment, address of, and comma operators have default meanings for operands of class types. If there is no overloaded version specified, the compiler defines its own version of these operators
      • The synthesized assignment operator (Section 13.2, p. 482) does memberwise assignment: It uses each member's own assignment operator to assign each member in turn
      • By default the address of (&) and comma (,) operators execute on class type objects the same way they do on objects of built-in type. The address of operator returns the address in memory of the object to which it is applied. The comma operator evaluates each expression from left to right and returns the value of its rightmost operand
      • The built-in logical AND (&&) and OR(||) operators apply short-circuit evaluation (Section 5.2, p. 152). If the operator is redefined, the short-circuit nature of the operators is lost
      • It is usually not a good idea to overload the comma, address-of, logical AND, or logical OR operators. These operators have built-in meanings that are useful and become inaccessible if we define our own versions
      • We sometimes must define our own version of assignment. When we do so, it should behave analogously to the synthesized operators: After an assignment, the values in the left-hand and right-hand operands should be the same and the operator should return a reference to its left-hand operand. Overloaded assignment should customize the built-in meaning of assignment, not circumvent it

    • Most Operators Have No Meaning for Class Objects

      • Operators other than assignment, address-of, and comma have no meaning when applied to an operand of class type unless an overloaded definition is provided. When designing a class, we decide which, if any, operators to support
      • The best way to design operators for a class is first to design the class' public interface. Once the interface is defined, it is possible to think about which operations should be defined as overloaded operators. Those operations with a logical mapping to an operator are good candidates

    • Compound Assignment Operators

      • If a class has an arithmetic (Section 5.1, p. 149) or bitwise (Section 5.3, p. 154) operator, then it is usually a good idea to provide the corresponding compound-assignment operator as well

    • Equality and Relational Operators

      • Classes that will be used as the key type of an associative container should define the < operator. The associative containers by default use the < operator of the key type. Even if the type will be stored only in a sequential container, the class ordinarily should define the equality (==) and less-than (<) operators. The reason is that many algorithms assume that these operators exist
      • If the class defines the equality operator, it should also define !=. Users of the class will assume that if they can compare for equality, they can also compare for inequality. The same argument applies to the other relational operators as well. If the class defines <, then it probably should define all four relational operators (>, >=, <, and <=)

        Caution: Use Operator Overloading Judiciously


        Each operator has an associated meaning from its use on the built-in types. Binary +, for example, is strongly identified with addition. Mapping binary + to an analogous operation for a class type can provide a convenient notational shorthand. For example, the library string type, following a convention common to many programming languages, uses + to represent concatenation"adding" one string to the other.


        Operator overloading is most useful when there is a logical mapping of a built-in operator to an operation on our type. Using overloaded operators rather than inventing named operations can make our programs more natural and intuitive. Overuse or outright abuse of operator overloading can make our classes incomprehensible.


        Obvious abuses of operator overloading rarely happen in practice. As an example, no responsible programmer would define operator+ to perform subtraction. More common, but still inadvisable, are uses that contort an operator's "normal" meaning to force a fit to a given type. Operators should be used only for operations that are likely to be unambiguous to users. An operator with ambiguous meaning, in this sense, is one that supports equally well a number of different interpretations.









        When the meaning of an overloaded operator is not obvious, it is better to give the operation a name. It is also usually better to use a named function rather than an operator for operations that are rarely done. If the operation is unusual, the brevity of using an operator is unnecessary.


    • Choosing Member or Nonmember Implementation

      • The following guidelines can be of help when deciding whether to make an operator a member or an ordinary nonmember function

        • The assignment (=), subscript ([]), call (()), and member access arrow (->) operators must be defined as members. Defining any of these operators as a nonmember function is flagged at compile time as an error
        • Like assignment, the compound-assignment operators ordinarily ought to be members of the class. Unlike assignment, they are not required to be so and the compiler will not complain if a nonmember compound-assignment operator is defined
        • Other operators that change the state of their object or that are closely tied to their given typesuch as increment, decrement, and dereferenceusually should be members of the class
        • Symmetric operators, such as the arithmetic, equality, relational, and bitwise operators, are best defined as ordinary nonmember functions


Section 14.2 Input and Output Operators



  • Classes that support I/O ordinarily should do so by using the same interface as defined by the iostream library for the built-in types. Thus, many classes provide overloaded instances of the input and output operators
  • 14.2.1. Overloading the Output Operator <<

    • To be consistent with the IO library, the operator should take an ostream& as its first parameter and a reference to a const object of the class type as its second. The operator should return a reference to its ostream parameter

      // general skeleton of the overloaded output operator
      ostream&
          operator <<(ostream& os, const ClassType &object)
      {
          // any special logic to prepare object
       
          // actual output of members
          os << // ...
       
              // return ostream object
              return os;
      }


  • The Sales_item Output Operator
  • Output Operators Usually Do Minimal Formatting

    • Class designers face one significant decision about output: whether and how much formatting to perform
    • Generally, output operators should print the contents of the object, with minimal formatting. They should not print a newline
    • The output operators for the built-in types do little if any formatting and do not print newlines. Given this treatment for the built-in types, users expect class output operators to behave similarly. By limiting the output operator to printing just the contents of the object, we let the users determine what if any additional formatting to perform. In particular, an output operator should not print a newline. If the operator does print a newline, then users would be unable to print descriptive text along with the object on the same line. By having the output operator perform minimal formatting, we let users control the details of their output

  • IO Operators Must Be Nonmember Functions

    • When we define an input or output operator that conforms to the conventions of the iostream library, we must make it a nonmember operator
    • We cannot make the operator a member of our own class.If we did, then the left-hand operand would have to be an object of our class type
    • Instead, if we want to use the overloaded operators to do IO for our types, we must define them as a nonmember functions. IO operators usually read or write the nonpublic data members. As a consequence, classes often make the IO operators friends

  • 14.2.2. Overloading the Input Operator >>

    • Similar to the output operator, the input operator takes a first parameter that is a reference to the stream from which it is to read, and returns a reference to that same stream. Its second parameter is a nonconst reference to the object into which to read. The second parameter must be nonconst because the purpose of an input operator is to read data into this object
    • A more important, and less obvious, difference between input and output operators is that input operators must deal with the possibility of errors and end-of-file
    • The Sales_item Input Operator


        istream& operator>>(istream& in, Sales_item& s)
        {
            double price;
            in >> s.isbn >> s.units_sold >> price;
            // check that the inputs succeeded
            if (in)
                s.revenue = s.units_sold * price;
            else
                s = Sales_item(); // input failed: reset object to default state
            return in;
        }

    • Errors During Input

      • The kinds of errors that might happen include:
      • Any of the read operations could fail because an incorrect value was provided.
      • Any of the reads could hit end-of-file or some other error on the input stream
      • Rather than checking each read, we check once before using the data we read

    • Handling Input Errors

      • If an input operator detects that the input failed, it is often a good idea to make sure that the object is in a usable and consistent state. Doing so is particularly important if the object might have been partially written before the error occurred
      • When designing an input operator, it is important to decide what to do about error-recovery, if anything

    • Indicating Errors

      • In addition to handling any errors that might occur, an input operator might need to set the condition state (Section 8.2, p. 287) of its input istream parameter
      • Some input operators do need to do additional checking.
      • Usually an input operator needs to set only the failbit. Setting eofbit would imply that the file was exhausted, and setting badbit would indicate that the stream was corrupted. These errors are best left to the IO library itself to indicate.

Section 14.3 Arithmetic and Relational Operators



  • Ordinarily, we define the arithmetic and relational operators as nonmember functions
  • Note that to be consistent with the built-in operator, addition returns an rvalue, not a reference
  • An arithmetic operator usually generates a new value that is the result of a computation on its two operands. That value is distinct from either operand and is calculated in a local variable. It would be a run-time error to return a reference to that variable
  • Classes that define both an arithmetic operator and the related compound assignment ordinarily ought to implement the arithmetic operator by using the compound assignment
  • It is simpler and more efficient to implement the arithmetic operator (e.g., +) in terms of the compound-assignment operator (e.g., +=) rather than the other way around. If we implemented += by calling +, then += would needlessly create and destroy a temporary to hold the result from +.
  • 14.3.1. Equality Operators

    • Ordinarily, classes in C++ use the equality operator to mean that the objects are equivalent. That is, they usually compare every data member and treat two objects as equal if and only if all corresponding members are the same


        inline bool
            operator==(const Sales_item &lhs, const Sales_item &rhs)
        {
            // must be made a friend of Sales_item
            return lhs.units_sold == rhs.units_sold &&
                lhs.revenue == rhs.revenue &&
                lhs.same_isbn(rhs);
        }
        inline bool
            operator!=(const Sales_item &lhs, const Sales_item &rhs)
        {
            return !(lhs == rhs); // != defined in terms of operator==
        }

    • The definition of these functions is trivial. More important are the design principles that these functions embody

      • If a class defines the == operator, it defines it to mean that two objects contain the same data
      • If a class has an operation to determine whether two objects of the type are equal, it is usually right to define that function as operator== rather than inventing a named operation. Users will expect to be able to compare objects using ==, and doing so is easier than remembering a new name
      • If a class defines operator==, it should also define operator!=. Users will expect that if they can use one operator, then the other will also exist
      • The equality and inequality operators should almost always be defined in terms of each other. One operator should do the real work to compare objects. The other should call the one that does the real work

    • Classes that define operator== are easier to use with the standard library. Some algorithms, such as find, use the == operator by default. If a class defines ==, then these algorithms can be used on that class type without any specialization

  • 14.3.2. Relational Operators

    • Classes for which the equality operator is defined also often have relational operators. In particular, because the associative containers and some of the algorithms use the less-than operator, it can be quite useful to define an operator<.
    • Because the logical definition of < is inconsistent with the logical definition of ==, it is better not to define < at all. We'll see in Chapter 15 how to use a separate named function to compare Sales_items when we want to store them in an associative container
    • The associative containers, as well as some of the algorithms, use the < operator by default. Ordinarily, the relational operators, like the equality operators, should be defined as nonmember functions



Section 14.4 Assignment Operators



  • We covered the assignment of one object of class type to another object of its type in Section 13.2 (p. 482). The class assignment operator takes a parameter that is the class type. Usually the parameter is a const reference to the class type. However, the parameter could be the class type or a nonconst reference to the class type. This operator will be synthesized by the compiler if we do not define it ourselves. The class assignment operator must be a member of the class so the compiler can know whether it needs to synthesize one
  • Additional assignment operators that differ by the type of the right-hand operand can be defined for a class type
  • Assignment operators can be overloaded. Unlike the compound-assignment operators, every assignment operator, regardless of parameter type, must be defined as a member function


      // illustration of assignment operators for class string
      class string {
      public:
          string& operator=(const string &);      // s1 = s2;
          string& operator=(const char *);        // s1 = "str";
          string& operator=(char);                // s1 = 'c';
          // ....
       };

  • Assignment Should Return a Reference to *this

    • The string assignment operators return a reference to string, which is consistent with assignment for the built-in types. Moreover, because assignment returns a reference there is no need to create and destroy a temporary copy of the result. The return value is usually a reference to the left-hand operand
    • Ordinarily, assignment operators and compound-assignment operators ought to return a reference to the left-hand operand

Section 14.5 Subscript Operator



  • Classes that represent containers from which individual elements can be retrieved usually define the subscript operator, operator[]. The library classes, string and vector, are examples of classes that define the subscript operator

  • The subscript operator must be defined as a class member function

  • Providing Read and Write Access


    • One complication in defining the subscript operator is that we want it to do the right thing when used as either the left- or right-hand operand of an assignment

    • It is also a good idea to be able to subscript const and nonconst objects. When applied to a const object, the return should be a const reference so that it is not usable as the target of an assignment

    • Ordinarily, a class that defines subscript needs to define two versions: one that is a nonconst member and returns a reference and one that is a const member and returns a const reference


  • Prototypical Subscript Operator



    • #include "stdafx.h"
      #include <vector>
      using namespace  std;
      class Foo {
      public:
          int &operator[] (const size_t);
          const int &operator[] (const size_t) const;
          // other interface members
      private:
          vector<int> data;
          // other member data and private utility functions
      };
       
      int& Foo::operator[] (const size_t index)
      {
          return data[index];  // no range checking on index
      }
      const int& Foo::operator[] (const size_t index) const
      {
          return data[index];  // no range checking on index
      }

Section 14.6 Member Access Operators


2011.2.21 here


Section 14.7 Increment and Decrement Operators


Section 14.8 Call Operator and Function Objects


Section 14.9 Conversions and Class Types

C++ Primer, Fourth Edition:Notes Chapter 17. Tools for Large Programs

 

 

 

Section 17.1 Exception Handling

Section 17.2 Namespaces

Section 17.3 Multiple and Virtual Inheritance

C++ Primer, Fourth Edition:Notes Chapter 16. Templates and Generic Programming

 

 

 

Section 16.1 Template Definitions

Section 16.2 Instantiation

Section 16.3 Template Compilation Models

Section 16.4 Class Template Members

Section 16.5 A Generic Handle Class

Section 16.6 Template Specializations

Section 16.7 Overloading and Function Templates

C++ Primer, Fourth Edition:Notes Chapter 15. Object-Oriented Programming

 

 

Section 15.1 OOP: An Overview

Section 15.2 Defining Base and Derived Classes

Section 15.3 Conversions and Inheritance

Section 15.4 Constructors and Copy Control

Section 15.5 Class Scope under Inheritance

Section 15.6 Pure Virtual Functions

Section 15.7 Containers and Inheritance

Section 15.8 Handle Classes and Inheritance

Section 15.9 Text Queries Revisited

C++ Primer, Fourth Edition:Notes Chapter Chapter 18. Specialized Tools and Techniques

 

 

Section 18.1 Optimizing Memory Allocation

Section 18.2 Run-Time Type Identification

Section 18.3 Pointer to Class Member

Section 18.4 Nested Classes

Section 18.5 Union: A Space-Saving Class

Section 18.6 Local Classes

Section 18.7 Inherently Nonportable Features

Thursday, February 10, 2011

C++ Primer, Fourth Edition:Notes Chapter 13. Copy Control

  • Each type, whether a built-in or class type, defines the meaning of a (possibly empty) set of operations on objects of that type
  • Each type also defines what happens when objects of the type are created. Initialization of objects of class type is defined by constructors. Types also control what happens when objects of the type are copied, assigned, or destroyed. Classes control these actions through special member functions: the copy constructor, the assignment operator, and the destructor. This chapter covers these operations
  • When we define a new type, we specifyexplicitly or implicitlywhat happens when objects of that type are copied, assigned, and destroyed. We do so by defining special members: the copy constructor, the assignment operator, and the destructor. If we do not explicitly define the copy constructor or the assignment operator, the compiler will (usually) define them for us
  • The copy constructor is a special constructor that has a single parameter that is a (usually const) reference to the class type. The copy constructor is used explicitly when we define a new object and initialize it from an object of the same type. It is used implicitly when we pass or return objects of that type to or from functions
  • The destructor is complementary to the constructors: It is applied automatically when an object goes out of scope or when a dynamically allocated object is deleted. The destructor is used to free resources acquired when the object was constructed or during the lifetime of the object. Regardless of whether a class defines its own destructor, the compiler automatically executes the destructors for the nonstatic data members of the class
  • Like constructors, the assignment operator may be overloaded by specifying different types for the right-hand operand. The version whose right-hand operand is of the class type is special: If we do not write one, the compiler will synthesize one for us
  • Collectively, the copy constructor, assignment operator, and destructor are referred to as copy control. The compiler automatically implements these operations, but the class may define its own versions
  • Copy control is an essential part of defining any C++ class. Programmers new to C++ are often confused by having to define what happens when objects are copied, assigned, or destroyed. This confusion is compounded because if we do not explicitly define these operations, the compiler defines them for usalthough they might not behave as we intend
  • Often the compiler-synthesized copy-control functions are finethey do exactly the work that needs to be done. But for some classes, relying on the default definitions leads to disaster. Frequently, the most difficult part of implementing the copy-control operations is recognizing when we need to override the default versions. One especially common case that requires the class to define its own the copy-control members is if the class has a pointer member

Section 13.1 The Copy Constructor

  • The constructor that takes a single parameter that is a (usually const) reference to an object of the class type itself is called the copy constructor. Like the default constructor, the copy constructor can be implicitly invoked by the compiler. The copy constructor is used to
    • Explicitly or implicitly initialize one object from another of the same type
    • Copy an object to pass it as an argument to a function
    • Copy an object to return it from a function
    • Initialize the elements in a sequential container
    • Initialize elements in an array from a list of element initializers
  • Forms of Object Definition
    • Recall that C++ supports two forms of initialization (Section 2.3.3, p. 48): direct and copy. Copy-initialization uses the = symbol, and direct-initialization places the initializer in parentheses
    • The copy and direct forms of initialization, when applied to objects of class type, are subtly different. Direct-initialization directly invokes the constructor matched by the arguments. Copy-initialization always involves the copy constructor. Copy-initialization first uses the indicated constructor to create a temporary object (Section 7.3.2, p. 247). It then uses the copy constructor to copy that temporary into the one we are creating
    • For objects of class type, copy-initialization can be used only when specifying a single argument or when we explicitly build a temporary object to copy
    • The copy form of initialization is primarily supported for compatibility with C usage. When it can do so, the compiler is permitted (but not obligated) to skip the copy constructor and create the object directly
    • Usually the difference between direct- or copy-initialization is at most a matter of low-level optimization. However, for types that do not support copying, or when using a constructor that is nonexplicit (Section 12.4.4, p. 462) the distinction can be essential
  • Parameters and Return Values
    • As we know, when a parameter is a nonreference type (Section 7.2.1, p. 230), the argument is copied. Similarly, a nonreference return value (Section 7.3.2, p. 247) is returned by copying the value in the return statement
    • When the parameter or return type is a class type, the copy is done by the copy constructor
  • Initializing Container Elements
    • The copy constructor is used to initialize the elements in a sequential container
    • As a general rule (Section 9.1.1, p. 307), unless you intend to use the default initial value of the container elements, it is more efficient to allocate an empty container and add elements as the values for those elements become known
  • Constructors and Array Elements
    • If we provide no element initializers for an array of class type, then the default constructor is used to initialize each element. However, if we provide explicit element initializers using the normal brace-enclosed array initialization list (Section 4.1.1, p. 111), then each element is initialized using copy-initialization. An element of the appropriate type is created from the specified value, and then the copy constructor is used to copy that value to the corresponding element
  • 13.1.1. The Synthesized Copy Constructor
    • If we do not otherwise define the copy constructor, the compiler synthesizes one for us. Unlike the synthesized default constructor (Section 12.4.3, p. 458), a copy constructor is synthesized even if we define other constructors. The behavior of the synthesized copy constructor is to memberwise initialize the new object as a copy of the original object
    • By memberwise, we mean that taking each nonstatic member in turn, the compiler copies the member from the existing object into the one being created. With one exception, the type of each member determines what it means to copy it. The synthesized copy constructor directly copies the value of members of built-in type. Members of class type are copied by using the copy constructor for that class. The one exception concerns array members. Even though we ordinarily cannot copy an array, if a class has a member that is an array, then the synthesized copy constructor will copy the array. It does so by copying each element
    • The simplest conceptual model of memberwise initialization is to think of the synthesized copy constructor as one in which each data member is initialized in the constructor initializer list
  • 13.1.2. Defining Our Own Copy Constructor
    • The copy constructor is the constructor that takes a single parameter that is a (usually const) reference to the class type
      class Foo {
           public:
              Foo();           // default constructor
              Foo(const Foo&); // copy constructor
              // ...
           };

    • Usually the parameter is a const reference, although we can also define the copy constructor to take a nonconst reference. Because the constructor is used (implicitly) to pass and return objects to and from functions, it usually should not be made explicit (Section 12.4.4, p. 462). The copy constructor should copy the members from its argument into the object that is being constructed
    • For many classes, the synthesized copy constructor does exactly the work that is needed. Classes that contain only members that are of class type or members that are of built-in (but not pointer type) often can be copied without explicitly defining the copy constructor
    • However, some classes must take control of what happens when objects are copied. Such classes often have a data member that is a pointer or that represents another resource that is allocated in the constructor. Other classes have bookkeeping that must be done whenever a new object is created. In both these cases, the copy constructor must be defined
    • Often the hardest part about defining a copy constructor is recognizing that a copy constructor is needed. Defining the constructor is usually pretty easy once the need for the constructor is recognized. The copy constructor itself is defined like any other constructor: It has the same name as the name of the class, it has no return value, it may (should) use a constructor initializer to initialize the members of the newly created object, and it may do any other necessary work inside a function body

  • 13.1.3. Preventing Copies

    • Some classes need to prevent copies from being made at all
    • To prevent copies, a class must explicitly declare its copy constructor as private
    • If the copy constructor is private, then user code will not be allowed to copy objects of the class type. The compiler will reject any attempt to make a copy
    • However, the friends and members of the class could still make copies. If we want to prevent copies even within the friends and members, we can do so by declaring a (private) copy constructor but not defining it
    • Most Classes Should Define Copy and Default Constructors

      • Classes that do not define the default constructor and/or the copy constructor impose serious limits on users of the class. Objects of classes that do not allow copies may be passed to (or returned from) a function only as a reference. They also may not be used as elements in a container
      • It is usually best to defineeither implicitly or explicitlythe default and copy constructors. The default constructor is synthesized only if there are no other constructors. If the copy constructor is defined, then the default constructor must be defined as well


Section 13.2 The Assignment Operator



  • Just as classes control how objects are initialized, they also define what happens when objects of their type are assigned
  • As with the copy constructor, the compiler synthesizes an assignment operator if the class does not define its own
  • Introducing Overloaded Assignment

    • Overloaded operators are functions that have the name operator followed by the symbol for the operator being defined. Hence, we define assignment by defining a function named operator=. Like any other function, an operator function has a return type and a parameter list. The parameter list must have the same number of parameters (including the implicit this parameter if the operator is a member) as the operator has operands. Assignment is binary, so the operator function has two parameters: The first parameter corresponds to the left-hand operand, and the second to the right-hand operand
    • Most operators may be defined as member or nonmember functions. When an operator is a member function, its first operand is implicitly bound to the this pointer. Some operators, assignment among them, must be members of the class for which the operator is defined. Because assignment must be a member of its class, this is bound to a pointer to the left-hand operand. The assignment operator, therefore, takes a single parameter that is an object of the same class type. Usually, the right-hand operand is passed as a const reference
    • The return type from the assignment operator should be the same as the return from assignment for the built-in types (Section 5.4.1, p. 160). Assignment to a built-in type returns a reference to its left-hand operand. Therefore, the assignment operator also returns a reference to the same type as its class

  • The Synthesized Assignment Operator

    • The synthesized assignment operator operates similarly to the synthesized copy constructor. It performs memberwise assignment: Each member of the right-hand object is assigned to the corresponding member of the left-hand object. Except for arrays, each member is assigned in the usual way for its type. For arrays, each array element is assigned
    • The synthesized assignment operator assigns each member in turn, using the built-in or class-defined assignment operator as appropriate to the type of the member. The operator returns *this, which is a reference to the left-hand object

  • Copy and Assign Usually Go Together

    • Classes that can use the synthesized copy constructor usually can use the synthesized assignment operator as well
    • However, a class may define its own assignment operator. In general, if a class needs a copy constructor, it will also need an assignment operator
    • In fact, these operations should be thought of as a unit. If we require one, we almost surely require the other


Section 13.3 The Destructor



  • The destructor is a special member function that can be used to do whatever resource deallocation is needed. It serves as the complement to the constructors of the class
  • When a Destructor Is Called

    • The destructor is called automatically whenever an object of its class is destroyed
    • An object that is dynamically allocated is destroyed only when a pointer pointing to the object is delete d. If we do not delete a pointer to a dynamically allocated object, then the destructor is never run on that object. The object will persist forever, leading to a memory leak. Moreover, any resources used inside the object will also not be released
    • The destructor is not run when a reference or a pointer to an object goes out of scope. The destructor is run only when a pointer to a dynamically allocated object is deleted or when an actual object (not a reference to the object) goes out of scope
    • Destructors are also run on the elements of class type in a container whether a library container or built-in array when the container is destroyed
    • The elements in the container are always destroyed in reverse order: The element indexed by size() - 1 is destroyed first, followed by the one indexed by size() - 2 and so on until element [0], which is destroyed last

  • When to Write an Explicit Destructor

    • Many classes do not require an explicit destructor. In particular, a class that has a constructor does not necessarily need to define its own destructor. Destructors are needed only if there is work for them to do. Ordinarily they are used to relinquish resources acquired in the constructor or during the lifetime of the object
    • A useful rule of thumb is that if a class needs a destructor, it will also need the assignment operator and a copy constructor. This rule is often referred to as the Rule of Three, indicating that if you need a destructor, then you need all three copy-control members
    • A destructor is not limited only to relinquishing resources. A destructor, in general, can perform any operation that the class designer wishes to have executed subsequent to the last use of an object of that class

  • The Synthesized Destructor

    • Unlike the copy constructor or assignment operator, the compiler always synthesizes a destructor for us. The synthesized destructor destroys each nonstatic member in the reverse order from that in which the object was created. In consequence, it destroys the members in reverse order from which they are declared in the class. For each member that is of class type, the synthesized destructor invokes that member's destructor to destroy the object
    • Destroying a member of built-in or compound type has no effect. In particular, the synthesized destructor does not delete the object pointed to by a pointer member

  • How to Write a Destructor

    • Classes that do allocate resources usually need to define a destructor to free those resources. The destructor is a member function with the name of the class prefixed by a tilde (~). It has no return value and takes no parameters. Because it cannot specify any parameters, it cannot be overloaded. Although we can define multiple class constructors, we can provide only a single destructor to be applied to all objects of our class
    • An important difference between the destructor and the copy constructor or assignment operator is that even if we write our own destructor, the synthesized destructor is still run


Section 13.4 A Message-Handling Example



  • The Message Class

  • Copy Control for the Message Class


    • When we write our own copy constructor, we must explicitly copy any members that we want copied. An explicitly defined copy constructor copies nothing automatically

    • As with any other constructor, if we do not initialize a class member, then that member is initialized using the member's default constructor. Default initialization in a copy constructor does not use the member's copy constructor

  • The put_Msg_in_Folders Member

  • Message Assignment Operator


    • The assignment operator starts by checking that the left- and right-hand operands are not the same. We do this check for reasons that will become apparent as we walk through the rest of the function

    • Assignment involves obliterating the left-hand operand. Once the members of the left-hand operand are destroyed, those in the right-hand operand are assigned to the corresponding left-hand members. If the objects were the same, then destroying the left-hand members would also destroy the right-hand members

    • It is crucially important for assignment operators to work correctly, even when an object is assigned to iself. A common way to ensure this behavior is by checking explicitly for self-assignment

  • The remove_Msg_from_Folders Member

  • The Message Destructor


    • The assignment operator often does the same work as is needed in the copy constructor and destructor. In such cases, the common work should be put in private utility functions


Section 13.5 Managing Pointer Members



  • Classes that contain pointers require careful attention to copy control. The reason they must do so is that copying a pointer copies only the address in the pointer. Copying a pointer does not copy the object to which the pointer points.

  • When designing a class with a pointer member, the first decision a class author must make is what behavior that pointer should provide. When we copy one pointer to another, the two pointers point to the same object. When two pointers point to the same object, it is possible to use either pointer to change the underlying object. Similarly, it is possible for one pointer to delete the object even though the user of the other pointer still thinks the underlying object exists

  • By default, a pointer member has the same behavior as a pointer object. However, through different copy-control strategies we can implement different behavior for pointer members

  • Most C++ classes take one of three approaches to managing pointer members


    • The pointer member can be given normal pointer like behavior. Such classes will have all the pitfalls of pointers but will require no special copy control.

    • The class can implement so-called "smart pointer" behavior. The object to which the pointer points is shared, but the class prevents dangling pointers.

    • The class can be given value like behavior. The object to which the pointer points will be unique to and managed separately by each class object.

  • In this section we look at three classes that implement each of these different approaches to managing their pointer members.

  • A Simple Class with a Pointer Member

  • Default Copy/Assignment and Pointer Members


    • Classes that have pointer members and use default synthesized copy control have all the pitfalls of ordinary pointers. In particular, the class itself has no way to avoid dangling pointers

  • Pointers Share the Same Object

  • Dangling Pointers Are Possible

  • 13.5.1. Defining Smart Pointer Classes


    • An alternative to having a pointer member behave exactly like a pointer is to define what is sometimes referred to as a smart pointer class. A smart pointer behaves like an ordinary pointer except that it adds functionality.

    • In this case, we'll give our smart pointer the responsibility for deleting the shared object. Users will dynamically allocate an object and pass the address of that object to our new HasPtr class. The user may still access the object through a plain pointer but must not delete the pointer. The HasPtr class will ensure that the object is deleted when the last HasPtr that points to it is destroyed.

    • Our new HasPtr class will need a destructor to delete the pointer. However, the destructor cannot delete the pointer unconditionally. If two HasPtr objects point to the same underlying object, we don't want to delete the object until both objects are destroyed. To write the destructor, we need to know whether this HasPtr is the last one pointing to a given object

    • Introducing Use Counts


      • A common technique used in defining smart pointers is to use a use count. The pointerlike class associates a counter with the object to which the class points. The use count keeps track of how many objects of the class share the same pointer. When the use count goes to zero, then the object is deleted. A use count is sometimes also referred to as a reference count

      • Each time a new object of the class is created, the pointer is initialized and the use count is set to 1. When an object is created as a copy of another, the copy constructor copies the pointer and increments the associated use count. When an object is assigned to, the assignment operator decrements the use count of the object to which the left-hand operand points (and deletes that object if the use count goes to zero) and increments the use count of the object pointed to by the right-hand operand. Finally, when the destructor is called, it decrements the use count and deletes the underlying object if the count goes to zero

    • The Use-Count Class


      • There are two classic strategies for implementing a use count, one of which we will use here; the other approach is described in Section 15.8.1 (p. 599). In the approach we use here, we'll define a separate concrete class to encapsulate the use count and the associated pointer

    • Using the Use-Counted Class

    • Assignment and Use Counts


      • This assignment operator guards against self-assignment by incrementing the use count of rhs before decrementing the use count of the left-hand operand

    • Changing Other Members

    • Advice: Managing Pointer Members


      Objects with pointer members often need to define the copy-control members. If we rely on the synthesized versions, then the class puts a burden on its users. Users must ensure that the object to which the member points stays around for at least as long as the object that points to it does.


      To manage a class with pointer members, we must define all three copy-control members: the copy constructor, assignment operator, and the destructor. These members can define either pointerlike or valuelike behavior for the pointer member.


      Valuelike classes give each object its own copy of the underlying values pointed to by pointer members. The copy constructor allocates a new element and copies the value from the object it is copying. The assignment operator destroys the existing object it holds and copies the value from its right-hand operand into its left-hand operand. The destructor destroys the object.


      As an alternative to defining either valuelike behavior or pointerlike behavior some classes are so-called "smart pointers." These classes share the same underlying value between objects, thus providing pointerlike behavior. But they use copy-control techniques to avoid some of the pitfalls of regular pointers. To implement smart pointer behavior, a class needs to ensure that the underlying object stays around until the last copy goes away. Use counting (Section 13.5.1, p. 495), is a common technique for managing smart pointer classes. Each copy of the same underlying value is given a use count. The copy constructor copies the pointer from the old object into the new one and increments the use count. The assignment operator decrements the use count of the left-hand operand and increments the count of the right-hand operand. If the use count of the left-hand operand goes to zero, the assignment operator must delete the object to which it points. Finally, the assignment operator copies the pointer from the right-hand operand into its left-hand operand. The destructor decrements the use count and deletes the underlying object if the count goes to zero.









      These approaches to managing pointers occur so frequently that programmers who use classes with pointer members must be thoroughly familiar with these programming techniques.


  • 13.5.2. Defining Value like Classes


    • A completely different approach to the problem of managing pointer members is to give them value semantics. Simply put, classes with value semantics define objects that behave like the arithmetic types: When we copy a valuelike object, we get a new, distinct copy. Changes made to the copy are not reflected in the original, and vice versa. The string class is an example of a valuelike class

    • The copy constructor no longer copies the pointer. It now allocates a new int object and initializes that object to hold the same value as the object of which it is a copy. Each object always holds its own, distinct copy of its int value. Because each object holds its own copy, the destructor unconditionally deletes the pointer

    • As always, the assignment operator must be correct even if we're assigning an object to itself. In this case, the operations are inherently safe even if the left- and right-hand objects are the same. Thus, there is no need to explicitly check for self-assignment