复制构造函数:只有单个形参,而且形参是对本类类型对象的引用(常用const修饰),这样的构造函数称为复制构造函数。与默认构造函数一样,复制构造函数可由编译器隐式调用。复制构造函数可以用于:
- 根据另一个同类型的对象显式或隐式初始化一个对象
- 复制一个对象,将它作为实参传给一个函数
- 从函数返回时复制一个对象
- 初始化顺序容器中的元素
- 根据元素初始化列表初始化数组元素
下面我写了一个程序对以上描述做出解释。
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class Test
{
public:
Test(){str="Kevin";x=10;cout<<"default constructor"<<endl;} //construction function
Test(const Test& t){str=t.str;x=t.x; cout<<"copy construction was used!"<<endl;} //copy constructor
Test& operator=(const Test& t){str=t.str;x=t.x;cout<<"operator="<<endl;return *this;}
private:
string str;
int x;
};
Test f(Test t)
{
cout<<"f function"<<endl;
return t;
}
main()
{
cout<<"Test t:"<<endl;
Test t;
cout<<endl<<"vector<Test> vec1(5):"<<endl;
vector<Test> vec1(5);
cout<<endl<<"vector<Test> vec2(5,t):"<<endl;
vector<Test> vec2(5,t);
cout<<endl<<"Test te[5]:"<<endl;
Test te[5];
cout<<endl<<"Test t2(t):"<<endl;
Test t2(t);
cout<<endl<<"f(t2):"<<endl;
f(t2);
cout<<endl<<""<<endl;
Test* p1=new Test();
*p1=t;
Test* p2=&t;
Test t3=t;
Test t4;
t4=t;
}
以上程序的输出结果是:
default constructor
vector<Test> vec1(5):
default constructor
copy construction was used!
copy construction was used!
copy construction was used!
copy construction was used!
copy construction was used!
vector<Test> vec2(5,t):
copy construction was used!
copy construction was used!
copy construction was used!
copy construction was used!
copy construction was used!
Test te[5]:
default constructor
default constructor
default constructor
default constructor
default constructor
Test t2(t):
copy construction was used!
f(t2):
copy construction was used!
f function
copy construction was used!
default constructor
operator=
copy construction was used!
default constructor
operator=
由上面程序中的第26行可以看出,复制构造函数被用于初始化容器元素的时候,如果没有之处元素的初始值,则编译器会首先调用默认的构造函数创建一个临时制,然后使用复制构造函数将临时制复制到每一个元素中。当指明了元素的初始值(如29行所示)时,编译器会直接将其复制到每一个元素。
由程序中第32行的运行结果可以看出,定义一个类类型的数组时,编译器会利用类的默认构造函数初始化每一个元素。
由程序中第38行的运行结果可以看出,当类类型被用做形参与返回值时,各自发生了一次复制。C++ primer中这样描述:“当函数的形参为非引用类型的时候,将复制实参的值。类似的,以非引用类型做返回值时,将返回return语句中的值的副本”。按照我的理解就是“当形参和返回值都不是引用时,进入函数时发生一次复制,离开函数时发生一次复制”。
程序的第41行至第46行是为了说明复制构造函数与赋值操作符的区别设计的。我们可以看到*p1=t;这一句用到了赋值操作符,t4=t;这一句也用到了赋值操作符,其他各种形式的语句都用的是复制构造函数。从中可以总结出一句话“从无到有是copy,从有到有是赋值”。这句话怎么理解呢?大家看,*p1=t这一句中的*p是早已经声明了的,在*p1声明的时候已经完成了它的初始化。在*p1=t这句中*p是已经初始化过的,所以这里*p1=t应用的是赋值操作符。同理第46行的中的t4也是一个已经声明过,完成了初始化的变量,所以在语句t4=t中使用的也是赋值操作符。而在Test t3=t; 这样的语句中,t3是刚刚声明出来的,还没有初始化,要求编译器按照t的内容初始化t3。这正好是复制构造函数的作用。