Module 14
Intructors: Abir
Das and
Sourangshu
Bhattacharya
Module 14: Programming in C++
Obj. Lifetime Copy Constructor and Copy Assignment Operator
String
Date
Rect
Copy Constructor
Call by Value
Intructors: Abir Das and Sourangshu Bhattacharya
Signature
Free Copy & Pitfall
Department of Computer Science and Engineering
Assignment Op.
Indian Institute of Technology, Kharagpur
Copy Objects
Self-Copy
{abir, sourangshu}@cse.iitkgp.ac.in
Signature
Free Assignment
Comparison
Slides taken from NPTEL course on Programming in Modern C++
Class as Type
by Prof. Partha Pratim Das
Module Summary
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 1
Module Objectives
Module 14
Intructors: Abir • More on Object Lifetime
Das and
Sourangshu
Bhattacharya
• Understand Copy Construction
Obj. Lifetime
• Understand Copy Assignment Operator
String
Date
• Understand Shallow and Deep Copy
Rect
Copy Constructor
Call by Value
Signature
Free Copy & Pitfall
Assignment Op.
Copy Objects
Self-Copy
Signature
Free Assignment
Comparison
Class as Type
Module Summary
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 2
Module Outline
Module 14 1 Object Lifetime Examples
Intructors: Abir String
Das and
Sourangshu Date: Practice
Bhattacharya Rect: Practice
Obj. Lifetime 2 Copy Constructor
String
Date
Call by Value
Rect Signature
Copy Constructor
Free Copy Constructor and Pitfalls
Call by Value
Signature
3 Copy Assignment Operator
Free Copy & Pitfall Copy Objects
Assignment Op. Self-Copy
Copy Objects Signature
Self-Copy
Signature
Free Assignment Operator
Free Assignment
4 Comparison of Copy Constructor and Copy Assignment Operator
Comparison
Class as Type 5 Class as a Data-type
Module Summary
6 Module Summary
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 3
Program 14.01/02: Order of Initialization: Order of Data Members
#include <iostream> #include <iostream>
Module 14 using namespace std; using namespace std;
Intructors: Abir
int init_m1(int m) { // Func. to init m1_ int init_m1(int m) { // Func. to init m1_
Das and cout << "Init m1_: " << m << endl; cout << "Init m1_: " << m << endl;
Sourangshu return m; return m;
Bhattacharya
} }
int init_m2(int m) { // Func. to init m2_ int init_m2(int m) { // Func. to init m2_
Obj. Lifetime
cout << "Init m2_: " << m << endl; cout << "Init m2_: " << m << endl;
String
return m; return m;
Date
Rect
} }
class X { int m1_; // Initialize 1st class X { int m2_; // Order of data members swapped
Copy Constructor int m2_; // Initialize 2nd int m1_;
Call by Value
public: X(int m1, int m2) : public: X(int m1, int m2) :
Signature
m1_(init_m1(m1)), // Called 1st m1_(init_m1(m1)), // Called 2nd
Free Copy & Pitfall
m2_(init_m2(m2)) // Called 2nd m2_(init_m2(m2)) // Called 1st
Assignment Op. { cout << "Ctor: " << endl; } { cout << "Ctor: " << endl; }
Copy Objects ~X() { cout << "Dtor: " << endl; } }; ~X() { cout << "Dtor: " << endl; } };
Self-Copy
int main() { X a(2, 3); return 0; } int main() { X a(2, 3); return 0; }
Signature
----- -----
Free Assignment
Init m1_: 2 Init m2_: 3
Comparison Init m2_: 3 Init m1_: 2
Class as Type Ctor: Ctor:
Dtor: Dtor:
Module Summary
• Order of initialization does not depend on the order in the initialization list. It depends on the order of data members
in the definition
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 4
Program 14.03/04: A Simple String Class
C Style C++ Style
Module 14
#include <iostream> #include <iostream>
Intructors: Abir #include <cstring> #include <cstring>
Das and
Sourangshu
#include <cstdlib> #include <cstdlib>
Bhattacharya using namespace std; using namespace std;
struct String { char *str_; // Container class String { char *str_; // Container
Obj. Lifetime size_t len_; // Length size_t len_; // Length
String }; public: String(char *s) : str_(strdup(s)), // Uses malloc()
Date void print(const String& s) { len_(strlen(str_))
Rect cout << s.str_ << ": " { cout << "ctor: "; print(); }
Copy Constructor << s.len_ << endl; ~String() { cout << "dtor: "; print();
Call by Value } free(str_); // To match malloc() in strdup()
Signature int main() { String s; }
Free Copy & Pitfall void print() { cout << "(" << str_ << ": "
Assignment Op. // Init data members << len_ << ")" << endl; }
Copy Objects
s.str_ = strdup("Partha"); size_t len() { return len_; }
Self-Copy s.len_ = strlen(s.str_); };
Signature print(s); int main() { String s = "Partha"; // Ctor called
Free Assignment free(s.str); s.print();
Comparison
} }
----- -----
Class as Type ctor: (Partha: 6)
Partha: 6
Module Summary (Partha: 6)
dtor: (Partha: 6)
• Note the order of initialization between str and len . What if we swap them?
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 5
Program 14.05: A Simple String Class:
Fails for wrong order of data members
#include <iostream>
Module 14
#include <cstring>
Intructors: Abir #include <cstdlib>
Das and using namespace std;
Sourangshu
Bhattacharya
class String {
Obj. Lifetime size_t len_; // Swapped members cause garbage to be printed or program crash (unhandled exception)
String char *str_;
Date public:
Rect String(char *s) : str_(strdup(s)), len_(strlen(str_)) { cout << "ctor: "; print(); }
Copy Constructor
~String() { cout << "dtor: "; print(); free(str_); }
Call by Value
void print() { cout << "(" << str_ << ": " << len_ << ")" << endl; }
Signature
};
Free Copy & Pitfall int main() { String s = "Partha";
s.print();
Assignment Op.
}
Copy Objects
Self-Copy
----- // May produce garbage or crash
Signature
ctor: (Partha: 20)
Free Assignment (Partha: 20) // Garbage
dtor: (Partha: 20)
Comparison
Class as Type • len precedes str in list of data members
• len (strlen(str )) is executed before str (strdup(s))
Module Summary
• When strlen(str ) is called str is still uninitialized
• May causes the program to crash
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 6
Practice: Program 14.06: A Simple Date Class
#include <iostream>
Module 14
using namespace std;
Intructors: Abir
Das and char monthNames[][4]={ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
Sourangshu
Bhattacharya char dayNames[][10] ={ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
class Date {
Obj. Lifetime enum Month { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };
String enum Day { Mon, Tue, Wed, Thr, Fri, Sat, Sun };
Date typedef unsigned int UINT;
Rect UINT date_; Month month_; UINT year_;
Copy Constructor
public:
Call by Value
Date(UINT d, UINT m, UINT y) : date_(d), month_((Month)m), year_(y) { cout << "ctor: "; print(); }
Signature
~Date() { cout << "dtor: "; print(); }
Free Copy & Pitfall void print() { cout << date_ << "/" << monthNames[month_ - 1] << "/" << year_ << endl; }
bool validDate() { /* Check validity */ return true; } // Not implemented
Assignment Op.
Day day() { /* Compute day from date using time.h */ return Mon; } // Not implemented
Copy Objects
Self-Copy
};
Signature
int main() {
Free Assignment Date d(30, 7, 1961);
d.print();
Comparison
}
Class as Type -----
Module Summary ctor: 30/Jul/1961
30/Jul/1961
dtor: 30/Jul/1961
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 7
Practice: Program 14.07: Point and Rect Classes:
Lifetime of Data Members or Embedded Objects
Module 14
#include <iostream> class Rect { Point TL_; Point BR_; public:
using namespace std; Rect(int tlx, int tly, int brx, int bry):
Intructors: Abir class Point { int x_; int y_; public: TL_(tlx, tly), BR_(brx, bry)
Das and
Sourangshu
Point(int x, int y): { cout << "Rect ctor: ";
Bhattacharya x_(x), y_(y) print(); cout << endl; }
{ cout << "Point ctor: "; ~Rect() { cout << "Rect dtor: ";
Obj. Lifetime print(); cout << endl; } print(); cout << endl; }
String ~Point() { cout << "Point dtor: "; void print() { cout << "["; TL_.print();
Date print(); cout << endl; } cout << " "; BR_.print(); cout << "]"; }
Rect void print() { cout << "(" << x_ << ", " };
Copy Constructor
<< y_ << ")"; } -----
Call by Value
}; Point ctor: (0, 2)
Signature Point ctor: (5, 7)
Free Copy & Pitfall int main() { Rect ctor: [(0, 2) (5, 7)]
Rect r (0, 2, 5, 7);
Assignment Op.
Copy Objects
[(0, 2) (5, 7)]
Self-Copy
cout << endl; r.print(); cout << endl;
Signature Rect dtor: [(0, 2) (5, 7)]
Free Assignment cout << endl; Point dtor: (5, 7)
} Point dtor: (0, 2)
Comparison
Class as Type • Attempt is to construct a Rect object
Module Summary • That, in turn, needs constructions of Point data members (or embedded objects) – TL and BR respectively
• Destruction, initiated at the end of scope of destructor’s body, naturally follows a reverse order
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 8
Copy Constructor
Module 14
Intructors: Abir • We know:
Das and
Sourangshu
Bhattacharya
Complex c1(4.2, 5.9);
invokes
Obj. Lifetime
String Constructor Complex::Complex(double, double);
Date
Rect • Which constructor is invoked for?
Copy Constructor
Call by Value
Complex c2(c1);
Signature
Free Copy & Pitfall
Assignment Op. Or for?
Copy Objects
Self-Copy Complex c2 = c1;
Signature
Free Assignment • It is the Copy Constructor that takes an object of the same type and constructs a
Comparison copy:
Class as Type
Complex::Complex(const Complex &);
Module Summary
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 9
Program 14.08: Complex: Copy Constructor
#include <iostream>
Module 14
#include <cmath> -----
Intructors: Abir using namespace std; Complex ctor: |4.2+j5.3| = 6.7624 // Ctor: c1
Das and class Complex { double re_, im_; public: Complex copy ctor: |4.2+j5.3| = 6.7624 // CCtor: c2 of c1
Sourangshu
Bhattacharya // Constructor Complex copy ctor: |4.2+j5.3| = 6.7624 // CCtor: c3 of c2
Complex(double re, double im): |4.2+j5.3| = 6.7624 // c1
Obj. Lifetime re_(re), im_(im) |4.2+j5.3| = 6.7624 // c2
String { cout << "Complex ctor: "; print(); } |4.2+j5.3| = 6.7624 // c3
Date // Copy Constructor Complex dtor: |4.2+j5.3| = 6.7624 // Dtor: c3
Rect Complex(const Complex& c): Complex dtor: |4.2+j5.3| = 6.7624 // Dtor: c2
Copy Constructor
re_(c.re_), im_(c.im_) Complex dtor: |4.2+j5.3| = 6.7624 // Dtor: c1
Call by Value
{ cout << "Complex copy ctor: "; print(); }
Signature
// Destructor
Free Copy & Pitfall ~Complex()
{ cout << "Complex dtor: "; print(); }
Assignment Op.
double norm() { return sqrt(re_*re_ + im_*im_); }
Copy Objects
Self-Copy
void print() { cout << "|" << re_ << "+j" << im_ << "| = " << norm() << endl; }
Signature
};
Free Assignment int main() {
Complex c1(4.2, 5.3), // Constructor - Complex(double, double)
Comparison
c2(c1), // Copy Constructor - Complex(const Complex&)
Class as Type c3 = c2; // Copy Constructor - Complex(const Complex&)
Module Summary
c1.print(); c2.print(); c3.print();
}
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 10
Why do we need Copy Constructor?
Module 14
Intructors: Abir • Consider the function call mechanisms in C++:
Das and
Sourangshu
Bhattacharya
◦ Call-by-reference: Set a reference to the actual parameter as a formal parameter.
Both the formal parameter and the actual parameter share the same location
Obj. Lifetime
String
(object). No copy is needed
Date ◦ Return-by-reference: Set a reference to the computed value as a return value. Both
Rect
the computed value and the return value share the same location (object). No copy
Copy Constructor
Call by Value is needed
Signature
Free Copy & Pitfall
◦ Call-by-value: Make a copy or clone of the actual parameter as a formal parameter.
Assignment Op.
This needs a Copy Constructor
Copy Objects ◦ Return-by-value: Make a copy or clone of the computed value as a return value.
Self-Copy
Signature This needs a Copy Constructor
Free Assignment
Comparison
• Copy Constructor is needed for initializing the data members of a UDT from an
Class as Type
existing value
Module Summary
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 11
Program 14.09: Complex: Call by value
#include <iostream>
Module 14
#include <cmath>
Intructors: Abir using namespace std;
Das and class Complex { double re_, im_; public:
Sourangshu
Bhattacharya Complex(double re, double im): re_(re), im_(im) // Constructor
{ cout << "ctor: "; print(); }
Obj. Lifetime Complex(const Complex& c): re_(c.re_), im_(c.im_) // Copy Constructor
String { cout << "copy ctor: "; print(); }
Date ~Complex() { cout << "dtor: "; print(); }
Rect double norm() { return sqrt(re_*re_ + im_*im_); }
Copy Constructor
void print() { cout << "|" << re_ << "+j" << im_ << "| = " << norm() << endl; }
Call by Value
};
Signature
void Display(Complex c_param) { // Call by value
Free Copy & Pitfall cout << "Display: "; c_param.print();
}
Assignment Op.
int main() { Complex c(4.2, 5.3); // Constructor - Complex(double, double)
Copy Objects
Self-Copy
Signature
Display(c); // Copy Constructor called to copy c to c_param
Free Assignment }
-----
Comparison
ctor: |4.2+j5.3| = 6.7624 // Ctor of c in main()
Class as Type copy ctor: |4.2+j5.3| = 6.7624 // Ctor c_param as copy of c, call Display()
Module Summary Display: |4.2+j5.3| = 6.7624 // c_param
dtor: |4.2+j5.3| = 6.7624 // Dtor c_param on exit from Display()
dtor: |4.2+j5.3| = 6.7624 // Dtor of c on exit from main()
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 12
Signature of Copy Constructors
Module 14
Intructors: Abir
• Signature of a Copy Constructor can be one of:
Das and
Sourangshu MyClass(const MyClass& other); // Common
Bhattacharya
// Source cannot be changed
MyClass(MyClass& other); // Occasional
Obj. Lifetime
// Source needs to change. Like in smart pointers
String
MyClass(volatile const MyClass& other); // Rare
Date
Rect
MyClass(volatile MyClass& other); // Rare
Copy Constructor
Call by Value
• None of the following are copy constructors, though they can copy:
Signature
Free Copy & Pitfall MyClass(MyClass* other);
Assignment Op. MyClass(const MyClass* other);
Copy Objects
Self-Copy
Signature
• Why the parameter to a copy constructor must be passed as Call-by-Reference?
Free Assignment
MyClass(MyClass other);
Comparison
Class as Type
Module Summary
The above is an infinite recursion of copy calls as the call to copy constructor itself
needs to make copy for the Call-by-Value mechanism
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 13
Free Copy Constructor
Module 14
• If no copy constructor is provided by the user, the compiler supplies a free one
Intructors: Abir
• Free copy constructor cannot initialize the object to proper values. It performs Shallow Copy
Das and • Shallow Copy aka bit-wise copy, field-by-field copy, field-for-field copy, or field copy
Sourangshu
Bhattacharya ◦ An object is created by simply copying the data of all variables of the original object
Obj. Lifetime
◦ Works well if none of the variables of the object are defined in heap / free store
String ◦ For dynamically created variables, the copied object refers to the same memory location
Date
Rect
◦ Creates ambiguity (changing one changes the copy) and run-time errors (dangling pointer)
Copy Constructor • Deep Copy or its variants Lazy Copy and Copy-on-Write
Call by Value
Signature
◦ An object is created by copying data of all variables except the ones on heap
Free Copy & Pitfall ◦ Allocates similar memory resources with the same value to the object
Assignment Op. ◦ Need to explicitly define the copy constructor and assign dynamic memory as required
Copy Objects
Self-Copy
◦ Required to dynamically allocate memory to the variables in the other constructors
Signature
Free Assignment
Comparison
Class as Type
Module Summary
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 14
Pitfalls of Bit-wise Copy: Shallow Copy
Module 14 • Consider a class:
Intructors: Abir
class A { int i_; // Non-pointer data member
Das and int* p_; // Pointer data member
Sourangshu public:
Bhattacharya
A(int i, int j) : i_(i), p_(new int(j)) { } // Init. with pointer to dynamically created object
~A() { cout << "Destruct " << this << ": "; // Object identity
Obj. Lifetime
cout << "i_ = " << i_ << " p_ = " << p_ << " *p = " << *p_ << endl; // Object state
String
delete p_; // Release resource
Date
}
Rect
};
Copy Constructor
Call by Value • As no copy constructor is provided, the implicit copy constructor does a bit-wise copy. So
Signature
Free Copy & Pitfall
when an A object is copied, p is copied and continues to point to the same dynamic int:
int main() { A a1(2, 3); A a2(a1); // Construct a2 as a copy of a1. Done by bit-wise copy
Assignment Op.
cout << "&a1 = " << &a1 << " &a2 = " << &a2 << endl;
Copy Objects
Self-Copy
}
Signature
Free Assignment
• The output is wrong, as a1.p = a2.p points to the same int location. Once a2 is destructed,
Comparison
a2.p is released, and a1.p becomes dangling. The program may print garbage or crash:
&a1 = 008FF838 &a2 = 008FF828 // Identities of objects
Class as Type
Destruct 008FF828: i_ = 2 p_ = 00C15440 *p = 3 // Dtor of a2. Note that a2.p_ = a1.p_
Module Summary Destruct 008FF838: i_ = 2 p_ = 00C15440 *p = -17891602 // Dtor of a1. a1.p_=a2.p_ points to garbage
• The bit-wise copy of members is known as Shallow Copy
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 15
Pitfalls of Bit-wise Copy: Deep Copy
Module 14 • Now suppose we provide a user-defined copy constructor:
Intructors: Abir
class A { int i_; // Non-pointer data member
Das and int* p_; // Pointer data member
Sourangshu public:
Bhattacharya
A(int i, int j) : i_(i), p_(new int(j)) { } // Init. with pointer to dynamically created object
A(const A& a) : i_(a.i_), // Copy Constructor
Obj. Lifetime
p_(new int(*a.p_)) { } // Allocation done and value copied - Deep Copy
String
~A() { cout << "Destruct " << this << ": "; // Object identity
Date
cout << "i_ = " << i_ << " p_ = " << p_ << " *p = " << *p_ << endl; // Object state
Rect
delete p_; // Release resource
Copy Constructor }
Call by Value
};
Signature
Free Copy & Pitfall • The output now is correct, as a1.p ̸= a2.p points to the different int locations with the
Assignment Op. values *a1.p = *a2.p properly copied:
Copy Objects
&a1 = 00B8F9E0 &a2 = 00B8F9D0 // Identities of objects
Self-Copy
Signature
Destruct 00B8F9D0: i_ = 2 p_ = 00C95480 *p = 3 // Dtor of a2. a2.p_ is different from a1.p_
Free Assignment
Destruct 00B8F9E0: i_ = 2 p_ = 00C95440 *p = 3 // Dtor of a1. Works correctly!
Comparison • This is known as Deep Copy where every member is copied properly. Note that:
Class as Type ◦ In every class, provide copy constructor to adopt to deep copy which is always safe
Module Summary ◦ Naturally, shallow copy is cheaper than deep copy.
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 16
Practice: Program 14.10: Complex: Free Copy Constructor
#include <iostream>
Module 14
#include <cmath>
Intructors: Abir using namespace std;
Das and class Complex { double re_, im_; public:
Sourangshu
Bhattacharya Complex(double re, double im) : re_(re), im_(im) { cout << "ctor: "; print(); } // Ctor
// Complex(const Complex& c) : re_(c.re_), im_(c.im_) { cout<<"copy ctor: "; print(); } // CCtor: Free only
Obj. Lifetime ~Complex() { cout << "dtor: "; print(); } // Dtor
String double norm() { return sqrt(re_*re_ + im_*im_); }
Date void print() { cout << "|" << re_ << "+j" << im_ << "| = " << norm() << endl; }
Rect };
Copy Constructor
void Display(Complex c_param) { cout << "Display: "; c_param.print(); }
Call by Value
int main() { Complex c(4.2, 5.3); // Constructor - Complex(double, double)
Signature
Display(c); // Free Copy Constructor called to copy c to c_param
Free Copy & Pitfall }
User-defined CCtor Free CCtor
Assignment Op.
ctor: |4.2+j5.3| = 6.7624 ctor: |4.2+j5.3| = 6.7624
Copy Objects
copy ctor: |4.2+j5.3| = 6.7624 No message from free CCtor
Self-Copy
Display: |4.2+j5.3| = 6.7624 Display: |4.2+j5.3| = 6.7624
Signature
Free Assignment
dtor: |4.2+j5.3| = 6.7624 dtor: |4.2+j5.3| = 6.7624
dtor: |4.2+j5.3| = 6.7624 dtor: |4.2+j5.3| = 6.7624
Comparison
Class as Type • User has provided no copy constructor
• Compiler provides free copy constructor
Module Summary
• Compiler-provided copy constructor performs bit-wise copy - hence there is no message
• Correct in this case as members are of built-in type and there is no dynamically allocated data
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 17
Practice: Program 14.11: String: User-defined Copy Constructor
#include <iostream>
Module 14
#include <cstdlib>
Intructors: Abir #include <cstring>
Das and using namespace std;
Sourangshu
Bhattacharya class String { public: char *str_; size_t len_;
String(char *s) : str_(strdup(s)), len_(strlen(str_)) { } // Ctor
Obj. Lifetime String(const String& s) : str_(strdup(s.str_)), len_(s.len_) { } // CCtor: User provided
String ~String() { free(str_); } // Dtor
Date void print() { cout << "(" << str_ << ": " << len_ << ")" << endl; }
Rect };
Copy Constructor
void strToUpper(String a) { // Make the string uppercase
Call by Value
for (int i = 0; i < a.len_; ++i) { a.str_[i] = toupper(a.str_[i]); }
Signature
cout << "strToUpper: "; a.print();
Free Copy & Pitfall } // a.~String() is invoked releasing a.str_. s.str_ remains intact
int main() { String s = "Partha"; s.print(); strToUpper(s); s.print(); }
Assignment Op.
---
Copy Objects
Self-Copy
(Partha: 6)
Signature
strToUpper: (PARTHA: 6)
Free Assignment (Partha: 6)
Comparison • User has provided copy constructor. So Compiler does not provide free copy constructor
Class as Type • When actual parameter s is copied to formal parameter a, space is allocated for a.str and then it is copied from
s.str . On exit from strToUpper, a is destructed and a.str is deallocated. But in main, s remains intact and access
Module Summary
to s.str is valid.
• Deep Copy: While copying the object, the pointed object is copied in a fresh allocation. This is safe
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 18
Practice: Program 14.12: String: Free Copy Constructor
#include <iostream>
Module 14 #include <cstring>
Intructors: Abir
#include <cstdlib>
Das and using namespace std;
Sourangshu class String { public: char *str_; size_t len_;
Bhattacharya
String(char *s) : str_(strdup(s)), len_(strlen(str_)) { } // Ctor
// String(const String& s) : str_(strdup(s.str_)), len_(s.len_) { } // CCtor: Free only
Obj. Lifetime
~String() { free(str_); } // Dtor
String
void print() { cout << "(" << str_ << ": " << len_ << ")" << endl; }
Date
Rect
};
void strToUpper(String a) { // Make the string uppercase
Copy Constructor for (int i = 0; i < a.len_; ++i) { a.str_[i] = toupper(a.str_[i]); } cout<<"strToUpper: "; a.print();
Call by Value
} // a.~String() is invoked releasing a.str_ and invalidating s.str_ = a.str_
Signature
int main() { String s = "Partha"; s.print(); strToUpper(s); s.print(); } // Last print fails
Free Copy & Pitfall
Assignment Op. User-defined CCtor Free CCtor
Copy Objects (Partha: 6) (Partha: 6)
Self-Copy strToUpper: (PARTHA: 6) strToUpper: (PARTHA: 6)
Signature (Partha: 6) (?????????????????????????????: 6)
Free Assignment
• User has provided no copy constructor. Compiler provides free copy constructor
Comparison
• Free copy constructor performs bit-copy - hence no allocation is done for str when actual parameter s is copied to
Class as Type formal parameter a. s.str is merely copied to a.str and both continue to point to the same memory. On exit from
Module Summary strToUpper, a is destructed and a.str is deallocated. Hence in main access to s.str is dangling. Program prints
garbage and / or crashes
• Shallow Copy: With bit-copy, only the pointer is copied - not the pointed object. This is risky
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 19
Copy Assignment Operator
Module 14
Intructors: Abir • We can copy an existing object to another existing object as
Das and
Sourangshu
Bhattacharya
Complex c1 = (4.2, 5.9), c2(5.1, 6.3);
Obj. Lifetime
String
c2 = c1; // c1 becomes { 4.2, 5.9 }
Date
Rect
Copy Constructor This is like normal assignment of built-in types and overwrites the old value with the
Call by Value
Signature
new value
Free Copy & Pitfall
• It is the Copy Assignment that takes an object of the same type and overwrites into
Assignment Op.
Copy Objects an existing one, and returns that object:
Self-Copy
Signature Complex::Complex& operator= (const Complex &);
Free Assignment
Comparison
Class as Type
Module Summary
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 20
Program 14.13: Complex: Copy Assignment
#include <iostream>
Module 14 #include <cmath>
Intructors: Abir
using namespace std;
Das and class Complex { double re_, im_; public:
Sourangshu Complex(double re, double im) : re_(re), im_(im) { cout << "ctor: "; print(); } // Ctor
Bhattacharya
Complex(const Complex& c) : re_(c.re_), im_(c.im_) { cout << "cctor: "; print(); } // CCtor
~Complex() { cout << "dtor: "; print(); } // Dtor
Obj. Lifetime
Complex& operator=(const Complex& c) // Copy Assignment Operator
String
{ re_ = c.re_; im_ = c.im_; cout << "copy: "; print(); return *this; } // Return *this for chaining
Date
Rect
double norm() { return sqrt(re_*re_ + im_*im_); }
void print() { cout << "|" << re_ << "+j" << im_ << "| = " << norm() << endl; } }; // Class Complex
Copy Constructor int main() { Complex c1(4.2, 5.3), c2(7.9, 8.5); Complex c3(c2); // c3 Copy Constructed from c2
Call by Value
c1.print(); c2.print(); c3.print();
Signature
c2 = c1; c2.print(); // Copy Assignment Operator
Free Copy & Pitfall
c1 = c2 = c3; c1.print(); c2.print(); c3.print(); // Copy Assignment Chain
Assignment Op. }
Copy Objects ctor: |4.2+j5.3| = 6.7624 // c1 - ctor copy: |7.9+j8.5| = 11.6043 // c2 <- c3
Self-Copy ctor: |7.9+j8.5| = 11.6043 // c2 - ctor copy: |7.9+j8.5| = 11.6043 // c1 <- c2
Signature cctor: |7.9+j8.5| = 11.6043 // c3 - ctor |7.9+j8.5| = 11.6043 // c1
Free Assignment
|4.2+j5.3| = 6.7624 // c1 |7.9+j8.5| = 11.6043 // c2
Comparison |7.9+j8.5| = 11.6043 // c2 |7.9+j8.5| = 11.6043 // c3
Class as Type
|7.9+j8.5| = 11.6043 // c3 dtor: |7.9+j8.5| = 11.6043 // c3 - dtor
copy: |4.2+j5.3| = 6.7624 // c2 <- c1 dtor: |7.9+j8.5| = 11.6043 // c2 - dtor
Module Summary |4.2+j5.3| = 6.7624 // c2 dtor: |7.9+j8.5| = 11.6043 // c1 - dtor
• Copy assignment operator should return the object to make chain assignments possible
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 21
Program 14.14: String: Copy Assignment
#include <iostream>
Module 14
#include <cstring>
Intructors: Abir #include <cstdlib>
Das and using namespace std;
Sourangshu
Bhattacharya class String { public: char *str_; size_t len_;
String(char *s) : str_(strdup(s)), len_(strlen(str_)) { } // Ctor
Obj. Lifetime String(const String& s) : str_(strdup(s.str_)), len_(s.len_) { } // CCtor
String ~String() { free(str_); } // Dtor
Date String& operator=(const String& s) { // Copy Assignment Operator
Rect free(str_); // Release existing memory
Copy Constructor
str_ = strdup(s.str_); // Perform deep copy
Call by Value
len_ = s.len_; // Copy data member of built-in type
Signature
return *this; // Return object for chain assignment
Free Copy & Pitfall }
void print() { cout << "(" << str_ << ": " << len_ << ")" << endl; }
Assignment Op.
};
Copy Objects
Self-Copy
int main() { String s1 = "Football", s2 = "Cricket"; s1.print(); s2.print(); s2 = s1; s2.print(); }
Signature
---
Free Assignment (Football: 8)
(Cricket: 7)
Comparison
(Football: 8)
Class as Type • In copy assignment operator, str = s.str should not be done for two reasons:
1) Resource held by str will leak
Module Summary
2) Shallow copy will result with its related issues
• What happens if a self-copy s1 = s1 is done?
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 22
Program 14.15: String: Self Copy
#include <iostream>
Module 14
#include <cstring>
Intructors: Abir #include <cstdlib>
Das and using namespace std;
Sourangshu
Bhattacharya class String { public: char *str_; size_t len_;
String(char *s) : str_(strdup(s)), len_(strlen(str_)) { } // Ctor
Obj. Lifetime String(const String& s) : str_(strdup(s.str_)), len_(s.len_) { } // CCtor
String ~String() { free(str_); } // Dtor
Date String& operator=(const String& s) { // Copy Assignment Operator
Rect free(str_); // Release existing memory
str_ = strdup(s.str_); // Perform deep copy
Copy Constructor • For self-copy
Call by Value
len_ = s.len_; // Copy data member of built-in type
Signature
return *this; // Return object for chain assignment
Free Copy & Pitfall }
void print() { cout << "(" << str_ << ": " << len_ << ")" << endl; }
Assignment Op.
};
Copy Objects
Self-Copy
int main() { String s1 = "Football", s2 = "Cricket"; s1.print(); s2.print(); s1 = s1; s1.print(); }
Signature
---
Free Assignment (Football: 8)
(Cricket: 7)
Comparison
(????????: 8) // Garbage is printed. May crash too
Class as Type
• Hence, free(str ) first releases the memory, and then strdup(s.str ) tries to copy from released memory
Module Summary
• This may crash or produce garbage values
• Self-copy must be detected and guarded
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 23
Program 14.16: String: Self Copy: Safe
#include <iostream>
Module 14
#include <cstring>
Intructors: Abir #include <cstdlib>
Das and using namespace std;
Sourangshu
Bhattacharya class String { public: char *str_; size_t len_;
String(char *s) : str_(strdup(s)), len_(strlen(str_)) { } // Ctor
Obj. Lifetime String(const String& s) : str_(strdup(s.str_)), len_(s.len_) { } // CCtor
String ~String() { free(str_); } // Dtor
Date String& operator=(const String& s) { // Copy Assignment Operator
Rect if (this != &s) { // Check if the source and destination are same
Copy Constructor
free(str_);
str_ = strdup(s.str_);
Call by Value • Check for se
Signature
len_ = s.len_;
Free Copy & Pitfall }
return *this;
Assignment Op.
}
Copy Objects
Self-Copy
void print() { cout << "(" << str_ << ": " << len_ << ")" << endl; }
Signature
};
Free Assignment int main() { String s1 = "Football", s2 = "Cricket"; s1.print(); s2.print(); s1 = s1; s1.print(); }
---
Comparison
(Football: 8)
Class as Type (Cricket: 7)
Module Summary (Football: 8)
• In case of self-copy, do nothing
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 24
Signature and Body of Copy Assignment Operator
Module 14 • For class MyClass, typical copy assignment operator will be:
Intructors: Abir MyClass& operator=(const MyClass& s) {
Das and
Sourangshu if (this != &s) { // Check if the source and destination are same
Bhattacharya // Release resources held by *this
// Copy members of s to members of *this
Obj. Lifetime }
String return *this; // Return object for chain assignment
Date }
Rect
Copy Constructor • Signature of a Copy Assignment Operator can be one of:
Call by Value
Signature MyClass& operator=(const MyClass& rhs); // Common. No change in Source
Free Copy & Pitfall
MyClass& operator=(MyClass& rhs); // Occasional. Change in Source
Assignment Op.
Copy Objects • The following Copy Assignment Operators are occasionally used:
Self-Copy
Signature
MyClass& operator=(MyClass rhs);
Free Assignment
const MyClass& operator=(const MyClass& rhs);
Comparison const MyClass& operator=(MyClass& rhs);
Class as Type const MyClass& operator=(MyClass rhs);
MyClass operator=(const MyClass& rhs);
Module Summary
MyClass operator=(MyClass& rhs);
MyClass operator=(MyClass rhs);
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 25
Free Assignment Operator
Module 14
Intructors: Abir • If no copy assignment operator is provided/overloaded by the user, the compiler
Das and
Sourangshu supplies a free one
Bhattacharya
• Free copy assignment operator cannot copy the object with proper values. It performs
Obj. Lifetime
String
Shallow Copy
Date
Rect
• In every class, provide copy assignment operator to adopt to deep copy which is always
Copy Constructor safe
Call by Value
Signature
Free Copy & Pitfall
Assignment Op.
Copy Objects
Self-Copy
Signature
Free Assignment
Comparison
Class as Type
Module Summary
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 26
Comparison of Copy Constructor and Copy Assignment Operator
Module 14
Copy Constructor Copy Assignment Operator
Intructors: Abir
Das and
Sourangshu
Bhattacharya • An overloaded constructor • An operator overloading
Obj. Lifetime • Initializes a new object with an existing • Assigns the value of one existing object
String
Date
object to another existing object
Rect • Used when a new object is created with • Used when we want to assign existing
Copy Constructor some existing object object to another object
Call by Value
Signature • Needed to support call-by-value and
Free Copy & Pitfall
return-by-value
Assignment Op.
Copy Objects
• Newly created object use new memory • Memory location of destination object
Self-Copy location is reused with pointer variables being re-
Signature
Free Assignment leased and reallocated
Comparison • Care is needed for self-copy
Class as Type • If not defined in the class, the compiler • If not overloaded, the compiler provides
Module Summary provides one with bitwise copy one with bitwise copy
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 27
Class as a Data-type
Module 14 • We add the copy construction and assignment to a class being a composite data type in C++
Intructors: Abir
Das and
Sourangshu // declare i to be of int type // declare c to be of Complex type
Bhattacharya int i; Complex c;
Obj. Lifetime // initialise i // initialise the real and imaginary components of c
String int i = 5; Complex c = (4, 5); // Ctor
Date
int j = i; Complex c1 = c; // CCtor
Rect
int k(j); Complex c2(c1); // CCtor
Copy Constructor
Call by Value // print i // print the real and imaginary components of c
Signature cout << i; cout << c.re << c.im;
Free Copy & Pitfall OR c.print(); // Method Complex::print() defined for printing
Assignment Op. OR cout << c; // operator<<() overloaded for printing
Copy Objects
Self-Copy // add two ints // add two Complex objects
Signature int i = 5, j = 6; Complex c1 = (4, 5), c2 = (4, 6);
Free Assignment i+j; c1.add(c2); // Method Complex::add() defined to add
Comparison OR c1+c2; // operator+() overloaded to add
Class as Type // copy value of i to j // copy value of one Complex object to another
Module Summary int i = 5, j; Complex c1 = (4, 5), c2 = (4, 6);
j = i; c2 = c1; // c2.re <- c1.re and c2.im <- c1.im by copy assignment
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 28
Module Summary
Module 14
Intructors: Abir • Copy Constructors
Das and
Sourangshu
Bhattacharya
◦ A new object is created
◦ The new object is initialized with the value of data members of another object
Obj. Lifetime
String • Copy Assignment Operator
Date
Rect
◦ An object is already existing (and initialized)
Copy Constructor ◦ The members of the existing object are replaced by values of data members of
Call by Value
Signature
another object
Free Copy & Pitfall ◦ Care is needed for self-copy
Assignment Op.
Copy Objects
• Deep and Shallow Copy for Pointer Members
Self-Copy
Signature
◦ Deep copy allocates new space for the contents and copies the pointed data
Free Assignment ◦ Shallow copy merely copies the pointer value – hence, the new copy and the original
Comparison
pointer continue to point to the same data
Class as Type
Module Summary
CS20202: Software Engineering Intructors: Abir Das and Sourangshu Bhattacharya 29