C++初阶类与对象(二):详解构造函数和析构函数

上次为类与对象开了一个头:C++初阶类与对象(一):学习类与对象、访问限定符、封装、this指针
今天就来更进一步


文章目录

  • 1.类的6个默认成员函数
  • 2.构造函数
    • 2.1引入和概念
    • 2.2构造函数特性
      • 2.2.1特性1~4
      • 2.2.2注意
      • 2.2.3特性5~7
      • 2.2.4注意
  • 3.析构函数
    • 3.1概念
    • 3.2特性


1.类的6个默认成员函数

请添加图片描述

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

空类中并不是什么都没有,任何类在什么都不写时,编译器会自动生成上面6个默认成员函数

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

今天就来主要聚焦于构造函数和析构函数


2.构造函数

2.1引入和概念

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(2023, 10, 19);d1.Print(); return 0;
}

每次实例化对象后,都要自己进行初始化(使用成员函数Init),但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,时不时还会忘记初始化。所以构造函数来了:

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

2.2构造函数特性

2.2.1特性1~4

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

其特征如下:

  1. 函数名与类名相同
  2. 无返回值(不是写void,就是真真正正的没有)。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载
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();  这样不能调用无参初始化Date d1;//调用无参构造函数d1.Print(); Date d2(2024, 1, 3);//调用带参构造函数d2.Print();return 0;
}

2.2.2注意

  • 调用无参初始化时不能加上括号:加上后与函数声明无法区分开
  • 有参构造函数使用全缺省可以代替无参构造函数,此时虽然二者依然构成重载,但是初始化对象不知道调用哪一个。此时只需要一个全缺省构造函数就可以
class Date
{
public: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;//调用带参构造函数,全缺省d1.Print();Date d2(2024, 1, 3);//调用带参构造函数d2.Print();Date d3(2024);//传部分也行d3.Print();return 0;
}

请添加图片描述

2.2.3特性5~7

  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成

如果Date类有自己定义的构造函数,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成默认构造函数

class Date
{
public://此时没有进行显示构造函数定义,会使用系统默认生成的无参构造函数void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;     // 年int _month;    // 月int _day;      // 日
};int main()
{Date d1;//调用默认构造函数d1.Print();return 0;
}

请添加图片描述

为什么是随机值呢?这就牵扯到了特性6

  1. 关于编译器生成的默认成员函数,大家会有疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用(上面那个例子都是随机值)?d1对象调用了编译器生成的默认构造函数,但是d1对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用?

解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认构造函数,对于内置类型(如int、float、指针等),默认构造函数不会对其进行任何初始化操作,这意味着它们的值将是未定义的,可能是随机的

class Stack
{
public:Stack(int capacity = 3){_a = (int*)malloc(sizeof(int) * capacity);assert(_a);_capacity = capacity;_top = -1;}private:int* _a;int _top;int _capacity;//这三个是内置类型
};class Queue
{private:Stack _push;//这两个栈都是自定义类型Stack _pop;
};int main()
{Stack s1;Queue q1;return 0;
}

请添加图片描述

可以看到两个自定义类型的Stack变量都进行了初始化,而且调用了Stack的构造函数,如果Stack没有给构造函数,那Queue也是随机值了

  1. 无参的构造函数全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数

2.2.4注意

  • C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值,可以用来对内置类型进行初始化
  • 指针都是内置类型(Date d1也是)*
class Stack
{
public://Stack(int capacity = 3)//{//	_a = (int*)malloc(sizeof(int) * capacity);//	assert(_a);//	_capacity = capacity;//	_top = -1;//}private:int* _a=nullptr;int _top=-1;int _capacity=1;//这三个是内置类型
};int main()
{Stack s1;return 0;
}

请添加图片描述


3.析构函数

3.1概念

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

3.2特性

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

  1. 析构函数名是在类名前加上字符 ~
  2. 无参数无返回值类型(跟构造函数一样)。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时(出了作用域),C++编译系统系统自动调用析构函数
class Date
{
public://此时没有进行显示构造函数定义,会使用系统默认生成的无参构造函数void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << "调用了析构函数";}private:int _year=1;     // 年int _month=1;    // 月int _day=1;      // 日
};int main()
{Date d1;d1.Print();return 0;
}

