Copy constructor

Copy constructor

A copy constructor is a special constructor in the C++ programming language creating a new object as a copy of an existing object. The first argument of such a constructor is a reference to an object of the same type as is being constructed (const or non-const), which might be followed by parameters of any type (all having default values).

Normally the compiler automatically creates a copy constructor for each class (known as a default copy constructor) but for special cases the programmer creates the copy constructor, known as a user-defined copy constructor. In such cases, the compiler does not create one. Hence, there is always one copy constructor that is either defined by the user or by the system.

A user-defined copy constructor is generally needed when an object owns pointers or non-shareable references, such as to a file, in which case a destructor and an assignment operator should also be written (see Rule of three).

Contents

Definition

Copying of objects is achieved by the use of a copy constructor and an assignment operator. A copy constructor has as its first parameter a (possibly const or volatile) reference to its own class type. It can have more arguments, but the rest must have default values associated with them.[1] The following would be valid copy constructors for class X:

X(const X& copy_from_me);
X(X& copy_from_me);
X(const volatile X& copy_from_me);
X(volatile X& copy_from_me);
X(const X& copy_from_me, int = 10);
X(const X& copy_from_me, double = 1.0, int = 40);

The first one should be used unless there is a good reason to use one of the others. One of the differences between the first and the second is that temporaries can be copied with the first. For example:

X a = X();     // valid given X(const X& copy_from_me) but not valid given X(X& copy_from_me)
               // because the second wants a non-const X&
               // to create a, the compiler first creates a temporary by invoking the default constructor
               // of X, then uses the copy constructor to initialize a as a copy of that temporary. 
               // However, for some compilers both the first and the second actually work.

Another difference between them is the obvious:

X const a;
X b = a;       // valid given X(const X& copy_from_me) but not valid given X(X& copy_from_me)
               // because the second wants a non-const X&

The X& form of the copy constructor is used when it is necessary to modify the copied object. This is very rare but it can be seen used in the standard library's std::auto_ptr. A reference must be provided:

X a;
X b = a;       // valid if any of the copy constructors are defined
               // since a reference is being passed.

The following are invalid copy constructors (Reason - copy_from_me is not passed as reference) :

X(X copy_from_me);
X(const X copy_from_me);

because the call to those constructors would require a copy as well, which would result in an infinitely recursive call.

The following cases may result in a call to a copy constructor:

  1. When an object is returned by value
  2. When an object is passed (to a function) by value as an argument
  3. When an object is thrown
  4. When an object is caught
  5. When an object is placed in a brace-enclosed initializer list

These cases are collectively called copy-initialization and are equivalent to:[2] T x = a;

It is however, not guaranteed that a copy constructor will be called in these cases, because the C++ Standard allows the compiler to optimize the copy away in certain cases, one example being the return value optimization (sometimes referred to as RVO).

Operation

An object can be assigned value using one of the two techniques:

  • Explicit assignment in an expression
  • Initialization

Explicit assignment in an expression

Object a;
Object b;
a = b;       // translates as Object::operator=(const Object&), thus A.operator=(B) is called 
             // (invoke simple copy, not copy constructor!)

Initialization

An object can be initialized by any one of the following ways.

a. Through declaration

Object B = A; // translates as Object::Object(const Object&) (invoke copy constructor)

b. Through function arguments

type function(Object a);

c. Through function return value

Object a = function();

The copy constructor is used only for initializations, and does not apply to assignments where the assignment operator is used instead.

The implicit copy constructor of a class calls base copy constructors and copies its members by means appropriate to their type. If it is a class type, the copy constructor is called. If it is a scalar type, the built-in assignment operator is used. Finally, if it is an array, each element is copied in the manner appropriate to its type.[3]

By using a user-defined copy constructor the programmer can define the behavior to be performed when an object is copied.

Examples

These examples illustrate how copy constructors work and why they are required sometimes.

Implicit copy constructor

Let us consider the following example.

//copy constructor
#include <iostream>
 
class Person {
public:
    int age;
 
    explicit Person(int age) 
    {
        this->age = age;
    }
};
 
int main() 
{
    Person timmy(10);
    Person sally(15);
 
    Person timmy_clone = timmy;
    std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl;
    timmy.age = 23;
    std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl;
}

Output

10 15 10
23 15 10

As expected, timmy has been copied to the new object, timmy_clone. While timmy's age was changed, timmy_clone's age remained the same. This is because they are totally different objects.

The compiler has generated a copy constructor for us, and it could be written like this:

Person(const Person& copy) 
{
    this->age = copy.age;
}

So, when do we really need a user-defined copy constructor? The next section will explore that question.

User-defined copy constructor

Now, consider a very simple dynamic array class like the following:

#include <iostream>
 
class Array {
public:
    int size;
    int* data;
 
    explicit Array(int size) 
    {
        this->size = size;
        this->data = new int[size];
    }
 
    ~Array() 
    {
        delete[] data;
    }
};
 
int main() 
{
    Array first(20);
    first.data[0] = 25;
 
    {
        Array copy = first;
        std::cout << first.data[0] << " " << copy.data[0] << std::endl;
    }    // (1)
 
    first.data[0] = 10;    // (2)
}

Output

25 25
Segmentation fault

Since we did not specify a copy constructor, the compiler generated one for us. The generated constructor would look something like:

Array(const Array& copy)
  : size(copy.size), data(copy.data) {}

