c++中拷贝构造函数的使用时机以及实例分析

1、c++中拷贝构造函数的使用时机

我们知道,c++中拷贝构造函数的使用时机有三种:

  • 使用一个已创建完毕的对象来初始化另一个对象;
  • 值传递的方式给函数传值;
  • 以值的方式返回局部对象。

第一种不必多言,我们重点关注第二种和第三种,直接从代码分析。

class Phone {
public:
	Phone() {
		p_Name = "华为";
		cout << "手机类默认构造函数的调用" << endl;
	}

	Phone(string name) :p_Name(name) {
		cout << "手机类有参构造函数的调用" << endl;
	}

	Phone(const Phone& p) {
		p_Name = p.p_Name;
		cout << "手机类拷贝构造函数的调用" << endl;
	}

	string getPName() {
		return p_Name;
	}

	~Phone() {
		cout << "手机类析构函数的调用" << endl;
	}

private:
	string p_Name;
};

首先定义一个Phone类,并初始化三种构造函数,方便更加清晰看到调用了哪种构造函数。

  • 先来看以值传递的方式给函数传值:
void test01(Phone p) {
	cout << p.getPName() << endl;
	cout << "值传递的方式给函数传值" << endl;
}

main函数中代码如下:

int main(){
	Phone p1;  // 默认构造的方式创建对象
	test01(p1);  // 使用值传递的方式给函数传值,传的值为自定义类
	system("pause");
	return 0;
}

运行结果如下,可以看到,使用值传递的方式给函数传值,本质上执行的代码为

Phone p = p1;

这是隐式拷贝构造函数调用方式。由于p是test01函数的形参,存放在栈区,在test01执行结束后,便会理解释放,即调用该类的析构函数。
在这里插入图片描述

  • 再来看值的方式返回局部对象:
Phone test02() {
	cout << "以值的方式返回局部对象" << endl;
	Phone p;
	return p;
}

main函数的定义如下:

int main(){
	Phone p1 = test02();  
	system("pause");
	return 0;
}

运行结果如下,首先通过无参构造的方式实例化一个对象p,然后将其返回。调用test02的时候,本质上就是

Phone p1 = p;

也是拷贝构造函数的调用。如果没有返回值接受,直接将main函数中的第一行改为test02();那么此处就是一个匿名对象,该对象也会马上被释放,如下图2所示。
在这里插入图片描述
在这里插入图片描述
以上就是拷贝函数的两种使用时机,接下来我会以一个案例来其具体应用。
我们定义一个Person类,让前面定义的Phone类作为Person类的类成员,Person类的定义如下:

class Person {
public:
	Person(string name, Phone p) {
		cout << "人类的构造函数的调用" << endl;
		cout << m_Phone.getPName() << endl;
		m_Name = name;
		m_Phone = p;
		cout << m_Phone.getPName() << endl;
	}

	string getName() {
		return m_Name;
	}

	// 以值的方式返回局部对象
	Phone getPhone() {
		return m_Phone;
	}

	~Person() {
		cout << "人类析构函数的调用" << endl;
	}

private:
	string m_Name;
	Phone m_Phone;
};

定义一个test函数具体做一些测试:

void test() {
	Phone phone("小米");  // 实例化手机类
	Person person("小王", phone);
	cout << "姓名:" << person.getName() << endl;
	cout << "手机牌子:" << person.getPhone().getPName() << endl;
	cout << "手机牌子:" << person.getPhone().getPName() << endl;
	cout << "手机牌子:" << person.getPhone().getPName() << endl;
}

main函数为:

int main() {
	test();
	system("pause");
	return 0;
}

在这里插入图片描述
相应的结果如上图所示,我们来主句分析打印结果。
首先test中的第一句Phone phone("小米");,很明显就是通过有参构造的方式实例化手机类;
复杂点就在test的第二局Person person("小王", phone);
我们前面讲到,使用值传递的方式给函数传值,调用的是拷贝构造函数,所以此时应该先进入手机类的拷贝构造函数中,即对应运行结果的第二句。即Phone p = phone;,然后进入人类的构造函数中,打印第三句;
这里需要补充一点,当其他对象作为本类成员,构造时先构造类对象,再构造自身,析构反之。所以编译器首先构造的是手机类对象,即Phone = m_Phone;,很明显这是默认构造的方式,所以对应结果的第四句;
然后在人类的构造函数中我们首先打印了m_Phone的手机类型,使用默认构造函数是为“华为”,所以就对应第五句;
然后通过m_Phone = p;,将拷贝phone得到的对象p的所有属性全部赋值给m_Phone,之后再打印手机类型,即为“小米”,对应第六句;
随着人类的构造函数执行完毕,作为形参变量的p,就会被立即释放,就会调用手机类的析构函数,对应第七句;
然后就是打印人的姓名,对应第八句;
然后就是通过person.getPhone().getPName(),获取手机类型,此处就对应于前面提到过的使用值传递的方式返回局部对象,所以会调用手机的拷贝构造函数,而且这是一个匿名对象,会立即释放,我们这里重复了3次这个语句,对应第八到第十七句;
随着test函数的执行完毕,所有在栈区的变量都会随之释放。根据规则,首先释放的是人类,所以会调用人类的析构函数,对应第18句;
然后释放人类中的类成员,即m_Phone对象,调用一次手机类的析构函数;
最后,作为test函数的局部变量,phone也要释放,所以最终会再调用一个手机类的析构函数。
至此,整个代码的分析结束。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值