一、拷贝构造函数的定义及作用
拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。
拷贝函数用于对象的初始化。 例:一个简单得拷贝函数如下
class Test3{
public:
Test3(const Test3&obj) {
a = obj.a + 100;
b = obj.b + 100;
cout << "拷贝构造函数" << endl;
}
~Test3(){//析构函数
}
};
二、拷贝构造函数的三种调用场景
2.1 对象通过另外一个对象进行初始化
直接定义一个对象来初始化另一个对象,具体有两种写法。
class Test3{
public:
Test3(const Test3&obj) {
a = obj.a + 100;
b = obj.b + 100;
cout << "拷贝构造函数" << endl;
}
~Test3(){//析构函数
}
};
void objectplay1()
{
//赋值构造函数:用一个对象初始化另外一个对象
Test3 t1(1, 2); //这里实际上只传入了2,逗号表达式传入最后一个值!!
//第一种调用方法
//注意赋值操作:t2=t1 不会调用拷贝构造函数
//相当于复制操作及可以理解为操作符重载
Test3 t2 = t1; //用t1来初始化t2
//第二种调用方法
Test3 t3(t2); //括号法初始化
}
2.2 对象以值传递的方式传入函数参数
该场景中有一个全局函数 void f(Location p), void f(Location p)的传入参数是一个元素,就是我们定义的Location。
class Location {
public:
Location(int _x, int _y) {
x = _x;
y = _y;
}
Location(Location &obj) {
x = obj.x + 10;
y = obj.y + 10;
}
~Location() {
cout << "an object destroyed." << endl;
}
int GexX() { return x; } int GetY() { return y; }
private:
int x;
int y;
};
//形参是一个元素,此时初始化会调用一次拷贝构造函数!!!
void f(Location p) {
cout << p.GexX() << endl;
}
void objectplay2() {
Location b(1, 2);
f(b);// b实参取值初始化形参P的过程当中会调用拷贝构造函数!!!!
}
调用f()函数的过程中,会经历一下几个步骤:
- 当使用 void f(Location p) 的时候,会产生一个匿名对象,假定匿名对象为xx。
- 调用拷贝构造函数去初始化匿名对象。【类似:Location xx(p);】
- 等 void f(Location p)函数执行完毕后,就会析构掉这个匿名对象。
可以通过分步调试验证!
2.3 对象以值传递的方式从函数返回
该场景中我们定义了一个 Location g() ,返回值为我们的对象。
class Location {
public:
Location(int _x, int _y) {
x = _x;
y = _y;
}
Location(Location &obj) {
x = obj.x + 10;
y = obj.y + 10;
}
~Location() {
cout << "an object destroyed." << endl;
}
int GexX() { return x; } int GetY() { return y; }
private:
int x;
int y;
};
//第三种 难点
//g函数返回一个元素
//结论1:函数的返回值是一个元素(复杂类型的),返回的是一个新的匿名对象
//所以会调用匿名对象的拷贝构造函数!
//结论2:匿名对象的去和留
//如果用匿名对象初始化宁外一个同类型的对象,那么匿名对象直接转成有名对象
//如果用匿名对象赋值(=)给宁外一个同类型的对象,匿名对象就被析构。
//你这样写C++设计者就认为你要返回一个新对象(匿名对象)
Location g() {
Location a(4, 5);
//这个时候会执行一次拷贝构造函数!
return a;
//紧接着他析构了一个构造函数
}
void objectplay3_1()
{
//又析构了,析构的是匿名对象
g();
}
void objectplay3_2()
{
//把匿名对象直接初始化为m
//此时C++编译器直接把匿名对象变成m,匿名函数没被析构直接变为m,没有调用拷贝构造函数
//Location m = g(); //似乎已经被优化,这样写的话编译不通过
Location m2(1,2);
m2 = g();//因为匿名对象是=给m2,匿名对象被析构
}
在void objectplay3_1场景中执行到Location g() 的return时,会产生以下几个重要的步骤:
- 首先会产生一个匿名对象,假定匿名对象为xx
- 然后调用拷贝构造函数把a的值给匿名函数【类似:Location xx(a);】
- 等Location g()执行完后,就析构掉匿名对象
在void objectplay3_2场景中执行到Location g() 的return时,会产生以下几个重要的步骤:
- 首先会产生一个匿名对象,假定匿名对象为xx
- 然后调用拷贝构造函数把a的值给匿名函数【类似:Location xx(a);】
- 匿名对象赋值给m2,所以析构掉匿名对象