【哇! C++】类和对象(三) - 构造函数和析构函数

        

目录

一、构造函数

1.1 构造函数的引入

1.2 构造函数的定义和语法

1.2.1 无参构造函数:

1.2.2 带参构造函数

1.3 构造函数的特性

1.4 默认构造函数

二、析构函数

2.1 析构函数的概念

2.2 特性


        如果一个类中什么成员都没有,简称为空类。

        空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

        默认成员函数用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

class Date
{};

一、构造函数

1.1 构造函数的引入

        对于Date类,有如下程序:

#include<iostream>
using namespace std;class Date
{
public:void Init(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "_year" << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Init(2025, 3, 5)return 0;
}

         对于Date类,可以通过Init公有方法给对象设置日期。但有时也会出现忘记Init初始化函数,而直接Push。为避免忘记,也为了方便程序撰写,所以,C++规定了构造函数。

1.2 构造函数的定义和语法

        构造函数是一种特殊的成员函数。虽然叫构造,但是其任务不是开空间,而是初始化对象。其特征如下:

  1. 构造函数名与类名相同;
  2. 无返回值,不需要写void,void是空返回值;
  3. 对象实例化时编译器自动调用对应的构造函数;
  4. 构造函数可以重载。

所以Date类构造函数可写为:

1.2.1 无参构造函数:

#include<iostream>
using namespace std;class Date
{
public:    Date(){_year = 1;_month = 1;_day = 1;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Print();//Date d2();//err//Date d1(2024, 1, 27);//err,没有与参数列表匹配的构造函数实例return 0;
}

运行结果为: 

        此外,C++规定,无参构造函数变量名后不能加小括号。Date d2();有可能是函数的声明,返回类型是Date,因为这个地方要写函数声明的话,小括号中是要加参数类型的。

1.2.2 带参构造函数

#include<iostream>
using namespace std;class Date
{
public:Date(){_year = 1;_month = 1;_day = 1;}Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print{cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1(2025, 3, 5);return 0;
}

        构造函数可以函数重载,也可以改写为全缺省。

#include<iostream>
using namespace std;class Date
{
public:Date(){_year = 1;_month = 1;_day = 1;}Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print{cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;Date d2(2025, 3, 5);return 0;
}

        语法上,上述两个函数也可以同时存在,因为函数重载,函数名虽然相同,但参数不同。但是在调用Date d1;时,会产生歧义,编译器不知道要调用谁,是要调用无参的还是要调用全缺省的。所以一般情况下,我们不这样写。

1.3 构造函数的特性

        1.C++规定,对象定义(实例化)的时候,必须调用构造函数。

        2.构造函数是默认成员函数,默认成员函数的特征是:我们没有显示定义,编译器会自动生成一个无参的;如果写了,编译器就不会生成。

        但是,当我们这样写如下代码以后,编译器调用了默认生成的构造函数,但是什么也没干,没有初始化。

#include<iostream>
using namespace std;class Date
{
public:void func(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year = 1;int _month = 1;int _day;
};int main()
{Date d1;d1.Print();//A _aa;return 0;
}

运行过程为:

运行结果为:

        说明了使用编译器实现的默认构造函数出来初始化的数据是随机值,不是0。

        C++98规定,默认生成的构造函数,对于内置类型不做处理;对于自定义类型会直接调用他的默认构造函数。即编译器对生成默认了构造函数,对int不做处理,但对于自定义成员类型A aa;要调用A的构造函数。

        内置类型/基本类型       -       int/char/double/指针

        自定义类型                   -       struct/class

        C++11对这个语法进行补丁,在声明的位置给缺省值。即不给缺省值,编译器什么都不管;给缺省值,就默认生成了构造函数,用缺省值去完成初始化。在类中,默认了Date(){int _year = 1;int _month = 1;};

1.4 默认构造函数

#include<iostream>
using namespace std;class Date
{    
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year = 1;int _month = 1;int _day;
};int main()
{Date d1;return 0;
}

        当我们运行时发现上述代码报错:此处编译报错,提示没有默认构造函数可以用。

        很多人经常理解默认构造函数就是编译器生成的那一个。编译器默认生成的构造函数是默认构造函数,但只是其中之一。

        无参的构造函数全缺省构造函数也被称为默认构造函数,且默认构造函数有且只能有一个。 一般情况下,建议优选全缺省构造函数。

总结:不需要传参就可以调用的构造函数,都可以叫默认构造函数。

        所以,上述代码中,没有无参,也没有全缺省的默认构造函数,此时就需要编译器就要生成一个默认构造函数。

        但是编译器默认生成的构造函数是有条件的,基于默认成员函数的特性:即没有写显示定义的构造函数,编译器才会自动生成一个无参的默认构造函数;一旦用户显示定义,编译器就不会再生成。

        为了使上述程序能够通过,需要函数重载,显式定义一个默认构造函数。

#include<iostream>
using namespace std;class Date
{    
public:Date(){_year = 1;_month = 1;_day = 1;}Date(int year, int month, int day){_year = year;_month = month;_day = day;}Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year = 1;int _month = 1;int _day;
};int main()
{Date d1;return 0;
}

二、析构函数

2.1 析构函数的概念

        内存泄漏是不会报错的,所以经常会忘记destory,所以C++就有了析构函数。

        析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。相当于destory,把动态开辟的数组空间free掉,不清理的话会出现内存泄漏。所以:构造函数完成的不是创建,析构函数完成的也不是销毁。

        在函数名前加~,在C语言中,~表示按位取反,所以选用这个符号就表示和构造函数的功能是相反的。

2.2 特性

        析构函数是特殊的成员函数,其特性如下:

  1. 析构函数名是类名前加字符“~”;
  2. 没有参数也没有返回值;
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载;
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

        第4点类似于构造函数,构造函数在实例化的时候会自动调用。

#include<iostream>
using namespace std;class Time
{
public:~Time(){cout << "~Time()" << endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)int _year = 1970;int _month = 1;int _day = 1;// 自定义类型Time _t;
};int main()
{Date d;return 0;
}

        程序运行结束后输出:~Time()。

        在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?

        因为:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year,_month,_day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。

        但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数。

        注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数。

        如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如
Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

#include<iostream>
using namespace std;class Stack
{
public:    Stack(int capacity = 3){cout << "Stack(int capacity = 3)" << endl;_arry = (int*)malloc(capacity * sizeof(4));if(_arry == NULL){perror("malloc");}_capacity = capacity;}void Push(int x){_arry[_size] = x;_size++;}~Stack(){cout << "~Stack()" << endl;free(_arry);_arry = NULL;_size = 0;_capacity = 0;}private:int* _arry;int _capacity = 0;int _size = 0;
};//注意必须要有分号class MyQueue
{
private:Stack st1;Stack st2;int _size = 0;
};//注意必须要有分号int main()
{MyQueue q;return 0;
}

运行结果为: 

2.3 析构顺序        

        销毁顺序为:局部对象(后定义的先析构)->局部的静态->全局对象(后定义的先析构)。程序证明如下:

class Date
{
public:Date(int year = 1){_year = year;}~Date(){cout << "~Date()->" << _year << endl;}
private:int _year ;int _month;int _day;
};void func()
{Date d3(3);static Date d4(4);
}Date d5(5);
static Date d6(6);int main()
{Date d1(1);Date d2(2);func();return 0;
}

        static Date d3(3);为局部静态,和上边两个的存储区域不同,第3是存在静态区的,虽然定义在了局部,但是生命周期是全局的。在main函数结束以后才会销毁。所以在这之前要先把main函数中的局部变量先销毁。

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

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

相关文章

【五.LangChain技术与应用】【11.LangChain少样本案例模板:小数据下的AI训练】

深夜的创业孵化器里,你盯着屏幕上的医疗AI项目,手里攥着仅有的97条标注数据——这是某三甲医院心内科攒了三年的罕见病例。投资人刚刚发来最后通牒:“下周demo要是还分不清心肌炎和感冒,就撤资!” 这时你需要掌握的不是更多数据,而是让每个样本都变成会复制的孙悟空的毫毛…

2005-2019年各省城镇人口数据

2005-2019年各省城镇人口数据 1、时间&#xff1a;2005-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;地区、年份、城镇人口(万人) 4、范围&#xff1a;31省 5、指标解释&#xff1a;‌城镇人口是指居住在城市、集镇的人口&#xff0c;主要依据人群…

Anaconda 部署 DeepSeek

可以通过 Anaconda 环境部署 DeepSeek 模型&#xff0c;但需结合 PyTorch 或 TensorFlow 等深度学习框架&#xff0c;并手动配置依赖项。 一、Anaconda 部署 DeepSeek 1. 创建并激活 Conda 环境 conda create -n deepseek python3.10 # 推荐 Python 3.8-3.10 conda activate…

Python 面向对象高级编程-定制类

目录 __str__ __iter__ __getitem__ __getattr__ __call__ 小结 看到类似__slots__这种形如__xxx__的变量或者函数名就要注意&#xff0c;这些在Python中是有特殊用途的。 __slots__我们已经知道怎么用了&#xff0c;__len__()方法我们也知道是为了能让class作用于len()…

MCP与RAG:增强大型语言模型的两种路径

引言 近年来&#xff0c;大型语言模型&#xff08;LLM&#xff09;在自然语言处理任务中展现了令人印象深刻的能力。然而&#xff0c;这些模型的局限性&#xff0c;如知识过时、生成幻觉&#xff08;hallucination&#xff09;等问题&#xff0c;促使研究人员开发了多种增强技…

IDEA Generate POJOs.groovy 踩坑小计 | 生成实体 |groovy报错

一、无法生成注释或生成的注释是null 问题可能的原因&#xff1a; 1.没有从表里提取注释信息&#xff0c;修改def calcFields(table)方法即可 def calcFields(table) {DasUtil.getColumns(table).reduce([]) { fields, col ->def spec Case.LOWER.apply(col.getDataType().…

ue5.5崩溃报gpu错误快速修复注册表命令方法

网上已经有很多方法了&#xff0c;自己写了个regedit比处理dos批处理命令&#xff0c;启动时需要win 管理员身份拷贝后&#xff0c;将以下代码&#xff0c;保存为 run.bat格式批处理文件&#xff0c;右键鼠标&#xff0c;在弹出菜单中&#xff0c;选择用管理员身份运行。即可。…

能量石[算法题]

题目来源&#xff1a;第十五届蓝桥杯大赛软件赛省赛Java 大学 B 组&#xff08;算法题&#xff09; 可以参考一下&#xff0c;本人也是比较菜 不喜勿喷&#xff0c;求求求 import java.util.Scanner;​public class Main {public static void main(String[] args) {Scanner s…

马尔科夫不等式和切比雪夫不等式

前言 本文隶属于专栏《机器学习数学通关指南》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见《机器学习数学通关指南》 正文 统计概率的利剑&#xff1a;掌…

基于 STC89C52 的 8x8 点阵显示汉字

一、引言 在电子信息显示领域,汉字的直观呈现为信息传递带来极大便利。8x8 点阵虽显示空间有限,但通过合理设计,能够清晰展示一些常用、简单的汉字,丰富电子设备的交互界面。STC89C52 单片机作为一款经典且应用广泛的微控制器,以其成本低廉、易于开发的特性,成为驱动 8x…

二进制、八进制、十进制和十六进制间的转换(原理及工程实现)

在计算机科学和编程中&#xff0c;进制转换是一个非常重要的基础知识。无论是二进制、八进制、十进制还是十六进制&#xff0c;它们在不同的场景中都有广泛的应用。本文将详细介绍常用进制之间的转换方法&#xff0c;并附上C语言示例代码&#xff0c;帮助大家更好地理解和掌握这…

从零开始的 Kafka 学习(二)| 集群启动

1. 相关概念 1.1 代理&#xff1a;Broker 使用Kafka前&#xff0c;我们都会启动Kafka服务进程&#xff0c;这里的Kafka服务进程我们一般会称之为Kafka Broker 或 Kafka Server。因为Kafka是分布式消息系统所以再实际的生产环境中&#xff0c;是需要多个服务进程形成集群提供消…

python如何随机产生一堆数字并输出

python随机产生一堆数字并输出的方法&#xff1a; 通过for循环语句多次执行for循环里面的“random.randint()”函数产生随机数。将产生的随机数赋值给变量&#xff0c;输出这个变量就可以了 执行结果如下&#xff1a;

vue3与react、 react hooks

一、Vue3新特性&#xff1a;setup、ref、reactive、computed、watch、watchEffect函数、生命周期钩子、自定义hooks函数、toRef和toRefs、shallowReactive 与 shallowRef、readonly 与 shallowReadonly、toRaw 与 markRaw、customRef、provide 与 inject、Fragment、Teleport、…

《基于WebGPU的下一代科学可视化——告别WebGL性能桎梏》

引言&#xff1a;科学可视化的算力革命 当WebGL在2011年首次亮相时&#xff0c;它开启了浏览器端3D渲染的新纪元。然而面对当今十亿级粒子模拟、实时物理仿真和深度学习可视化需求&#xff0c;WebGL的架构瓶颈日益凸显。WebGPU作为下一代Web图形标准&#xff0c;通过显存直存、…

宠物医疗对接DeepSeek详细方案

基于DeepSeek本地化部署技术与医疗场景优化实践 一、核心架构设计 1. 本地化部署与数据安全 私有化服务器部署:将DeepSeek模型部署在宠物医院本地服务器,所有诊疗数据(如宠物病历、影像报告)均存储于院内,避免云端传输风险数据加密机制:采用AES-256加密算法对医疗数据加…

K8s 1.27.1 实战系列(一)准备工作

一、主机规划与硬件要求 1、节点数量 至少需要 3 台服务器(1 台 Master 节点,2 台 Worker 节点)。本地测试可缩容:若仅用于测试,可缩减为 1 个 Master 和 1 个 Worker,但需注意稳定性风险。2、硬件配置 ​Master 节点:建议 2 核 CPU、8GB 内存、80GB 硬盘。​Worker 节…

2.PSCAD是什么软件?

PSCAD&#xff08;Power Systems Computer Aided Design&#xff09;是一款功能强大的电力系统仿真软件&#xff0c;广泛应用于电力系统的建模、仿真和分析。它结合了电磁暂态仿真引擎EMTDC&#xff08;Electromagnetic Transients including DC&#xff09;&#xff0c;能够精…

Stable Diffusion模型Pony系列模型深度解析

Stable Diffusion模型Pony系列模型深度解析 一、技术架构与核心特性 基于SDXL的深度优化 Pony系列模型以SDXL为基础框架&#xff0c;通过针对二次元/动漫风格的微调&#xff0c;强化了在该领域的生成能力&#xff0c;同时保留了对写实场景的兼容性‌。其训练数据特别侧重于人…

FastGPT 引申:混合检索完整实例

文章目录 FastGPT 引申&#xff1a;混合检索完整实例1. 各检索方式的初始结果2. RRF合并过程3. 合并后的结果4. Rerank重排序后5. 最终RRF合并6. 内容总结 FastGPT 引申&#xff1a;混合检索完整实例 下边通过一个简单的例子说明不同检索方式的分值变化过程&#xff0c;假设我…