【C++第二阶段】调用拷贝构造函数时机

构造函数调用时机

有三种不同的情况会由编译器自动调用‘拷贝’构造函数。分别是:

直接调用

①使用括号或显式方法,直接调用拷贝构造函数。

使用一个已经创建完毕的对象来初始化一个新对象。

意为当有一个已经存在的对象时,再使用括号法或显式法,将类作为初始化参数传递给另一个对象中,会调用拷贝构造函数。

在实验之前,先做准备工作。

头文件Person.h

#pragma once
#include
#include
using namespace std;

class Person {
	string person_name = "NULL";
	int person_age = 0;
public:
	//默认无参构造函数调用
	Person();
	//有参构造函数调用
	Person(string name, int age);
	Person( int age,string name);
	Person(string name);
	Person(int age);

	//拷贝构造函数调用
	Person(const Person& person);

	//析构函数
	~Person();

	void set_name(string name);
	string get_name();

	void set_age(int age);
	int get_age();


};

实现文件Person.cpp

#include
#include
using namespace std;
#include"Person.h"

//默认无参构造函数调用
Person::Person() 
	{
		cout << "==========================" << endl;
		cout << "默认无参构造函数,已调用。" << endl;
		cout << "==========================" << endl;
	}

//有参构造函数调用
Person::Person(string name, int age) {
	person_name = name;
	person_age = age;
	cout << "=============================" << endl;
	cout << "有参name+age构造函数,已调用。" << endl;
	cout << "=============================" << endl;

}

Person::Person(int age,string name) {
	person_name = name;
	person_age = age;
	cout << "=============================" << endl;
	cout << "有参age+name构造函数,已调用。" << endl;
	cout << "=============================" << endl;

}
Person::Person(string name) {
	person_name = name;
	cout << "==========================" << endl;
	cout << "有参name构造函数,已调用。" << endl;
	cout << "==========================" << endl;
}

Person::Person(int age) {
	person_age = age;
	cout << "==========================" << endl;
	cout << "有参name构造函数,已调用。" << endl;
	cout << "==========================" << endl;
}

//拷贝构造函数调用
Person::Person(const Person& p_copy) {
	person_name = p_copy.person_name;
	person_age = p_copy.person_age;
	//为什么加了const之后类的私有权限下的成员属性可以取出来?
	//参考链接:https://blog.csdn.net/weixin_40539125/article/details/84205194
	//因为是在类内,甭管这个p_copy是什么对象,但是此时此刻就是在这个Person类内
	cout << "======================" << endl;
	cout << "拷贝构造函数,已调用。" << endl;
	cout << "======================" << endl;
}

Person::~Person() {
	cout << "=============" << endl;
	cout << "析构函数调用." << endl;
	cout << "=============" << endl;
}
void Person::set_name(string name) {
	person_name = name;
}
string Person::get_name() {
	return person_name;
}

void Person::set_age(int age) {
	person_age = age;
}
int Person::get_age() {
	return person_age;
}

主函数所在文件Section2.cpp

#include
#include
using namespace std;
#include"Person.h"

void test_v0() {
	string name = "张三";
	int age = 23;
	//括号法调用有参构造函数
	Person p_v0(name, age);
	//括号法调用拷贝构造函数
	Person p_v0_0(p_v0);

	cout << "输出p_v0_0的姓名为:" << p_v0_0.get_name() << ",年龄为:" << p_v0_0.get_age() << "." << endl;
}

int main() {
	cout << "hello world !" << endl;

	test_v0();
	system("pause");
	return 0;
}

运行结果为:

【C++第二阶段】调用拷贝构造函数时机_第1张图片

可以看到,通过括号法进行参数的创建时,调用了拷贝构造函数。

值传递方式作为参数传入函数

②作为方法的参数传递时,调用拷贝构造函数。

值传递的方式给函数参数传值。

在作为函数(等同于“方法”)的值传递时,编译器会将实参传入,但此时因为是值传递,所以在函数内部会复制一份实参作为函数内部的局部变量,此时编译器自动调用了拷贝构造函数。

代码实现:

#include
#include
using namespace std;
#include"Person.h"

void test_v1_0(Person person) {
	//括号法调用拷贝构造函数
	Person p_v1_0(person);
	cout << "p_v1_0的地址为:" << &p_v1_0 << "." << endl;
}

void test_v1() {
	cout << "================================" << endl;
	cout << "类作为参数时,调用拷贝构造函数。" << endl;

	Person p_v1;
	p_v1.set_name("张三");
	p_v1.set_age(24);
	cout << "p_v1的地址为:\t" << &p_v1 << "." << endl;

	test_v1_0(p_v1);

	cout << "================================" << endl;
}


int main() {
	cout << "hello world !" << endl;
	test_v1();

	system("pause");
	return 0;
}

运行结果如下:

【C++第二阶段】调用拷贝构造函数时机_第2张图片

可以看到用值传递的方式给函数test_v1_0参数传值,在调用函数test_v1时,会把实参拷贝一份放入函数中。所以在打印完p_v1的地址后,进行了两次拷贝构造函数的调用。同时,在函数test_v1_0调用结束后,会销毁两个对象,故调用了两次析构函数。在函数test_v1结束时,销毁了最开始的p_v1对象。

以值方式返回局部对象

③类作为返回值时,调用拷贝构造函数。

以值方式返回局部对象。

