Chapter 13: Copy Control. Overview. Overview. Overview

Similar documents
Copy Control 2008/04/08. Programming Research Laboratory Seoul National University

3.Constructors and Destructors. Develop cpp program to implement constructor and destructor.

Ch. 12: Operator Overloading

CS201 Some Important Definitions

See the CS 2704 notes on C++ Class Basics for more details and examples. Data Structures & OO Development I

Chapter 15. Object-Oriented Programming

OBJECT ORIENTED PROGRAMMING USING C++ CSCI Object Oriented Analysis and Design By Manali Torpe

Smart Pointers. Some slides from Internet

Abstraction and Encapsulation. Benefits of Abstraction & Encapsulation. Concrete Types. Using Typedefs to streamline classes.

C++ Classes 2008/04/03. Programming Research Laboratory Seoul National University

COMP6771 Advanced C++ Programming

QUIZ. What is wrong with this code that uses default arguments?

1. Describe History of C++? 2. What is Dev. C++? 3. Why Use Dev. C++ instead of C++ DOS IDE?

Chapter 10 Pointers and Dynamic Arrays. GEDB030 Computer Programming for Engineers Fall 2017 Euiseong Seo

2 ADT Programming User-defined abstract data types

Short Notes of CS201

COMP6771 Advanced C++ Programming

KOM3191 Object Oriented Programming Dr Muharrem Mercimek OPERATOR OVERLOADING. KOM3191 Object-Oriented Programming

CS201 - Introduction to Programming Glossary By

Cpt S 122 Data Structures. Introduction to C++ Part II

Object-Oriented Programming for Scientific Computing

Chapter 10. Pointers and Dynamic Arrays. Copyright 2016 Pearson, Inc. All rights reserved.

CSE 333. Lecture 11 - constructor insanity. Hal Perkins Paul G. Allen School of Computer Science & Engineering University of Washington

The Compositional C++ Language. Denition. Abstract. This document gives a concise denition of the syntax and semantics

Instantiation of Template class

Interview Questions of C++

Arrays. Returning arrays Pointers Dynamic arrays Smart pointers Vectors

The issues. Programming in C++ Common storage modes. Static storage in C++ Session 8 Memory Management

Modern and Lucid C++ Advanced for Professional Programmers. Part 3 Move Semantics. Department I - C Plus Plus Advanced

Absolute C++ Walter Savitch

Software Engineering Concepts: Invariants Silently Written & Called Functions Simple Class Example

Intermediate Programming & Design (C++) Classes in C++

Quiz Start Time: 09:34 PM Time Left 82 sec(s)

QUIZ on Ch.5. Why is it sometimes not a good idea to place the private part of the interface in a header file?

CS201 Latest Solved MCQs

C How to Program, 6/e, 7/e

OBJECT ORIENTED PROGRAMMING

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Java Primer 1: Types, Classes and Operators

Arrays and Pointers. Overview. Arrays Introducing Pointers C-Style Character Strings Multidimensioned Arrays

Object-Oriented Principles and Practice / C++

Operator overloading. Conversions. friend. inline

Fast Introduction to Object Oriented Programming and C++

QUIZ Friends class Y;

More About Classes. Gaddis Ch. 14, CS 2308 :: Fall 2015 Molly O'Neil

Introduction Of Classes ( OOPS )

CS304 Object Oriented Programming Final Term

GEA 2017, Week 4. February 21, 2017

Program construction in C++ for Scientific Computing

Object-Oriented Programming

Data Abstraction. Hwansoo Han

CS 251 INTERMEDIATE SOFTWARE DESIGN SPRING C ++ Basics Review part 2 Auto pointer, templates, STL algorithms

Project. C++: Smart Pointers. The Plan. Announcement. Memory Leak. Pointer Ownership. Today: STL 1 Wednesday: STL 2 Thursday: Smart Pointers

1/29/2011 AUTO POINTER (AUTO_PTR) INTERMEDIATE SOFTWARE DESIGN SPRING delete ptr might not happen memory leak!

