10.5.4. The Copy Constructor

Time: 00:04:07 | Download: Large, Large (CC), Small | Streaming | Slides (PDF)
Review

Fully understanding the copy constructor may require reviewing previously introduced concepts:

Three common operations cause a C++ program to copy an object:

The operations are necessary and frequently occur in C++ programs, so the compiler automatically provides two object-copying functions: an assignment operator and a copy constructor. (We'll see how to implement the assignment operator as a function in the next chapter.) The compiler-created functions are sufficient in many but not all cases. Programmers must override both functions when the compiler-created versions are insufficient. The following guidelines, based on our earlier definitions of "simple" and "complex" classes, clarify when the overrides are necessary:

Simple classes (without pointer members)
The compiler-created byte-wise copy constructor and assignment operator are sufficient.
Complex classes (with one or more pointer members
Programmers must override the copy constructor and the assignment operator.

Operations That Copy Objects

To understand which functions we need to override, we must first understand which C++ statements copy an object and which functions perform the operation. Spotting an object copy is generally easy, but identifying the copying function is not, as the following figure demonstrates.

Functions Copying ObjectsCompiler-Created Copy Functions
void person::f1(person p3)	// pass by value
{
	...
}

person person::f2()		// return by value
{
	...
	return person(...);
}

person person::f3()		// return by value
{
	person temp(...);
		...
	return temp;
}
person::person(person& p)		// copy constructor
{
	...
}




person& person::operator=(person& p)	// assignment operator
{
	...
	return *this;
}
 
 
 
 
Statements Triggering Object CopiesComments
person p1(p);
Copy Constructor: Creates a new person object, p1, by copying an existing object, p.
f1(p);
Copy Constructor: The copy constructor creates the parameter object, p3 (see f1 above left), by copying the argument object, p.
person p2 = p;
Copy Constructor: The assignment operator, =, notwithstanding, the statement creates a new object, p2, and the copy constructor builds it by copying an existing object, p.
p0 = p;
Assignment Operator: The assignment operator copies an existing object to another existing object. Any data stored in p0 is overwritten.
p0 = f2();
Assignment Operator: f2 returns an object by value, and the assignment operator copies it to an existing variable, p0.
person p4 = f2();
No copy!: When a program defines a new object, e.g. p4, and creates a new object in the return statement (see f2), C++ builds the object in function-call scope - directly in p4 for this example.
person p5 = f3();
Copy Constructor: Unlike f2, f3 creates and returns a temporary variable, which the copy constructor copies to p5. f3 triggers two more function calls than f2: one constructor and one destructor.
Operations copying objects. The C++ compiler automatically creates two functions, a copy constructor and an assignment operator, to copy objects in a program. The figure illustrates three functions that utilize one of the copy functions and the statements calling them, but sometimes it's unclear which copy function a statement calls. The ellipses represent detail removed for simplicity. See person.cpp at the bottom of the page.

The Compiler-Created Copy Constructor

The compiler-created copy constructor is necessarily simple and general. Our first task is to understand what the copy constructor does and then explore how the compiler might implement it.

The person UML class diagram (the diagram only shows attributes):

person
------------------
-name : string
-weight : int
-height : double
string	name;
int	weight;
double	height;
Copying a simple object creates a new object identical to the original.
(a)(b)
person::person(person& p)
{
	name = p.name;
	weight = p.weight;
	height = p.height;
}
person::person(person& p)
{
	memcpy(this, &p, sizeof(person));
}
(c)(d)
Copying a "simple" object with the compiler-created copy constructor. A program copies or duplicates an object by allocating memory and calling a copy constructor to initialize the new object's member variables. The compiler-created copy constructor copies the values saved in the exiting object's member variables to the corresponding members of the new object.

Caution

A fundamental object-oriented principle is that a class hides its implementation - the data it stores and how its functions manage it - from programmers. Consequently, we can't know how a specific C++ compiler implements its string class, but it likely points to an array allocated on the heap. The string class overloads the assignment operator to handle this implementation. But memcpy is a low-level operation that is "unaware" of pointers and is unable to duplicate heap data. See Figure 4(b) below.

Overriding The Copy Constructor

The previous discussion of object ownership in aggregation relationships alluded to situations where two or more objects in a program share another object. While programmers can establish the sharing while copying a "complex" object, we generally intend the copy operation to produce two distinct and independent objects. Independence implies that once the copy operations are complete, we can change either object without affecting the other. The compiler-created copy constructor produces independent objects when the original object is "simple," but producing independent "complex" objects requires programmers to override the compiler-created copy constructor.

string*  name;
int	 weight;
double	 height;
Copying a complex object duplicates the whole object but not the aggregated parts. The new and original whole objects share (point to) the same aggregated parts.
(a)(b)
Copying a "complex" object with the compiler-created copy constructor. This example illustrates what happens when a program copies a "complex" object using the compiler-created copy constructor.
  1. This version changes the name member variable to a pointer, changing the person class from "simple" to "complex."
  2. The compiler-created copy constructor accurately copies the original object's member variables, including its pointers. But the value saved in a pointer is an address. Consequently, the original and the copied objects save the same address in their respective pointer members - they point to the same part object - and are not independent. Changing the name in either object also changes the name in the other.
The compiler-created assignment operator behaves similarly, as seen in the next chapter.

Steps for overriding the copy constructor

  1. The function's name, like all constructors, is the name of the class.
  2. The function has exactly one parameter, which is an instance of the class, passed by reference.
  3. Copy each non-pointer member variable by simple assignment or by using memcpy.
  4. Copy each pointer member by allocating new memory with the new operator and copying the saved data from the original to the new object.
person::person(person& p)
{
    name = new string(*p.name);
    weight = p.weight;
    height = p.height;
}
A correct overridden copy constructor will copy the original whole object and all aggregated parts.
(a)
person::person(person& p)
{
    memcpy(this, &p, sizeof(person));
    name = new string(*p.name);
}
(b)(c)
Overriding the copy constructor.
  1. This version copies the original object member-by-member, a common approach for overriding a copy constructor. It's common to dereference any pointers before duplicating the pointer members, but this depends on the other constructors in the class.
  2. When there are many non-pointer members, it is more efficient to do a byte-wise copy with memcpy first and then copy each pointer member individually. Note that the order of operations is important: memcpy must be done first, followed by the individual pointer copies. (Can you figure out why?)
  3. When the copy operation is complete, the pointer members are also correctly copied, resulting in distinct and independent objects that do not share data.

If the original object has an embedded or composed part, the program must initialize it. Initialization is automatic if the object's class has a default constructor. Otherwise, the overridden copy function must explicitly call a general constructor. The Actor 3 example in the next section demonstrates this process.

Downloadable Code Examples

Complete versions of both programs described above are available for download and study (formatted with tabs set at eight spaces):

 ViewDownload
No pointer member variables person.cpp person.cpp
One pointer member variable person_pointer.cpp person_pointer.cpp