The problem with this constructor is that it performs a shallow copy of the data pointer. It only copies the address of the original data member; this means they both share a pointer to the same chunk of memory, which is not what we want. When the program reaches line (1), copy's destructor gets called (because objects on the stack are destroyed automatically when their scope ends). Array's destructor deletes the data array of the original, therefore when it deleted copy's data, because they share the same pointer, it also deleted first's data. Line (2) now accesses invalid data and writes to it! This produces the infamous segmentation fault.

If we write our own copy constructor that performs a deep copy then this problem goes away.

Array(const Array& copy)
    : size(copy.size), data(new int[copy.size]) 
{
    std::copy(copy.data, copy.data + copy.size, data);    // #include <algorithm> for std::copy
}

Here, we are creating a new int array and copying the contents to it. Now, copy's destructor only deletes its data and not first's data as well. Line (2) will not produce a segmentation fault anymore.

Instead of doing a deep copy right away, there are some optimization strategies that can be used. These allow you to safely share the same data between several objects, thus saving space. The copy on write strategy makes a copy of the data only when it is written to. Reference counting keeps the count of how many objects are referencing the data, and will delete it only when this count reaches zero (e.g. boost::shared_ptr).

Copy constructors and templates

Contrary to expectations, a template copy constructor is not a user-defined copy constructor. Thus it is not enough to just have:

template <typename A> Array::Array(const A& copy)
    : size(copy.size()), data(new int[copy.size()]) 
{
    std::copy(copy.begin(), copy.end(), data);
}

(Note that the type of A can be Array.) A user-defined, non-template copy constructor must also be provided for construction of Array from Array.

Bitwise Copy Constructor

A bitwise copy constructor performs a simple variable - by - variable assignment copy of all the components of an object. The bitwise copy constructor is so named as it merely copies bit - by - bit from the original object to the new object.

Bitwise Copy Constructor.jpg

It is clear from the above figure that in a bitwise copy constructor the original as well as the new object point to the same variable. Hence, the value of the variable is changed for both the objects even if it is changed by only one object. [4]

Logical Copy Constructor

A logical copy constructor makes a true copy of the structure as well as its dynamic structures. Logical copy constructor come into picture mainly when there are pointers or complex objects, within the object.

Logical Copy Constructor.jpg

It can be seen from the above figure that in a logical copy constructor, a new dynamic - member variable is created for the pointer along with copying the values. [5]

Explicit copy constructor

An explicit copy constructor is one that is declared explicit by using the explicit keyword. For example:

explicit X(const X& copy_from_me);

It is used to prevent copying of objects at function calls or with the copy-initialization syntax.

See also

Notes

  1. ^ INCITS ISO IEC 14882-2003 12.8.2. [1]
  2. ^ ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §8.5 Initializers [dcl.init] para. 12
  3. ^ INCITS ISO IEC 14882-2003 12.8.8. [2]
  4. ^ Computer Science A Structured Approach Using C++ by Behrouz A. Forouzan and Richard F. Gilberg,figure 10-9, page 507
  5. ^ Computer Science A Structured Approach Using C++ by Behrouz A. Forouzan and Richard F. Gilberg,figure 10-9, page 507

Wikimedia Foundation. 2010.

Игры ⚽ Нужен реферат?

Look at other dictionaries:

  • copy constructor — noun A constructor that takes an existing instance as a parameter and copies its values …   Wiktionary

  • Constructor (object-oriented programming) — Programming language comparisons General comparison Basic syntax Basic instructions Arrays Associative arrays String operations …   Wikipedia

  • Copy elision — In C++ computer programming, copy elision refers to a compiler optimization technique that eliminates unnecessary copying of objects. The C++ language standard generally allows implementations to perform any optimization, as long as the resulting …   Wikipedia

  • Constructor (computer science) — In object oriented programming, a constructor (sometimes shortened to ctor) in a class is a special block of statements called when an object is created, either when it is declared (statically constructed on the stack, possible in C++ but not in… …   Wikipedia

  • Constructor — Als Konstruktoren und Destruktoren werden in der Programmierung spezielle Prozeduren bezeichnet, die beim Erzeugen und Zerstören von Objekten oder Variablen aufgerufen werden. Konstruktoren bringen die Objekte in einen definierten Anfangszustand …   Deutsch Wikipedia

  • Object copy — An object copy is an action in computing where a data object has its attributes copied to another object of the same data type. An object is a composite data type in object oriented programming languages. The copying of data is one of the most… …   Wikipedia

  • Von Neumann universal constructor — John von Neumann s Universal Constructor is a self replicating machine in a cellular automata environment. It was designed in the 1940s, without the use of a computer. The fundamental details of the machine were published in von Neumann s book… …   Wikipedia

  • C++0x — is the planned new standard for the C++ programming language. It is intended to replace the existing C++ standard, ISO/IEC 14882, which was published in 1998 and updated in 2003. These predecessors are informally known as C++98 and C++03. The new …   Wikipedia

  • C++11 — C++11, also formerly known as C++0x,[1] is the name of the most recent iteration of the C++ programming language, replacing C++TR1, approved by the ISO as of 12 August 2011.[2] The name is derived from the tradition of naming language versions by …   Wikipedia

  • Object lifetime — In computer science, the object lifetime (or life cycle) of an object in object oriented programming is the time between an object s creation (also known as instantiation or construction) till the object is no longer used, and is destructed or… …   Wikipedia

Share the article and excerpts

Direct link
Do a right-click on the link above
and select “Copy Link”