Copy Constructor in C++
What is a copy constructor? (什么是复制构造函数?)
A copy constructor is a member function that initializes an object using another object of the same class. A copy constructor has the following general function prototype:
复制构造函数是一个成员函数,它使用同一类的另一个对象来初始化一个对象。复制构造函数具有以下通用函数原型:
ClassName (const ClassName &old_obj);
Following is a simple example of copy constructor.
#include<iostream>
using namespace std;
class Point
{
private:
int x, y;
public:
Point(int x1, int y1) { x = x1; y = y1; }
// Copy constructor
Point(const Point &p1) {x = p1.x; y = p1.y; }
int getX() { return x; }
int getY() { return y; }
};
int main()
{
Point p1(10, 15); // Normal constructor is called here
Point p2 = p1; // Copy constructor is called here
// Let us access values assigned by constructors
cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();
cout << "\np2.x = " << p2.getX() << ", p2.y = " << p2.getY();
return 0;
}
Output:
p1.x = 10, p1.y = 15
p2.x = 10, p2.y = 15
When is copy constructor called? (什么时候复制构造函数被调用?)
In C++, a Copy Constructor may be called in the following cases:
- When an object of the class is returned by value.
- When an object of the class is passed (to a function) by value as an argument.
- When an object is constructed based on another object of the same class.
- When the compiler generates a temporary object.
It is, however, not guaranteed that a copy constructor will be called in all these cases, because the C++ Standard allows the compiler to optimize the copy away in certain cases, one example is the return value optimization (sometimes referred to as RVO).
在 C++ 中,在以下情况下可能会调用 Copy Constructor:
- 当类的对象按值return时。
- 当类的对象按值作为参数传递(给函数)时。
- 当一个对象是基于同一个类的另一个对象构造时。
- 当编译器生成临时对象时。
但是,不能保证在所有这些情况下都会调用复制构造函数,因为 C++ 标准允许编译器在某些情况下优化复制,例如返回值优化(有时称为 RVO)。
When is a user-defined copy constructor needed? (什么时候需要用户自定义复制构造函数)
If we don’t define our own copy constructor, the C++ compiler creates a default copy constructor for each class which does a member-wise copy between objects. The compiler created copy constructor works fine in general. We need to define our own copy constructor only if an object has pointers or any runtime allocation of the resource like filehandle, a network connection…etc.
The default constructor does only shallow copy.
如果我们不定义自己的复制构造函数,C++ 编译器会为每个类创建一个默认的复制构造函数,它会在对象之间进行成员方式的复制。编译器创建的复制构造函数通常可以正常工作。仅当对象具有指针或资源的任何运行时分配(如文件句柄、网络连接等)时,我们才需要定义自己的复制构造函数。
默认构造函数只做浅拷贝。
Deep copy is possible only with user defined copy constructor. In user defined copy constructor, we make sure that pointers (or references) of copied object point to new memory locations.
只有使用用户定义的复制构造函数才能进行深度复制。在用户定义的复制构造函数中,我们确保复制对象的指针(或引用)指向新的内存位置。
Copy constructor vs Assignment Operator(复制构造函数 vs 赋值运算符 )
以下两个语句中,哪一个调用了复制构造函数,哪一个调用了赋值运算符?
MyClass t1, t2;
MyClass t3 = t1; // ----> (1)
t2 = t1; // -----> (2)
Copy constructor is called when a new object is created from an existing object, as a copy of the existing object. Assignment operator is called when an already initialized object is assigned a new value from another existing object. In the above example (1) calls copy constructor and (2) calls assignment operator.
当从现有对象创建新对象时调用复制构造函数,作为现有对象的副本。当已经初始化的对象从另一个现有对象分配新值时,将调用赋值运算符。在上面的例子中 (1) 调用复制构造函数, (2) 调用赋值运算符。
Write an example class where copy constructor is needed? (需要编写复制构造函数的示例类)
Following is a complete C++ program to demonstrate use of Copy constructor. In the following String class, we must write copy constructor.
下面是一个完整的 C++ 程序来演示 Copy 构造函数的使用。在下面的 String 类中,我们必须编写复制构造函数。
#include<iostream>
#include<cstring>
using namespace std;
class String
{
private:
char *s;
int size;
public:
String(const char *str = NULL); // constructor
~String() { delete [] s; }// destructor
String(const String&); // copy constructor
void print() { cout << s << endl; } // Function to print string
void change(const char *); // Function to change
};
String::String(const char *str)
{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
void String::change(const char *str)
{
delete [] s;
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
String::String(const String& old_str)
{
size = old_str.size;
s = new char[size+1];
strcpy(s, old_str.s);
}
int main()
{
String str1("GeeksQuiz");
String str2 = str1;
str1.print(); // what is printed ?
str2.print();
str2.change("GeeksforGeeks");
str1.print(); // what is printed now ?
str2.print();
return 0;
}
Output:
GeeksQuiz
GeeksQuiz
GeeksQuiz
GeeksforGeeks
What would be the problem if we remove copy constructor from above code? (如果我们从上面的代码中删除复制构造函数会出现什么问题? )
If we remove copy constructor from the above program, we don’t get the expected output. The changes made to str2 reflect in str1 as well which is never expected.
如果我们从上述程序中删除复制构造函数,我们不会得到预期的输出。对 str2 所做的更改也反映在 str1 中,这是意料之外的。
#include<iostream>
#include<cstring>
using namespace std;
class String
{
private:
char *s;
int size;
public:
String(const char *str = NULL); // constructor
~String() { delete [] s; }// destructor
void print() { cout << s << endl; }
void change(const char *); // Function to change
};
String::String(const char *str)
{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
void String::change(const char *str)
{
delete [] s;
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}
int main()
{
String str1("GeeksQuiz");
String str2 = str1;
str1.print(); // what is printed ?
str2.print();
str2.change("GeeksforGeeks");
str1.print(); // what is printed now ?
str2.print();
return 0;
}
Output:
GeeksQuiz
GeeksQuiz
GeeksforGeeks
GeeksforGeeks
Can we make copy constructor private? (我们可以将复制构造函数设为私有吗? )
Yes, a copy constructor can be made private. When we make a copy constructor private in a class, objects of that class become non-copyable. This is particularly useful when our class has pointers or dynamically allocated resources. In such situations, we can either write our own copy constructor like above String example or make a private copy constructor so that users get compiler errors rather than surprises at runtime.
是的,可以将复制构造函数设为私有。当我们在类中将复制构造函数设为私有时,该类的对象将变为不可复制。当我们的类有指针或动态分配的资源时,这特别有用。在这种情况下,我们可以像上面的 String 示例那样编写自己的复制构造函数,也可以创建一个私有的复制构造函数,以便用户在运行时得到编译器错误而不是意外。
Why argument to a copy constructor must be passed as a reference? (为什么必须将复制构造函数的参数作为引用传递? )
A copy constructor is called when an object is passed by value. Copy constructor itself is a function. So if we pass an argument by value in a copy constructor, a call to copy constructor would be made to call copy constructor which becomes a non-terminating chain of calls. Therefore compiler doesn’t allow parameters to be passed by value.
当对象按值传递时,将调用复制构造函数。复制构造函数本身就是一个函数。因此,如果我们在复制构造函数中按值传递参数,则对复制构造函数的调用将成为调用复制构造函数的非终止链。因此编译器不允许参数按值传递。
Why argument to a copy constructor should be const? (为什么复制构造函数的参数应该是 const? )
See https://www.geeksforgeeks.org/copy-constructor-argument-const/