C++ 对象的初始化和清理:构造函数和析构函数

news2025/1/22 15:45:10

目录

构造函数和析构函数

构造函数

析构函数

构造函数的分类及调用

括号法 

显示法

隐式转换法

拷贝构造函数的调用时机

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

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

以值方式返回局部对象

构造函数调用规则

初始化列表

类对象作为其他类成员 各类的构造和析构执行顺序


  

构造函数和析构函数

构造函数

主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

构造函数语法:类名(){}

  1. 构造函数,没有返回值也不写void
  2. 函数名称与类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数

主要作用在于对象**销毁前**系统自动调用,执行一些清理工作。

析构函数语法:~类名(){}

  1. 析构函数,没有返回值也不写void
  2. 函数名称与类名相同,在名称前加上符号  ~
  3. 析构函数不可以有参数,因此不可以发生重载
  4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构 

class Person {

public:
	/* 构造函数 */
	Person() {
		cout << "我是构造函数" << endl; // 我是构造函数
	}

	/* 细狗函数 */
	~Person() {
		cout << "我是细狗函数" << endl; // 我是细狗函数(system("pause")在的情况,不会执行,因为代码还没走完,不会去销毁类)
	}
};

int main() {
	class Person zhanghai;

	system("pause");
	return 0;
}

构造函数的分类及调用

两种分类方式:

  1. 按参数分为: 有参构造和无参构造(默认构造)
  2. 按类型分为: 普通构造和拷贝构造

三种调用方式:

  1. 括号法
  2. 显示法
  3. 隐式转换法

括号法 

class Person {
public:
	int age;

	Person() {
		cout << "Person的无参构造函数调用" << endl;
	}
	Person(int a) {
		age = a;
		cout << "Person的有参构造函数调用" << endl;
	}
	Person(const Person &p) { 
		//将传入的人身上的所有属性,拷贝到我身上
		age = p.age;
		cout << "Person的拷贝构造函数调用" << endl;
	}

	~Person() {
		cout << "Person的细狗函数" << endl;
	}
};

class Person p1;   // 默认 无参构造函数的调用 (调用默认的无参构造时,不要加小括号)
class Person p2(1); // 有参构造函数的调用
class Person p3(p2); // 拷贝构造函数的调用
cout << p2.age << endl; // 10
cout << p3.age << endl; // 10

显示法

class Person {
public:
	int age;

	Person() {
		cout << "Person的无参构造函数调用" << endl;
	}
	Person(int a) {
		age = a;
		cout << "Person的有参构造函数调用" << endl;
	}
	Person(const Person &p) { 
		//将传入的人身上的所有属性,拷贝到我身上
		age = p.age;
		cout << "Person的拷贝构造函数调用" << endl;
	}

	~Person() {
		cout << "Person的细狗函数" << endl;
	}
};

Person p1;
Person p2 = Person(10); // 有参构造
Person p3 = Person(p2); // 拷贝构造

Person(10); // 匿名对象,特点:当前行执行结束后,系统会立即回收掉匿名对象
cout << "我的上一句输出是 Person的细狗函数" << endl;
Person(p3); // 不要利用拷贝构造函数初始化对象,编辑器会认为 Person(p3) == Person p3

隐式转换法


class Person {
public:
	int age;

	Person() {
		cout << "Person的无参构造函数调用" << endl;
	}
	Person(int a) {
		age = a;
		cout << "Person的有参构造函数调用" << endl;
	}
	Person(const Person &p) { 
		//将传入的人身上的所有属性,拷贝到我身上
		age = p.age;
		cout << "Person的拷贝构造函数调用" << endl;
	}

	~Person() {
		cout << "Person的细狗函数" << endl;
	}
};

Person p4 = 10; // 有参构造 相当于 Person p4 = Person(10)
Person p5 = p4; // 拷贝构造 相当于 Person p5 = Person(p4)

拷贝构造函数的调用时机

C++中拷贝构造函数调用时机通常有三种情况

  1. 使用一个已经创建完毕的对象来初始化一个新对象
  2. 值传递的方式给函数参数传值
  3. 以值方式返回局部对象

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

class Person {
public:
	Person() {
		cout << "无参构造函数!" << endl;
		mAge = 0;
	}
	Person(int age) {
		cout << "有参构造函数!" << endl;
		mAge = age;
	}
	Person(const Person& p) {
		cout << "拷贝构造函数!" << endl;
		mAge = p.mAge;
	}
	//析构函数在释放内存之前调用
	~Person() {
		cout << "析构函数!" << endl;
	}
public:
	int mAge;
};

