4.8 Copy Constructors




A class can define how its instances are copied through a special constructor called a "copy constructor". The sense of a copy constructor is that it uses an existing object's attributes (data, state, instance variables) to initialize the new object being created.

A copy constructor is a constructor that has a certain, fixed signature. Like other constructors, the copy constructor cannot be called explicitly. The copy constructor is called implicitly when the C++ language dictates that a copy of an object is required.

If a class does not define a copy constructor, a default copy constructor is used. The default copy constructor performs a byte-by-byte copy from the existing object to the new object. In many simple cases the default copy constructor is adequate. However, as the examples below will show, the default copy constructor is often inadequate. In addition, many consider it good style to always define a copy constructor even if it has the same effect as the default copy constructor.

An object will be copied in two circumstances: (1) when a new object is declared and initialized using an existing object, and (2) when an object is passed as a parameter "by copy". These two circumstances are illustrated in the following code:


	Frame  window1(Location(100, 100), Shape (200,300));
	Frame  window2(Location(100, 100), Shape (200,300));
	Message originalMsg("Hello World");

	Message copiedMsg( originalMsg );	// copy constructor used

	...

	window1.Display(originalMsg);		// copy constructor used
	window2.Display(copiedMsg);		// copy constructor used

In this example, the declaration of the Message object "copiedMsg" uses the copy constructor defined for the Message class. The initial values for the data members of the object "copiedMsg" are initialized based on the data members in the object "originalMsg". The call on the Display method passes a Message object by copy. On each of the two calls to Display, the copy constructor for the Message class will be used to generate a copy of the object being passed as an argument.

The example above will be used to show why the default copy constructor is inadequate for Message class objects. Recall that the implementation of the Message class uses a character string (a char*) to hold a pointer that defines the text to be displayed on the screen. To avoid a memory leak, the destructor for the Message class appropriately deletes this character string when the Message object is deleted. The relevant portion of the Message class is:

	class Message {
	private:
	  char*    message;
	  ...
	public:
	  ...
	  ~Message();                            // delete message 
	};

	Message::~Message(){ delete message; }

When a copy is made of a Message object using the default copy constructor, the pointer (message), but not the string to which it points, is copied. This default copying results in the following situation:

Shared Data

Both objects point to the same place in memory. As long as both objects exists, there is no problem. However, if either of the objects is deleted the character string will be deallocated by that object's destructor, leaving the other object pointing to a place in memory that has an undefined content.

The errors that can result from this situation are as follows. In the case above, the first call on the Display method will create a copy of the originalMsg object. This copy will be destructed at the end of the Display method. Thus, both the originalMsg object and the copiedMsg object are pointing to memory with undefined content. When the second call on Display is made one of three things can happen:

  1. the correct string ("Hello World") is displayed in window2; this will occur if the deleted memory is not immediately recycled or, even if recycled, is not immediately changed. The memory may remain the same for some indeterminate period.
  2. an incorrect (garbage) string will be displayed in window2; this will occur if the deleted memory is recycled and changed to something that looks like a character string (although it may not be).
  3. the program will crash; this will occur if the deleted memory has been recycled and changed so that the system cannot interpret it as a character string.

The copy constructor for the Message class illustrates how a copy constructor is defined:


	class Message {
          private:
	   ...
	  public:

		Message(const Message& other);    // copy constructor

	};

The copy constructor has a single argument - a reference to an object of the same class (i.e., the Message class in this case) that is not changed (i.e., it is declared "const"). The input argument ("other") is declared "const" because the copy constructor does not need to change the argument to initialize the new object.

The code for the Message class' copy constructor is as follows:


	Message::Message(const Message& other) {
		message = copystring(other.message); }

This constructor copies the character string that is defined by the other object. In this case, when each object is destructed only its own copy of the character string will be deleted. No memory leaks are created and no "dangling" pointers occur.


Next Stop


Exercises

  1. Add a copy constructor to the Shape class. Test your code with a simple main program.

  2. Add a copy constructor to the Location class. Test your code with a simple main program.

Last Updated: February 26, 1996 / kafura@cs.vt.edu