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"。

所以,我们可以通过返回类对象的引用,这样就会返回现存对象,而不是临时创建的对象。(当然,局部变量是不可以引用传回的,临时变量在函数结束时销毁,引用为空)

posted @ 2020-03-24 19:27  scyq  阅读(5800)  评论(0编辑  收藏  举报