void test01() {

	Person man(100); //p对象已经创建完毕
	Person newman(man); //调用拷贝构造函数
	Person newman2 = man; //拷贝构造

	//Person newman3;
	//newman3 = man; //不是调用拷贝构造函数,赋值操作
}

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

class Person {
public:
	Person() {
		cout << "无参构造函数!" << endl;
		mAge = 0;
	}
	Person(int age) {
		cout << "有参构造函数!" << endl;
		mAge = age;
	}
	Person(const Person& p) {
		cout << "拷贝构造函数!" << endl;
		mAge = p.mAge;
	}
	//析构函数在释放内存之前调用
	~Person() {
		cout << "析构函数!" << endl;
	}
public:
	int mAge;
};


void doWork(Person p1) { //相当于Person p1 = p;

}
void test02() {
	Person p; //无参构造函数
	doWork(p); 
}

以值方式返回局部对象

class Person {
public:
	Person() {
		cout << "无参构造函数!" << endl;
		mAge = 0;
	}
	Person(int age) {
		cout << "有参构造函数!" << endl;
		mAge = age;
	}
	Person(const Person& p) {
		cout << "拷贝构造函数!" << endl;
		mAge = p.mAge;
	}
	//析构函数在释放内存之前调用
	~Person() {
		cout << "析构函数!" << endl;
	}
public:
	int mAge;
};

Person doWork2()
{
	Person p1;
	cout << (int *)&p1 << endl;
	return p1; // 返回的是p1的副本,跟p1毫无干系,只是相同而已
}

void test03()
{
	Person p = doWork2(); // Person p = p1
	cout << (int *)&p << endl;
}

int main() {

	/*
	  依次输出:
		无参构造函数
		ff44aaff
		拷贝构造函数
		析构函数
		ff55aacc
		析构函数
	*/
	test03();

	return 0;
}

构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

  • 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
#include <iostream>
using namespace std;

class Person {
public:
	//无参(默认)构造函数
	Person() {
		cout << "无参构造函数!" << endl;
	}
	//有参构造函数
	Person(int a) {
		age = a;
		cout << "有参构造函数!" << endl;
	}
	//拷贝构造函数
	//Person(const Person& p) {
	//	age = p.age;
	//	cout << "拷贝构造函数!" << endl;
	//}
	//析构函数
	~Person() {
		cout << "析构函数!" << endl;
	}
public:
	int age;
};

void test01()
{
	Person p1(18);
	//如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
	Person p2(p1);

	cout << "p2的年龄为: " << p2.age << endl;
}

void test02()
{
	//如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
	Person p1; //此时如果用户自己没有提供默认构造,会出错
	Person p2(10); //用户提供的有参
	Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供

	//如果用户提供拷贝构造,编译器不会提供其他构造函数
	Person p4; //此时如果用户自己没有提供默认构造,会出错
	Person p5(10); //此时如果用户自己没有提供有参,会出错
	Person p6(p5); //用户自己提供拷贝构造
}

int main() {
	/*
	  注释掉 拷贝构造函数 的情况
		有参构造函数!
		p2的年龄为: 18
		析构函数!
		析构函数!
	*/
	test01();

	return 0;
}

初始化列表