请添加图片描述

  1. 关于编译器自动生成的析构函数,是否会完成一些事情呢?编译器生成的默认析构函数,对自定类型成员调用它的析构函数。对内置类型不进行处理
class Time
{
public:~Time(){cout << "调用了time的析构函数" << endl;}
private:int _hour;int _minute;int _second;
};class Date
{
private:// 基本类型(内置类型)int _year = 1;int _month = 1;int _day = 1;// 自定义类型Time _time;
};int main()
{Date d1;return 0;
}
  • 程序运行结束后输出:调用了time的析构函数

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

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

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

  • main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数

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

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

好啦,这次的内容就先到这里啦!期待下次与大家分享!!!

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1pao3hba2ho4v

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

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

相关文章

Linux网络--- SSH服务

一、ssh服务简介 1、什么是ssh SSH&#xff08;Secure Shell&#xff09;是一种安全通道协议&#xff0c;主要用来实现字符界面的远程登录、远程复制等功能。SSH 协议对通信双方的数据传输进行了加密处理&#xff0c;其中包括用户登录时输入的用户口令&#xff0c;SSH 为建立在…

OpenCV实战:控制手势实现无触摸拖拽功能

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 今天来学习一下如何使用OpenCV来控制手势&#xff0c;瞬间提升操作体验&#xff01;跨越界限&#xff0c;OpenCV手势控制拖拽功能现身。 一、主要步骤及库的功能介绍 1.主要步骤 要实现本次实验&#xff0c;主要步骤如下…

Ubuntu 22.04安装使用easyconnect

EasyConnect 百度百科&#xff0c;EasyConnect能够帮助您在办公室之外使用公司内网的所有系统及应用。在您的公司部署深信服远程应用发布解决方案后&#xff0c;您的公司所有业务系统及应用都可以轻松迁移至移动互联网上。您可以通过手机、PAD等智能移动终端随时随地开展您的业…

CodeReview 小工具

大家开发中有没有遇到一个版本开发的非常杂&#xff0c;开发很多个项目&#xff0c;改动几周后甚至已经忘了自己改了些什么&#xff0c;领导要对代码review的时候&#xff0c;理不清楚自己改过的代码&#xff0c;只能将主要改动的大功能过一遍。这样就很容易造成review遗漏&…

牛客月赛86+cf(edu)好题

思路&#xff1a;前缀和双指针 代码&#xff1a; #include <bits/stdc.h> using namespace std; using i64 int64_t; int main() {cin.tie(nullptr)->sync_with_stdio(false);cout << fixed << setprecision(20);int t 1;for (int ti 0; ti < t; …

展锐T618_虎贲T618紫光展锐安卓核心板规格参数

基于紫光展锐八核T618平台的纯国产化方案&#xff0c;采用了开放的智能Android操作系统&#xff0c;并集成了4G网络、2.5G5G双频WIFI(可支持1*1 MIMO)、BLUETOOTH近距离无线传输技术以及GNSS无线定位技术。用户可以根据特定场合的需求&#xff0c;选择合适的嵌入式ARM核心模块&…

Linux命令手册

简介 Multics&#xff08;大而全&#xff09;项目失败&#xff0c;吸取教训启动Unix&#xff08;小而精&#xff09;&#xff0c;Linus Benedict Torvalds受Unix启发开发初始版本Linux内核&#xff0c;Git也由其开发&#xff0c;目的是为了更好的管理Linux内核开发。Unix是商业…

linux第一个小程序 --- 进度条【简洁】

行缓冲区的概念 结果&#xff1a;先输入hello world然后休眠三秒后结束 当去掉’\n“ 后&#xff0c;结果就变成了先休眠三秒&#xff0c;然后打印hello world后结束。 该现象就证明了缓冲区的存在。 当缓冲区中遇到’‘\n’或者缓冲区被写满后才会被打印出来&#xff0c;在第…

Linux系统:一些趣味小命令

目录 1、小火车 2、小牛 3、随机小趣物 4、开机自启出现 5、其他趣味图可复制 5.1 文字版 5.2 宗教信仰 5.3 手势 5.4 宇宙星际 5.5 其他 前提&#xff1a;先安装epel额外源 [rootlocalhost ~]#yum install epel-release.noarch -y 1、小火车 [rootlocalhost ~]#y…

JS封装本地缓存的设置,读取,移除,清空方法及使用示例

