The copy constructor is a special kind of constructor which creates a new object which is a copy of an existing one, and does it efficiently.
(拷贝构造函数是一种特别的构造函数,用于复制已经存在的对象到新生成的对象,这是一种高效的方式。)
Here below is a simple declaration of a copy constructor:
(下面是拷贝构造函数简单的声明:)
class string
{
string();
~string();
string(const string &s)
{
copy(s.m_str);
}
};
Now you can use it as follow:
(现在,你可以按照下面的方式使用拷贝构造函数)
// create an object which is copy of another object
string s1("hello");
string s2(s1); // copy constructor activated
// create an object as a copy of a temporary object
string s3(string("abc"));
string s4 = s1;
// object s4 does not activate the constructor, but its copy constructor to make only a copy of s1, rather than building a new object
you have to use const in the argument at the copy constructor to create an object as a copy of a temporary object: e.g. string(const string &s).
(在拷贝构造函数中,你必须使用const关键字作为参数)
to make things clear, you can create a new object as copy of a different object without using a copy constructor, like this:
(进一步将,你可以不使用拷贝构造函数来复制一个对象到另一个新对象)
string s4;
s4.set(s1);
this is an example of inefficient code. Since s4 first call its constructor to build a new object and then it make a bit-wise copy of s1. The whole process of calling the constructor to build an object which next is being rewritten, is wasteful, takes time and resources. Copy constructor allow you to prevents this inefficiency.
(这是一个效率低下的代码。由于s4首先需要调用自己的构造函数创建一个对象,然后把s1按位拷贝到s4.整个处理的过程是调用构造函数创建一个对象,然后重写这个对象,这是一种浪费时间、浪费资源的做法。拷贝构造函数可以帮你预防这种低效。)
Default copy constructor
If the programmer did not declared the copy constructor for a class, the compiler will add its own default copy constructor for the objects derived from that class.
Default copy constructor does a very simple operation, they will do a bit-wise (member-wise) copy of an object, which means that the object will be copied bit by bit.
(如果程序员没有提供拷贝构造函数,那么编译器会自动生成默认的拷贝构造函数。默认的拷贝构造函数会执行简单的操作,即按位拷贝对象。)
string s1("hello");
string s2(s1);
string s2 = s1; //the same as above
There is a danger in copying an object bit by bit, if the object contains pointers since the pointer address will be copied in the process resulting in two different objects that share the same memory buffer. You can imagine what will happen when two copies of an object calls their destructors one after the other. The first object that call its destructor will have no problems, since it will try to deallocate the pointer and succeed, but the second objects destructor try to deallocate a pointer which does not exist anymore and the whole application crashes!
(如果对象中含有指针,那么按位拷贝就是很危险的。是因为两个对象的指针会指向同一片内存。你可以想象一下,当两个拷贝的对象去调用他们的析构函数的时候会发生什么。首先调用析构函数的对象不会有任何问题,但是第二个对象的析构函数就会尝试去释放不存在的资源,这样会道是整个应用程序的崩溃。)
For a situation where an object contain pointers we have to write our own copy constructor that will prevent situations like those mentioned above.
(对于类对象包含指针的情况下,我们要编写自己的拷贝构造函数,这样可以防止上述情况的发生)
There are 3 situations in which the copy constructor is called:
When we make copy of an object.
When we pass an object as an argument by value to a method.
When we return an object from a method by value.
(有三种情况下拷贝构造函数会被调用:
当拷贝对象时候
当按值传递一个对象作为方法参数的时候
当按值返回一个对象时候)
we saw the first scenario above, and we will now look at the other two scenarios.
(我们已经在上述中展示了第一种情况,下面我将会带领大家看看后两种情况。)
When objects are passed to a function as arguments by value, a bit wise copy of the object will be passed to the function and placed on the stack, therefor the constructor of the object will not be called. It make sense if you think of it, you want to pass an object in a certain state containing the data you need for the function to process, if you wanted it in the initialization state with its default data, you could just create it inside the function instead of passing it as an argument.
(当一个对象按值传递给一个函数作为参数,对象将会按位拷贝到函数中,并且置于栈中。所以此时对象的构造函数不会被 调用。当你希望传递的对象有初始化值的时候,你应该在函数的内部创建它来代替作为参数传给函数。)
When the function end, the constructor of the object will be called. This is also make sense since the object was passed by value and its data will not be needed outside the function scope.
(当函数结束的时候,对象的析构函数就会被调用。这似乎也很合理,因为我们是按值进行的传递,在超出函数的作用范围后,我们不需要该变量。)
No pay attention to this, the situation in which only the object destructor is called can make great deal of troubles. Think what will happen when the object holds a pointer to some address in the memory. When this object is passed as argument to a function the pointer in the new temporary created object will hold the same address as the original object, since its a bit wise copy. When the function ends, the destructor will free the address pointed by the pointer. From this point, if the destructor of the original object will be called it will try to free an address which already free, and we all know what it the consequences of that …
(没有注意到的是,对象的析构函数调用后会引起很大的麻烦。想想如果对象中包含了指向地址的指针会发生什么。由于是按位拷贝,当传递一个对象给函数作为参数的时候,新产生的临时性的对象的指针将会与 原来对象的指针指向同一个地址。当函数结束的时候,析构函数将会释放指针指向的地址。从这点来看,如果原来对象 的析构函数被调用,它将会释放已经释放的内存,我们都知道会发生什么样的后果。)
lets look at an example to make things clear.
Here we define string class which holds a char* pointer:
(让我们看看下面的例子,以便更加清楚 这里我们定义的string类包含了一个char*指针)
class string
{
// constructor
string(char* aStr)
{
str = new char[sizeof(aStr)];
strcpy (str,aStr);
}
// destructor
~string()
{
del str;
}
char *getChars(){ return str; }
char* str;
};
now we will write a function that receive a string object as an argument.
(现在,我们写一个接受string对象作为参数的函数:)
void function (string str)
{
// do somthing
}
Lets look what will happen when we call this function :
(当我们调用这个函数的时候看看会发生什么:)
void main ()
{
string str("hello");
function(str);
function(str); // program crush
}
The first time we call function(str) everything works properly, when the function ends, the input argument on the stack is destroyed, and its destructor will be called, and delete the pointer.
The second time we call the function , everything still works properly, but when the function ends, the constructor will try now to free the address pointed by str, and crash.
(第一次调用function函数的时候一切都很正常,当函数结束的时候,栈上的参数会被销毁,所以对象的析构函数会被调用,并且删除指针指向的内容。 第二次调用function函数的时候,一切似乎同样正常。但当函数结束的时候,析构函数会试图去释放指针str指向地址的内容,这样就会造成奔溃。)
The copy constructor come to help us solve this kind of problems. Here is a solution:
(拷贝构造函数会帮助我们解决这样的问题,下面是解决方法:)
class string
{
// constructor
string(char* aStr)
{
str = new char[sizeof(aStr)];
strcpy (str,aStr);
}
string(string &strObj)
{
tmpStr = strObj.getChars();
str = new tmpStr[sizeof(tmpStr)];
strcpy (str,tmpStr);
}
// destructor
~string()
{
del str;
}
char* str;
};
The same way we can handle the third scenario where a method returns an object:
(同样的方式,我们演示了上诉的第三种情况,即返回一个对象)
class string
{
public:
string(char* aStr)
{
str = new char[ strlen(aStr) + 1 ];
strcpy (str,aStr);
}
string(string &strObj)
{
str = new char[ strObj.str ];
strcpy( str, strObj.str );
}
string &string::operator=(const string &s)
{
string temp( s );
std::swap( temp.str, str );
return *this;
}
// destructor
~string()
{
delete[] str;
}
private:
char* str;
};