Vector and Free Store (Vectors and Arrays)

Programming in C ++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

CPSC 427: Object-Oriented Programming

Value categories. PRvalues. Lvalues

Pointers! Arizona State University 1

Memory Leak. C++: Memory Problems. Memory Leak. Memory Leak. Pointer Ownership. Memory Leak

Example : class Student { int rollno; float marks; public: student( ) //Constructor { rollno=0; marks=0.0; } //other public members };

Slide Set 14. for ENCM 339 Fall Steve Norman, PhD, PEng. Electrical & Computer Engineering Schulich School of Engineering University of Calgary

Object Oriented Design

explicit class and default definitions revision of SC22/WG21/N1582 =

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Outline. User-dened types Categories. Constructors. Constructors. 4. Classes. Concrete classes. Default constructor. Default constructor

Ch. 11: References & the Copy-Constructor. - continued -

Scope. Scope is such an important thing that we ll review what we know about scope now:

Lesson 12 - Operator Overloading Customising Operators

Data Structures using OOP C++ Lecture 3

COMP6771 Advanced C++ Programming

Special Member Functions

Computer Science II CSci 1200 Lecture 18 Operators and Friends

Function Declarations. Reference and Pointer Pitfalls. Overloaded Functions. Default Arguments

Classes and Objects. Class scope: - private members are only accessible by the class methods.

Determine a word is a palindrome? bool ispalindrome(string word, int front, int back) { Search if a number is in an array

calling a function - function-name(argument list); y = square ( z ); include parentheses even if parameter list is empty!

CPSC 427a: Object-Oriented Programming

Cpt S 122 Data Structures. Course Review Midterm Exam # 2

CSCI-1200 Data Structures Spring 2018 Lecture 8 Templated Classes & Vector Implementation

CSCI 262 Data Structures. Arrays and Pointers. Arrays. Arrays and Pointers 2/6/2018 POINTER ARITHMETIC

... IntArray B (100); // IntArray with 100 elements, each initialized to 0

ALL ABOUT POINTERS C/C++ POINTERS

Consider the program...

static CS106L Spring 2009 Handout #21 May 12, 2009 Introduction

Special Member Functions. Compiler-Generated Destructor. Compiler-Generated Default Constructor. Special Member Functions

nptr = new int; // assigns valid address_of_int value to nptr std::cin >> n; // assigns valid int value to n

Advanced Systems Programming

Operator overloading

Copying Data. Contents. Steven J. Zeil. November 13, Destructors 2

M.EC201 Programming language

Constructors and Destructors. OOC 4 th Sem, B Div Prof. Mouna M. Naravani

A class is a user-defined type. It is composed of built-in types, other user-defined types and

CE221 Programming in C++ Part 1 Introduction

Recharge (int, int, int); //constructor declared void disply();

Pointers. Developed By Ms. K.M.Sanghavi

14.1. Chapter 14: static member variable. Instance and Static Members 8/23/2014. Instance and Static Members

Implementing Abstractions

CS1622. Semantic Analysis. The Compiler So Far. Lecture 15 Semantic Analysis. How to build symbol tables How to use them to find

Advanced C++ 4/13/2017. The user. Types of users. Const correctness. Const declaration. This pointer and const.

Transcription:

Chapter 13: Copy Control Overview The Copy Constructor The Assignment Operator The Destructor A Message-Handling Example Managing Pointer Members Each type, whether a built-in or class type, defines the meaning of a (possibly empty) set of operations on objects of that type We can add two int values, run size on a vector, and so on These operations define what can be done with objects of the given type. 1 2 Overview 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 the destructor Overview When we define a new type, we specify explicitly or implicitly what 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. 3 4 1

Overview 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. Overview In this chapter we will cover the assignment operator. 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. 5 6 Overview 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 Overview This confusion is compounded because if we do not explicitly define these operations, the compiler defines them for us although they might not behave as we intend Often the compiler-synthesized copy-control functions are fine they do exactly the work that needs to be done But for some classes, relying on the default definitions leads to disaster 7 8 2

Overview 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. 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, e.g., string s2(s1) Sales_item item2(item1) Like the default constructor, the copy constructor can be implicitly invoked by the compiler 9 10 The Copy Constructor The Copy Constructor The copy constructor is used to Explicitly or implicitly initialize one object from another of the same type Recall that C++ supports two forms of initialization: direct and copy Copy-initialization uses the = symbol direct-initialization places the initializer in parentheses 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 string null_book = "9-999-99999-9"; // copy-initialization string dots(10, '.'); // direct-initialization string empty_copy = string(); // copy-initialization string empty_direct; // direct-initialization 11 12 3

The Copy Constructor 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 It then uses the copy constructor to copy that temporary into the one we are creating: string null_book = "9-999-99999-9"; // copy-initialization string dots(10, '.'); // direct-initialization 13 The Copy Constructor 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. string null_book = "9-999-99999-9"; // copy-initialization string dots(10, '.'); // direct-initialization When dots is created, the string constructor that takes a count and a character is called and directly initializes the members in dots To create null_book, the compiler first creates a temporary by invoking the string constructor that takes a C-style character string parameter The compiler then uses the string copy constructor to initialize null_book as a copy of that temporary 14 The Copy Constructor string empty_copy = string(); // copy-initialization string empty_direct; // direct-initialization The initialization of empty_copy and empty_direct both call the string default constructor In the first case, the default constructor creates a temporary object, which is then used by the copy constructor to initialize empty_copy In the second case, the default constructor is run directly on empty_direct. The Copy Constructor Usually the difference between direct- or copyinitialization 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 the distinction can be essential: 15 16 4

The Copy Constructor ifstream file1("filename"); // ok: direct initialization ifstream file2 = "filename"; // error // This initialization is okay only if // the Sales_item(const string&) constructor is not explicit Sales_item item = string("9-999-99999-9"); The ifstream class defines a constructor that can be called with a C-style string. That constructor is used to initialize file1 The seemingly equivalent initialization of file2 uses copy-initialization. That definition is not okay. We cannot copy objects of the IO types, so we cannot use copy-initialization on objects of these types. 17 The Copy Constructor ifstream file1("filename"); // ok: direct initialization ifstream file2 = "filename"; // error: copy constructor is private // This initialization is okay only if // the Sales_item(const string&) constructor is not explicit Sales_item item = string("9-999-99999-9"); Whether the initialization of item is okay depends on which version of our Sales_item class we are using. Some versions define the constructor that takes a string as explicit If the constructor is explicit, then the initialization fails If the constructor is not explicit, then the initialization is fine. 18 Initializing Container Elements The copy constructor is used to initialize the elements in a sequential container For example, we can initialize a container using a single parameter that represents a size This form of construction uses both the default constructor and the copy constructor for the element Initializing Container Elements // default string constructor and five string copy constructors invoked vector<string> svec(5); The compiler initializes svec by first using the default string constructor to create a temporary value. The copy constructor is then used to copy the temporary into each element of svec. // default string constructor and five string copy constructors invoked vector<string> svec(5); 19 20 5

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, then each element is initialized using copyinitialization 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: Constructors and Array Elements Sales_item primer_eds[] = { string("0-201-16487-6"), string("0-201-54848-8"), string("0-201-82470-1"), Sales_item() }; 21 22 The Synthesized Copy Constructor If we do not otherwise define the copy constructor, the compiler synthesizes one for us Unlike the synthesized default constructor, 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. The Synthesized Copy Constructor 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 23 24 6

The Synthesized Copy Constructor The one exception concerns array members 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 Synthesized Copy Constructor 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 class Sales_item { // other members and constructors as before private: std::string isbn; int units_sold; double revenue; }; 25 26 The Synthesized Copy Constructor the synthesized Sales_item copy constructor would look something like: Sales_item::Sales_item(const Sales_item &orig): isbn(orig.isbn), // uses string copy constructor units_sold(orig.units_sold), // copies orig.units_sold revenue(orig.revenue) // copy orig.revenue { } 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 //... }; 27 28 7

Defining Our Own Copy Constructor Usually the parameter is a const reference, although we can also define the copy constructor to take a nonconst reference 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. Defining Our Own 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. 29 30 Defining Our Own Copy Constructor 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. Preventing Copies Some classes need to prevent copies from being made at all For example, the iostream classes do not permit copying It might seem that if we want to forbid copies, we could omit the copy constructor However, if we don't define a copy constructor, the compiler will synthesize one To prevent copies, a class must explicitly declare its copy constructor as private. 31 32 8

Preventing Copies 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 It is legal to declare but not define a member function Preventing Copies However, any attempt to use an undefined member results in a link-time failure By declaring (but not defining) a private copy constructor, we can forestall any attempt to copy an object of the class type Attempts to make copies in user code will be flagged as an error at compile time, and attempts to make copies in member functions or friends will result in an error at link time. 33 34 The Assignment Operator Just as classes control how objects are initialized, they also define what happens when objects of their type are assigned: Sales_item trans, accum; trans = accum; 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 35 36 9

Introducing Overloaded Assignment 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, The second to the right-hand operand. Introducing Overloaded Assignment 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 assignment operator also returns a reference to the same type as its class. 37 38 Introducing Overloaded Assignment For example, the assignment operator for Sales_item might be declared as class Sales_item { public: // other members as before // equivalent to the synthesized assignment operator Sales_item& operator=(const Sales_item &); }; 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. 39 40 10

The Synthesized Assignment Operator As an example, the synthesized Sales_item assignment operator would look something like: // equivalent to the synthesized assignment operator Sales_item& Sales_item::operator=(const Sales_item &rhs) { isbn = rhs.isbn; // calls string::operator= units_sold = rhs.units_sold; // uses built-in int assignment revenue = rhs.revenue; // uses built-in double assignment return *this; } 41 Copy and Assign Usually Go Together Classes that can use the synthesized copy constructor usually can use the synthesized assignment operator as well Our Sales_item class has no need to define either the copy constructor or the assignment operator: The synthesized versions of these operators work fine. 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. 42 The Destructor One purpose of a constructor is to provide for the automatic acquisition of a resource For example, a constructor might allocate a buffer or open a file Having allocated the resource in the constructor, we need a corresponding operation that automatically deallocates or otherwise releases the resource 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. 43 The Destructor The destructor is called automatically whenever an object of its class is destroyed: // p points to default constructed object Sales_item *p = new Sales_item; { // new scope Sales_item item(*p); // copy constructor copies *p delete p; // destructor called on object pointed to by p } // exit local scope; destructor called on item 44 11

The Destructor // p points to default constructed object Sales_item *p = new Sales_item; { // new scope Sales_item item(*p); // copy constructor copies *p delete p; // destructor called on object pointed to by p } // exit local scope; destructor called on item Variables such as item are destroyed automatically when they go out of scope Hence, the destructor on item is run when the close curly is encountered. 45 The Destructor // p points to default constructed object Sales_item *p = new Sales_item; { // new scope Sales_item item(*p); // copy constructor copies *p delete p; // destructor called on object pointed to by p } // exit local scope; destructor called on item An object that is dynamically allocated is destroyed only when a pointer pointing to the object is deleted 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. 46 The Destructor 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. 47 The Destructor 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: { Sales_item *p = new Sales_item[10]; // dynamically allocated vector<sales_item> vec(p, p + 10); // local object //... delete [] p; // array is freed; destructor run on each element } // vec goes out of scope; destructor run on each element 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. 48 12

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. When to Write an Explicit Destructor 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 49 50 The Synthesized Destructor 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. How to Write a Destructor 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. 51 52 13

How to Write a Destructor 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 class Sales_item { public: // empty; no work to do other than destroying the members, // which happens automatically ~Sales_item() { } // other members as before }; 53 How to Write a Destructor When objects of type Sales_item are destroyed, this destructor, which does nothing, would be run After it completes, the synthesized destructor would also be run to destroy the members of the class The synthesized destructor destroys the string member by calling the string destructor, which frees the memory used to hold the isbn. The units_sold and revenue members are of built-in type, so the synthesized destructor does nothing to destroy them. class Sales_item { public: // empty; no work to do other than destroying the members, // which happens automatically ~Sales_item() { } 54 // other members as before A Message-Handling Example As an example of a class that needs to control copies in order to do some bookkeeping, we'll sketch out two classes that might be used in a mail-handling application These classes, Message and Folder, represent, respectively, email (or other) messages and directories in which a message might appear A given Message might appear in more than one Folder. We'll have save and remove operations on Message that save or remove that message in the specified Folder. A Message-Handling Example Rather than putting a copy of each Message into each Folder, we'll have each Message hold a set of pointers to the Folders in which this Message appears Each Folder will also store pointers to the Messages it contains 55 56 14

A Message-Handling Example When we create a new Message, we will specify the contents of the message but no Folder Calling save will put a Message in a Folder When we copy a Message, we'll copy the contents of the original message the set of Folder pointers We must also add a pointer to this Message to each of the Folders that points to the original Message. Assigning Message Assigning one Message to another behaves similarly to copying a Message: After the assignment, the contents and set of Folders will be the same We'll start by removing the existing left-hand message from the Folders it was in prior to the assignment Once the old Message is gone, we'll copy the contents and set of Folders from the right-hand operand into the left We'll also have to add a pointer to the left-hand Message to each Folder in this set. 57 58 Destroy a Message When we destroy a Message, we must update each Folder that points to the Message Once the Message goes away, those pointers will be no good, so we must remove the pointer to this Message from each Folder in the Message's own set of Folder pointers. Destroy a Message Looking at this list of operations, we can see that the destructor and the assignment operator share the work of removing messages from the list of Folders that had held a given Message Similarly, the copy constructor and the assignment operator share the work of adding a Message to a given list of Folders We'll define a pair of private utility functions to do these tasks. 59 60 15

The Message Class class Message { public: // folders is initialized to the empty set automatically Message(const std::string &str = ""): contents (str) { } // copy control: we must manage pointers to this Message // from the Folders pointed to by folders Message(const Message&); Message& operator=(const Message&); ~Message(); The Message Class // add/remove this Message from specified Folder's set of messages void save (Folder&); void remove(folder&); private: std::string contents; // actual message text std::set<folder*> folders; // Folders that have this Message // Utility functions used by copy constructor, assignment, destructor: // Add this Message to the Folders that point to the parameter 61 void put_msg_in_folders(const std::set<folder*>&); // remove this Message from every Folder in folders void remove_msg_from_folders(); }; 62 Copy Control for the Message Class When we copy a Message, we have to add the newly created Message to each Folder that holds the Message from which we're copying This work is beyond what the synthesized constructor would do for us, so we must define our own copy constructor: Copy Control for the Message Class Message::Message(const Message &m): contents(m.contents), folders(m.folders) { // add this Message to each Folder that points to m put_msg_in_folders(folders); } 63 The copy constructor initializes the data members of the new object as copies of the members from the old We must also iterate through folders, adding this Message to each Folder in that set The copy constructor uses the put_msg_in_folder function to do this work. 64 16

The put_msg_in_folders Member put_msg_in_folders iterates through the pointers in the folders member of the parameter rhs These pointers denote each Folder that points to rhs. We need to add a pointer to this Message to each of those Folders. The function does this work by looping through rhs.folders, calling the Folder member named addmsg. That function will do whatever it takes to add a pointer to this Message to that Folder: The put_msg_in_folders Member // add this Message to Folders that point to rhs void Message::put_Msg_in_Folders(const set<folder*> &rhs) { for(std::set<folder*>::const_iterator beg = rhs.begin(); beg!= rhs.end(); ++beg) (*beg)->addmsg(this); // *beg points to a Folder } 65 66 Message Assignment Operator Assignment is more complicated than the copy constructor Like the copy constructor, assignment must assign the contents and update folders to match that of the right-hand operand It must also add this Message to each of the Folders that points to the rhs We can use our put_msg_in_folders function to do this part of the assignment. Message Assignment Operator Before copying from the rhs, we must first remove this Message from each of the Folders that currently point to it We'll need to iterate through folders, removing the pointer to this Message from each Folder in folders. The function named remove_msg_from_folders will do this work. 67 68 17

Message Assignment Operator Message& Message::operator=(const Message &rhs) { if (&rhs!= this) { remove_msg_from_folders(); // update existing Folders contents = rhs.contents; // copy contents from rhs folders = rhs.folders; // copy Folder pointers from rhs // add this Message to each Folder in rhs put_msg_in_folders(rhs.folders); } return *this; } 69 The remove_msg_from_folders Member The implementation of the remove_msg_from_folders function is similar to that of put_msg_in_folders, except that this time we'll call remmsg to remove this Message from each Folder pointed to by folders: // remove this Message from corresponding Folders void Message::remove_Msg_from_Folders() { // remove this message from corresponding folders for(std::set<folder*>::const_iterator beg = folders.begin (); beg!= folders.end (); ++beg) (*beg)->remmsg(this); // *beg points to a Folder } 70 The Message Destructor Managing Pointer Members Message::~Message() { remove_msg_from_folders(); } The system automatically invokes the string destructor to free contents and the set destructor to clean up the memory used to hold the folders member Thus, the only work for the Message destructor is to call remove_msg_from_folders. Using the standard library greatly reduces the need for pointers in modern C++ programs However, many applications still require the use of pointers, particularly in the implementation of classes 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. 71 72 18

Managing Pointer Members 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. Managing Pointer Members 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: 1. The pointer member can be given normal pointerlike behavior. Such classes will have all the pitfalls of pointers but will require no special copy control. 2. The class can implement so-called "smart pointer" behavior. The object to which the pointer points is shared, but the class prevents dangling pointers. 3. The class can be given valuelike behavior. The object to which the pointer points will be unique to and managed separately by each class object. 73 74 A Simple Class with a Pointer Member class HasPtr { public: // copy of the values we're given HasPtr(int *p, int i): ptr(p), val(i) { } // const members to return the value of the indicated data int *get_ptr() const { return ptr; } int get_int() const { return val; } A Simple Class with a Pointer Member // return or change the value pointed to, so ok for const objects int get_ptr_val() const { return *ptr; } void set_ptr_val(int val) const { *ptr = val; } private: int *ptr; int val; }; // non const members to change the indicated data member void set_ptr(int *p) { ptr = p; } void set_int(int i) { val = i; } 75 76 19

Default Copy/Assignment and Pointer Members Because the class does not define a copy constructor, copying one HasPtr object to another copies both members: int obj = 0; HasPtr ptr1(&obj, 42); // int* member points to obj, val is 42 HasPtr ptr2(ptr1); // int* member points to obj, val is 42 Pointers Share the Same Object When we copy an arithmetic value, the copy is independent from the original. We can change one copy without changing the other: ptr1.set_int(0); // changes val member only in ptr1 ptr2.get_int(); // returns 42 ptr1.get_int(); // returns 0 After the copy, the int values are distinct and independent, whereas the pointers are intertwined. 77 78 Pointers Share the Same Object When we copy a pointer, the address values are distinct, but the pointers point to the same underlying object. If we call set_ptr_val on either object, the underlying object is changed for both: ptr1.set_ptr_val(42); // sets object to which both ptr1 and ptr2 point ptr2.get_ptr_val(); // returns 42 When two pointers point to the same object, either one can change the value of the shared object. Dangling Pointers Are Possible Because our class copies the pointers directly, it presents our users with a potential problem: HasPtr stores the pointer it was given It is up to the user to guarantee that the object to which that pointer points stays around as long as the HasPtr object does: int *ip = new int(42); // dynamically allocated int initialized to 42 HasPtr ptr(ip, 10); // Has Ptr points to same object as ip does delete ip; // object pointed to by ip is freed ptr.set_ptr_val(0); // disaster: The object to which Has Ptr points //was freed! 79 80 20

Defining Smart Pointer Classes In the previous section we defined a simple class that held a pointer and an int The pointer member behaved in all ways like any other pointer Any changes made to the object to which the pointer pointed were made to a single, shared object If the user deleted that object, then our class had a dangling pointer Its pointer member pointed at an object that no longer existed. 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 81 82 Defining Smart Pointer Classes 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. Defining Smart Pointer Classes 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. 83 84 21

Introducing Use Counts A common technique used in defining smart pointers is to use a use count The pointer like 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. Introducing Use Counts 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. 85 86 Introducing Use Counts The counter cannot go directly into our HasPtr object. To see why, consider what happens in the following case: int obj; HasPtr p1(&obj, 42); HasPtr p2(p1); // p1 and p2 both point to same int object HasPtr p3(p1); // p1, p2, and p3 all point to same int object If the use count is stored in a HasPtr object, how can we update it correctly when p3 is created? We could increment the count in p1 and copy that count into p3, but how would we update the counter in p2? The Use-Count Class we'll define a separate concrete class to encapsulate the use count and the associated pointer: // private class for use by HasPtr only class U_Ptr { friend class HasPtr; int *ip; size_t use; U_Ptr(int *p): ip(p), use(1) { } ~U_Ptr() { delete ip; } }; 87 88 22

The Use-Count Class All the members of this class are private We don't intend ordinary users to use the U_Ptr class, so we do not give it any public members The HasPtr class is made a friend so that its members will have access to the members of U_Ptr The U_Ptr class holds the pointer and the use count. Each HasPtr will point to a U_Ptr The Use-Count Class The use count will keep track of how many HasPtr objects point to each U_Ptr object The only functions U_Ptr defines are its constructor and destructor. The constructor copies the pointer, which the destructor deletes. The constructor also sets the use count to 1, indicating that a HasPtr object points to this U_Ptr. 89 90 The Use-Count Class Assuming we just created a HasPtr object from a pointer that pointed to an int value of 42, we might picture the objects as follows: The Use-Count Class If we copy this object, then the objects will be 91 92 23

The Use-Count Class class HasPtr { public: // HasPtr owns the pointer; HasPtr(int *p, int i): ptr(new U_Ptr(p)), val(i) { } // copy members and increment the use count HasPtr(const HasPtr &orig): ptr(orig.ptr), val(orig.val) { ++ptr->use; } HasPtr& operator=(const HasPtr&); Assignment and Use Counts The assignment operator is a bit more complicated than the copy constructor 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 // if use count goes to zero, delete the U_Ptr object ~HasPtr() { if (--ptr->use == 0) delete ptr; } private: U_Ptr *ptr; // points to use-counted U_Ptr class int val; }; 93 94 Assignment and Use Counts HasPtr& HasPtr::operator=(const HasPtr &rhs) { ++rhs.ptr->use; // increment use count on rhs first if (--ptr->use == 0) delete ptr; // if use count goes to 0, delete it ptr = rhs.ptr; // copy the U_Ptr object val = rhs.val; // copy the int member return *this; } 95 Changing Other Members The other members that access the int* now need to change to get to the int indirectly through the U_Ptr pointer: class HasPtr { public: // copy control and constructors as before // accessors must change to fetch value from U_Ptr object int *get_ptr() const { return ptr->ip; } int get_int() const { return val; } // change the appropriate data member void set_ptr(int *p) { ptr->ip = p; } void set_int(int i) { val = i; } 96 24

Changing Other Members The other members that access the int* now need to change to get to the int indirectly through the U_Ptr pointer: // return or change the value pointed to, so ok for const objects // Note: *ptr->ip is equivalent to *(ptr->ip) int get_ptr_val() const { return *ptr->ip; } void set_ptr_val(int i) { *ptr->ip = i; } private: U_Ptr *ptr; int val; }; // points to use-counted U_Ptr class 97 25