C++的拷贝构造函数

From Wikipedia, the free encyclopedia

Jump to: navigation, search

A copy constructor is a special constructor in the C++ programming language used to create a new object as a copy of an existing object. This constructor takes a single argument: a reference to the object to be copied.

Normally the compiler automatically creates a copy constructor for each class (known as an implicit copy constructor) but for special cases the programmer creates the copy constructor, known as an explicit copy constructor. In such cases, the compiler doesn't create one.

A 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

[hide]
<script type="text/javascript"> // </script>

[edit] Definition

Copying of objects is achieved by the use of a copy constructor and a copy 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(X const&);
X(X&);
X(X const volatile&);
X(X volatile&);
X(X const&, int = 10);
X(X const&, double = 1.0, int = 40);

The first one should be used if a good reason to use one of the others does not exist. 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 if you have X(X const&) but not valid if you have X(X&)
// because the second can't hold a temporary

Another difference between them is the obvious:

X const a;
X b = a; // valid if you have X(X const&) but not valid if you have X(X&)
// 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 is defined
// since you're passing in a reference

The following are invalid copy constructors (or regular constructors):

X(X);
X(X const);

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

There are four instances when a copy constructor is called:

  1. When an object is returned by value
  2. When an object is passed (into a function) by value as an argument
  3. When an object is constructed based on another object (of the same class)
  4. When the compiler generates a temporary object (as in 1 and 2 above; as in explicit casting, etc...)

[edit] Operation

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

  • Explicit assignment in an expression
  • Initialization

[edit] Explicit assignment in an expression

Object A;
Object B;
A = B; // translates as Object::operator=(const Object&), thus A.operator=(B) is called

[edit] 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&)

b. Through function arguments

type function (Object a);

c. Through function return value

Object a = function();

The copy constructor is used only in latter case (initializations) and doesn't 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.[2]

By using an explicit copy constructor the programmer can define the behavior to be performed when an object is copied.

[edit] Examples

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

[edit] Implicit copy constructor

Let us consider the following example.

#include <iostream>

class Person
{
public:

int age;

Person(int age)
: 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(Person const& copy)
: age(copy.age) {}

So, when do we really need an explicit copy constructor? The next section will explore that question.

[edit] Explicit copy constructor

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

#include <iostream>

class Array
{
public:
int size;
int* data;

Array(int size)
: size(size), 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 didn't specify a copy constructor, the compiler generated one for us. The generated constructor would look something like:

Array(Array const& 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 famous segmentation fault.

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

Array(Array const& 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).

[edit] Copy constructors and templates

Contrary to expectations, a template copy constructor is not an explicit 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.) An explicit, non-template, copy constructor must also be provided for construction of Array from Array.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值