返回局部对象,说的是不返回局部变量,而由编译器新拷贝了一个对象,赋值给新的对象。

代码实现:

#include
#include
using namespace std;
#include"Person.h"

Person test_v2_0() {
	Person p_v2;
	cout << "p_v2的地址为:\t\t" << &p_v2 << "." << endl;
	return p_v2;

}

void test_v2() {
	//类作为返回值时,调用拷贝构造函数
	Person p_return = test_v2_0();
	cout << "p_return 的地址为:\t" << &p_return << "." << endl;
}


int main() {
	cout << "hello world !" << endl;
	test_v2();
	
	system("pause");
	return 0;
}

注意:g++编译器默认开启了返回值优化RVO,返回时只有一个地址。如下所示:

【C++第二阶段】调用拷贝构造函数时机_第3张图片

PS:

若关闭返回值优化,则需要在项目资源管理器中右键对应包含的cpp接口实现文件-Person.cpp单击属性,打开属性配置,

【C++第二阶段】调用拷贝构造函数时机_第4张图片

禁用即可,同时注意配置(C):那里,要切换不同的配置,把优化都给关掉。

但是,仍然没有改变最后的结果。

所以,现在先理解,若以对象作为函数的返回值,会将在函数内部定义的局部变量销毁,并由编译器自动调用构造函数新建一个对象,作为返回值。

接着将g++编译器后面加上选项-fno-elide-constructors禁用优化,否则没有拷贝过程!!(bilibili弹幕)

参考:https://blog.csdn.net/weixin_42929607/article/details/106248798

其他学习过程中的想法

问题a:如果开发者不自己写拷贝构造函数,所有的成员属性能被拷贝吗?

猜想a:开发者不重写的话,所有成员属性可以被拷贝。

实验验证a:取出使用括号法,显式法,隐式法调用的拷贝构造函数的对象的成员属性。(成员属性有初始值)需要把Person.hPerson.cpp拷贝构造函数的声明与实现注释掉,然后使用括号法进行拷贝构造函数的调用。

#include
#include
using namespace std;

#include"Person.h"

void test_v0() {
	string name = "李四";
	int age = 25;
	//括号法调用有参构造函数
	Person p_v0(name, age);
	//括号法调用拷贝构造函数
	Person p_v0_0(p_v0);

	cout << "输出p_v0_0的姓名为:" << p_v0_0.get_name() << ",年龄为:" << p_v0_0.get_age() << "." << endl;
}

int main() {
	cout << "hello world !" << endl;
	test_v0();

	system("pause");
	return 0;
}

结果a:

【C++第二阶段】调用拷贝构造函数时机_第5张图片

所以,它是有默认的拷贝构造函数的,并且能够复制值。那么,其他的构造函数是否存在呢?比如有参。同理,将有参构造函数的声明与实现注释掉,查看结果。

发现会编译不通过,直接报错。

【C++第二阶段】调用拷贝构造函数时机_第6张图片

问题b:如果作为引用传递,编译器会调用哪种构造函数?同时,在函数中设置作为引用传入的实参对象中的成员属性会更改值吗?

猜想b:作为引入参数传入时,不会调用构造函数,因为是指针常量—类引用—传递到函数中,只有在新建对象时,才会调用拷贝构造函数。所以,析构函数语句显示应该有2条,默认无参构造函数显示一条,拷贝构造函数显示一条。在局部函数中,作为引用传入的实参对象中的成员属性会改变。

实验验证猜想:上代码:

#include
#include
using namespace std;

#include"Person.h"

void test_v1_0(Person &person) {
	//括号法调用拷贝构造函数
	Person p_v1_0(person);
	cout << "p_v1_0的地址为:" << &p_v1_0 << "." << endl;
	string v1_0_name = "王五";
	int v1_0_age = 100;
	person.set_name(v1_0_name);
	person.set_age(v1_0_age);
	cout << "此时p_v1_0的姓名为:\t" << p_v1_0.get_name() << ",年龄为:" << p_v1_0.get_age() << "." << endl;
}

void test_v1() {
	cout << "================================" << endl;
	cout << "类作为参数时,调用拷贝构造函数。" << endl;

	Person p_v1;
	p_v1.set_name("张三");
	p_v1.set_age(24);
	cout << "p_v1的地址为:\t" << &p_v1 << "." << endl;
	cout << "此时p_v1的姓名为:\t" << p_v1.get_name() << ",年龄为:" << p_v1.get_age() << "." << endl;

	test_v1_0(p_v1);
	cout << "运行完test_v1_0传引入参数后..." << endl;
	cout << "p_v1的地址为:\t" << &p_v1 << "." << endl;
	cout << "此时p_v1的姓名为:\t" << p_v1.get_name() << ",年龄为:" << p_v1.get_age() << "." << endl;

	cout << "================================" << endl;
}

int main() {
	cout << "hello world !" << endl;
	test_v1();

	system("pause");
	return 0;
}

结果:

【C++第二阶段】调用拷贝构造函数时机_第7张图片

可以看到,问题b的猜想已验证正确。

问题c:成员属性是private权限,为什么拷贝构造函数开发者重写时可以直接使用“作为参数”的类对象成员属性?

回答:因为这样也算是在类中,只不过对象名不同。

参考链接:https://blog.csdn.net/weixin_40539125/article/details/84205194

你可能感兴趣的:(C++学习与回顾,c++,开发语言)