C++用来初始化属性 ——`构造函数():属性1(值1),属性2(值2)... {}

class Person {
public:

	传统方式初始化
	//Person(int a, int b, int c) {
	//	m_A = a;
	//	m_B = b;
	//	m_C = c;
	//}

	//初始化列表方式初始化,注意冒号的位置
	Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {
	
	}
	void PrintPerson() {
		cout << "mA:" << m_A << endl; // 1
		cout << "mB:" << m_B << endl; // 2
		cout << "mC:" << m_C << endl; // 3
	}
private:
	int m_A;
	int m_B;
	int m_C;
};

int main() {

	Person p(1, 2, 3);
	p.PrintPerson();

	return 0;
}

类对象作为其他类成员 各类的构造和析构执行顺序

构造的顺序是 :

先调用对象成员的构造,再调用本类构造

析构顺序与构造相反

#include <iostream>
#include <string>
using namespace std;

class Phone
{
public:
	Phone(string name)
	{
		m_PhoneName = name;
		cout << "Phone构造" << endl;
	}

	~Phone()
	{
		cout << "Phone析构" << endl;
	}

	string m_PhoneName;

};


class Person
{
public:

	// 初始化列表可以告诉编译器调用哪一个构造函数 Phone m_Phone = pName => Phone m_Phone = Phone(pName) 有参构造
	Person(string name, string pName) :m_Name(name), m_Phone(pName)
	{
		cout << "Person构造" << endl;
	}

	~Person()
	{
		cout << "Person析构" << endl;
	}

	void playGame()
	{
		cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手机! " << endl;
	}

	string m_Name;
	Phone m_Phone;

};
void test01()
{
	// 当类中成员是其他类对象时,我们称该成员为 对象成员
	// 构造的顺序是 :先调用对象成员的构造,再调用本类构造
	// 析构顺序与构造相反
	Person p("张三", "苹果X");
	p.playGame();

}


int main() {

	test01();

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1305410.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA:深入了解Java中的Synchronized关键字

1、简述 在Java中&#xff0c;多线程编程是一项常见的任务&#xff0c;然而&#xff0c;它也伴随着一系列潜在的问题&#xff0c;比如竞态条件&#xff08;Race Condition&#xff09;和数据不一致性。为了解决这些问题&#xff0c;Java提供了一种同步机制&#xff0c;即synch…

项目一:IIC读写EEPROM AT24C02

回头想了想在工作中调过的EEPROM还挺多的&#xff0c;有M24M02 、M28010 、AT24C02等&#xff0c;今天讲一下AT24C02吧 一、AT24C02简介 1.1 特点 文档已经上传了&#xff0c;需要的同学可以自行下载哈&#xff0c;晚点我会把下载链接附上来。 我大概照着文档翻译了一下&am…

总线一:I2C简介(介绍看这一篇就够啦)

本节主要介绍以下内容&#xff1a; I2C协议简介 STM32的I2C特性及架构 I2C初始化结构体详解 一、I2C协议简介 I2C 通讯协议(Inter&#xff0d;Integrated Circuit)是由Phiilps公司开发的&#xff0c;由于它引脚少&#xff0c;硬件实现简单&#xff0c;可扩展性强&#xff…

2000-2021年全国各省环境规制水平数据

2000-2021年全国各省环境规制水平数据 1、时间&#xff1a;2000-2021年 2、范围&#xff1a;30省市 3、指标&#xff1a;工业污染治理完成投资、工业增加值、环境规制强度 4、计算说明&#xff1a;环境规制工业污染治理完成投资/工业增加值 5、来源&#xff1a;国家统计局…

LLM之RAG理论(一)| CoN:腾讯提出笔记链(CHAIN-OF-NOTE)来提高检索增强模型(RAG)的透明度

论文地址&#xff1a;https://arxiv.org/pdf/2311.09210.pdf 检索增强语言模型&#xff08;RALM&#xff09;已成为自然语言处理中一种强大的新范式。通过将大型预训练语言模型与外部知识检索相结合&#xff0c;RALM可以减少事实错误和幻觉&#xff0c;同时注入最新知识。然而&…

Linux - 进程间通信(中)- 管道的应用场景

前言 在上篇博客当中&#xff0c;对Linux 当中的进程通信&#xff0c;做了详细阐述&#xff0c;主要是针对父子进程的通信来阐述的同时&#xff0c;也进行了模拟实现。 对于管道也有了初步了解&#xff0c;但是这仅仅是 进程间通信的一部分&#xff0c;Linux 当中关于进程间通…

散点图,盒须图,折线图混放在一个echarts

散点图&#xff0c;何须图&#xff0c;折线图混放在一个echarts option {tooltip: {trigger: axis,axisPointer: {type: cross,crossStyle: {color: #999}}},legend: {data:[盒须图1,盒须图2,折线图,散点图]},xAxis: [{type: category,data: [周一,周二,周三,周四,周五,周六…

智能优化算法应用:基于萤火虫算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于萤火虫算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于萤火虫算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.萤火虫算法4.实验参数设定5.算法结果6.参考文…

官宣 | HelpLook已入驻企业微信应用市场

HelpLook正式入驻企业微信第三方应用市场。 HelpLook支持自定义域名与AI站内搜索&#xff0c;能够帮助企业微信用户搭建所见即所得的企业知识库、产品帮助中心、用户手册、企业博客。 | 怎么找到HelpLook并开始使用 在企业微信的第三方应用就可直接搜索HelpLook&#xff0c;添…

mysql数据库损坏后重装,数据库备份

重装 先卸载 sudo apt-get remove --purge mysql-server mysql-client mysql-common sudo apt-get autoremove sudo apt-get autoclean 然后重新安装MySQL: sudo apt-get install mysql-server mysql-client 首先要先使用无密码登录数据库一定要使用 sudo mysql -uroo…

新手上路:盘点「性能测试」必须掌握的技术点

前段时间&#xff0c;有一些小伙伴提出希望我们推送点性能测试的技术干货。所以&#xff0c;小编今天通过上网查资料&#xff0c;结合项目实操过程中的一些问题&#xff0c;总结了一些关于性能测试的内容&#xff0c;希望是大家想要了解的内容哈。 1、性能测试的目的 首先&am…

分布式环境认证和授权-基于springboot+JWT+拦截器实现-实操+源码下载

1、功能概述&#xff1f; 1、当用户登录的时候&#xff0c;将用户的信息通过JWT进行加密和签名&#xff0c;并将JWT产生了token信息保存到当前浏览器的localStoragee中,即本地存储中。 2、当用户登录成功后&#xff0c;访问其他资源的时候&#xff0c;程序从localStorage中获…

linux(4):linux基础命令第三弹

在linux基础命令第二弹中http://t.csdnimg.cn/JPNYY我们讲了有关路径&#xff0c;创建目录和文件、文件夹&#xff0c;以及如何查看文件内容的问题&#xff0c;第三弹我们将学习有关文件操作和查找以及过滤关键字、展示文件字节&#xff0c;行数的命令&#xff0c;还有一个很重…

【程序员的自我修养04】目标文件生成可执行文件过程

绪论 大家好&#xff0c;欢迎来到【程序员的自我修养】专栏。正如其专栏名&#xff0c;本专栏主要分享学习《程序员的自我修养——链接、装载与库》的知识点以及结合自己的工作经验以及思考。编译原理相关知识本身就比较有难度&#xff0c;我会尽自己最大的努力&#xff0c;争…

.Net中的集合

所有的集合都是继承自IEnumerable。集合总体可以分为以下几类&#xff1a;关联/非关联型集合&#xff0c;顺序/随机访问集合&#xff0c;顺序/无序集合&#xff0c;泛型/非泛型集合&#xff0c;线程集合。 各集合类底层接口关系图 泛型与非泛型集合类的分析 泛型集合是类型安…

自动化测试基础知识:什么是自动化测试?需要学习哪些知识与工具!

1、自动化测试概念 自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。通常&#xff0c; 在设计了测试用例并通过评审之后&#xff0c;由测 试人员根据测试用例中描述的规程一步步执行测试&#xff0c;得到实际结果与期望结果的比较。简言之&#xff0c;自动化测试…

【操作系统导论】比例份额调度

本文介绍一种 比例份额&#xff08;proportional-share&#xff09; 调度程序&#xff0c;也称为 公平份额&#xff08;fair-share&#xff09;。 彩票调度 简介 彩票调度 的基本思想&#xff1a; 每隔一段时间&#xff0c;都会举行一次彩票抽奖&#xff0c;以确定接下来应该…

【上海大学数字逻辑实验报告】六、时序电路

一、 实验目的 掌握同步二进制计数器和移位寄存器的原理。学会用分立元件构成2位同步二进制加计数器。学会在Quartus II上设计单向移位寄存器。学会在Quartus II上设计环形计数器。 二、 实验原理 同步计数器是指计数器中的各触发器的时钟脉冲输入端连接在一起&#xff0c;接…

做题总结 707. 设计链表

做题总结 707. 设计链表 leetcode中单链表节点的默认定义我的尝试正确运行的代码&#xff08;java&#xff09; leetcode中单链表节点的默认定义 class ListNode {int val;ListNode next;//无参public ListNode() {}//有参:1public ListNode(int val) {this.val val;}//有参:…

【项目小结】优点分析

一、 个人博客系统 一&#xff09;限制强制登录 问题&#xff1a;限制用户登录后才能进行相关操作解决&#xff1a; 1&#xff09;前端&#xff1a; ① 写一个函数用于判断登录状态&#xff0c;如果返回的状态码是200就不进行任何操作&#xff0c;否则Ajax实现页面的跳转操作…