C++拷贝构造函数详解
C++拷贝构造函数详解
简介
拷贝构造函数是C++独有的一种特殊的构造函数,以同型对象初始化自我对象。
拷贝构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用拷贝构造函数。当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式调用拷贝构造函数。
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:
- 通过使用另一个同类型的对象来初始化新创建的对象。
- 复制对象把它作为参数传递给函数。
- 复制对象,并从函数返回这个对象。
如果用户没有定义拷贝构造函数,但是调用了拷贝构造函数,那么编译器会自动生成一个默认的拷贝构造函数。但是如果自己定义了拷贝构造函数,编译器则不在生成。
最常见的形式如下:
classname (const classname& obj){
// code here
}
下面对三种调用拷贝构造函数的情况进行一一说明:
通过使用另一个同类型的对象来初始化新创建的对象
#include <iostream>
using namespace std;
class Student {
public:
Student(); // default构造函数
Student(const Student& obj); // 拷贝构造函数
int getNumber();
private:
int number;
};
// 定义默认构造函数
Student::Student(){
this->number = 0;
cout << "default constructor" << endl;
}
// 定义拷贝构造函数
Student::Student(const Student& obj) {
this->number = obj.number;
cout << "copy constructor" << endl;
}
int Student::getNumber() {
return this->number;
}
int main(){
Student s1; // 调用默认构造函数
Student s2(s1); // 调用拷贝构造函数
Student s3 = s2; // 调用拷贝构造函数
cout << s1.getNumber() << endl;
cout << s2.getNumber() << endl;
cout << s3.getNumber();
return 0;
}
/*
Output
default constructor
copy constructor
copy constructor
0
0
0
*/
这里创建了三个对象,s1 s2 s3。
s1是调用了默认的构造函数,number为0。
s2是调用了拷贝构造函数,传入了s1,复制了s1的number,输出了"copy constructor"。
s3的创建是一种特殊的调用拷贝构造函数的方法。注意,如果有一个新对象被创建,那么一定会调用构造函数。这里就是传入了s2到我们定义的拷贝构造函数里。
可见,通过使用另一个同类型的对象来初始化新创建的对象,拷贝构造函数的用处是很明显的。
复制对象把它作为参数传递给函数
拷贝构造函数是一个非常重要的函数,其之所以重要,是因为它定义了一个对象如何以值传递给函数。
试想如下代码:
#include <iostream>
using namespace std;
class Student {
public:
Student(); // default构造函数
Student(const Student& obj); // 拷贝构造函数
int getNumber();
private:
int number;
};
// 定义默认构造函数
Student::Student(){
this->number = 0;
cout << "default constructor" << endl;
}
// 定义拷贝构造函数
Student::Student(const Student& obj) {
this->number = obj.number;
cout << "copy constructor" << endl;
}
int Student::getNumber() {
return this->number;
}
// 输出某个对象的number
void showNumber(Student a) {
cout << a.getNumber();
}
int main(){
Student s; // 调用默认构造函数
showNumber(s);
return 0;
}
你认为输出结果会是什么?首先肯定会输出一个"default constructor",因为s的默认构造函数输出了该字符串。那么,showNumber()会输出什么呢?正确的输出是:
default constructor
copy constructor
0
这就回到了我们开头的那句话,拷贝构造函数定义了一个对象如何以值传递给函数。
在函数showNumber()中,参数a是以值传递的方式传入的。所以主函数中调用showNumber()时,对象s被复制到了形参a当中。这个复制操作是通过调用了拷贝构造函数完成的,所以调用了自身的拷贝构造函数,自然会输出一遍"copy constructor"。
相当于执行了这样一个操作
Student a(s);
等待showNumber()结束之后,再析构a这个对象。
这也就是为什么传递用户自定类型通常用引用传递,同时也解释了,为什么拷贝构造函数传参为什么是引用传参?
设想,如果拷贝构造函数这么写,会发生什么?
Student (const Student obj);
假设有个Student对象s。我值传递s进入了拷贝构造函数,那么按照之前所说,编译器会去申请一个空间给形参obj,同时调用自身的拷贝构造函数把s的数据成员的值传给obj。但是这个调用拷贝构造函数的过程,又是一个值传递,又需要开辟一个空间....
所以,只要以值传递的方式调用拷贝构造函数,就会申请空间,申请空间的过程中又会申请空间,又申请空间的过程又又申请空间。如此往复,会陷入无限套娃的死循环。
所以拷贝构造函数一定不能是值传递。
复制对象,并从函数返回这个对象
返回类型为类对象的函数,如果返回类型不是引用,在执行return时,会创建一个临时对象,并调用拷贝构造函数给这个临时对象赋值。
#include <iostream>
using namespace std;
class Student {
public:
Student(); // default构造函数
Student(const Student& obj); // 拷贝构造函数
int getNumber();
private:
int number;
};
// 定义默认构造函数
Student::Student(){
this->number = 0;
cout << "default constructor" << endl;
}
// 定义拷贝构造函数
Student::Student(const Student& obj) {
this->number = obj.number;
cout << "copy constructor" << endl;
}
int Student::getNumber() {
return this->number;
}
Student returnS() {
Student s;
return s;
}
int main(){
cout << returnS().getNumber();
return 0;
}
// output
/*
default constructor
copy constructor
0
*/
我们在函数returnS()中,创建了局部对象s。s在创建的时候,调用了默认的构造函数,输出"default constructor"。然而在return的时候,调用了拷贝构造函数,创建了一个看不见的对象,所以再次输出"copy constructor"。
所以,我们可以通过返回类对象的引用,这样就会返回现存对象,而不是临时创建的对象。(当然,局部变量是不可以引用传回的,临时变量在函数结束时销毁,引用为空)