我封装了一个JS通用的缓存管理对象&#xff0c;可以提供缓存的设置&#xff0c;读取&#xff0c;移除&#xff0c;清空操作&#xff0c;使用也很方便&#xff0c;封装方法的代码在最下方。 Q: 为什么不直接用原生的缓存方法&#xff0c;要封装&#xff1f; A1:原生的缓存管理…

Qt网络通信

1. UDP通信 1.1 udp通信的基本流程 创建套接字 绑定套接字 进行通信 关闭套接字 涉及到的类和信号 QUdpSocket&#xff1a;Udp套接字类&#xff0c;类对象就是一个udp套接字对象 QHostAddress&#xff1a;ip地址类 void readyRead()&#xff1a;信号&#xff0c;当有数据到达可…

[足式机器人]Part2 Dr. CAN学习笔记- Kalman Filter卡尔曼滤波器Ch05-3+4

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Kalman Filter卡尔曼滤波器 Ch05-34 3. Step by step : Deriation of Kalmen Gain 卡尔曼增益/因数 详细推导4. Priori/Posterrori error Covariance Martix 误差协方差矩阵 3. Step by step :…

gh0st远程控制——客户端界面编写(一)

1、新建一个基于对话框的MFC程序 ghost内核对unicode支持不好&#xff0c;所以不要勾选 在静态库使用MFC有助于我们的代码供别人使用 2、设置窗口可最大最小化 对话框 》右键属性 3、 为对话框添加列表 一个代表列表框架&#xff0c;一个代表日志框架 分别为2个控件添加唯…

Umi3 创建,配置环境,路由传参(代码示例)

目录 创建项目 配置环境 创建脚手架 项目结构及其目录、 路由 配置路由 嵌套路由 编程式导航和声明式导航 声明式导航 编程式导航 约定式路由 路由传参 query传参&#xff08;问号&#xff09; 接收参数 params传参&#xff08;动态传参&#xff09; 接收参数 创…

精品量化公式——“筹码动态”,筹码动态改进版,增加了三个买点信号标识

不多说&#xff0c;直接上效果如图&#xff1a; ► 日线表现 代码评估 技术指标代码评估&#xff1a; 散筹估算: 使用EMA&#xff08;指数移动平均&#xff09;方法计算(WINNER(C*1.1)-WINNER(C*0.9))*70的3日均线&#xff0c;用黄色粗线表示。这是用于估算市场中散户投资者的…

【我与Java的成长记】之多态,重载与重写详解

系列文章目录 能看懂文字就能明白系列 C语言笔记传送门 Java笔记传送门 &#x1f31f; 个人主页&#xff1a;古德猫宁- &#x1f308; 信念如阳光&#xff0c;照亮前行的每一步 文章目录 系列文章目录&#x1f308; *信念如阳光&#xff0c;照亮前行的每一步* 前言一、多态的概…

gitlab 部署项目新分支

公司代码管理平台新切换到gitlab下&#xff0c;上线发版流程随之变更 1新建分支&#xff0c;开发完成&#xff0c;提交新分支 2.去gitlab平台上找到Merge requests 3 点击右上角的New merge request select source branch 选择新建的分支 点击 compare branches and contin…

navigateTo失效-跳转不了页面解决办法!uniapp\vue

改了一个小时多的错误&#xff0c;跳转页面无论怎么样都跳转不了&#xff0c;有2个问题&#xff1a; 注意&#xff1a;uniapp的报错可以在console里检查&#xff01; 1.pages.json文件没有配置路径&#xff0c; 在pages:[ ]里面加 &#xff08;根据自己的路径进行修改 {&qu…

d2l包安装教程

目录 一、下载d2l包 1、错误的安装方法 2、正确的安装方法 二、可能会遇到的问题 1、网络超时导致下载中断 2、windows powershell激活虚拟环境时报错 一、下载d2l包 直接按照教程安装 — 动手学深度学习 2.0.0 documentation运行命令pip install d2l0.17.6安装会比较慢&…

如何实现 H5 秒开?

我在简历上写了精通 H5&#xff0c;结果面试官上来就问&#xff1a; 同学&#xff0c;你说你精通 H5 &#xff0c;那你能不能说一下怎么实现 H5 秒 由于没怎么做过性能优化&#xff0c;我只能凭着印象&#xff0c;断断续续地罗列了几点&#xff1a; 网络优化&#xff1a;http2、…