【c++】类和对象(三)构造函数和析构函数

Alt

🔥个人主页:Quitecoder

🔥专栏:c++笔记仓

Alt

朋友们大家好,本篇文章我们带来类和对象重要的部分,构造函数和析构函数

目录

  • 1.类的6个默认成员函数
  • 2.构造函数
    • 2.1构造函数其他特性
  • 3.构析函数
    • 3.1特性:

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

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

任何类在什么都不写时,编译器会自动生成以下6个默认成员函数(用户没有显式实现,编译器会生成的成员函数称为默认成员函数)

class Date {};

在这里插入图片描述

2.构造函数

我们看下面这个类

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(2005, 6, 23);d1.Print();Date d2;d2.Init(2024, 3, 25);d2.Print();return 0;
}

对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,并且容易忘记那能否在对象创建时,就将信息设置进去呢?

构造函数是一种特殊的成员函数,它在创建对象时自动调用,其主要目的是初始化对象。在C++中,构造函数具有与其所属类相同的名称,并且没有返回类型。构造函数可以有参数,也可以没有参数,允许通过不同的方式初始化对象的成员变量。如果一个类定义中没有显式地包含任何构造函数编译器会自动生成一个默认构造函数(只在没有其他任何构造函数时)

特性:

  1. 函数名与类名相同
  2. 无返回值
  3. 对象实例化时编译器自动调用对应的构造函数
  4. 构造函数可以重载

那么上面所示的代码构造函数如何写呢?如下:

class Date
{
public:Date(){_year = 1;_month = 2;_day = 3;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;	d1.Print();return 0;
}

这种是不带参数的
在这里插入图片描述
在我们进行实例化 Date d1;时,自动调用构造函数完成初始化,我们可以用汇编代码进行查看:
在这里插入图片描述

我们也可以在其中加入带参数的构造函数,实现函数重载

class Date
{
public:Date(){_year = 1;_month = 2;_day = 3;}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;
};

我们知道,构造函数是实例化调用的,那么如何实现带参数构造函数呢?

代码如下:

 Date d1; // 调用无参构造函数Date d2(2005, 1, 1); // 调用带参的构造函数

注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明

在这里插入图片描述

Date d3();

能不能这样定义呢?
在这里插入图片描述
这里编译错误,即这里并不能与函数的声明区分开,所以书写格式严格按照上述方法来写

如果我们将第一个无参格式屏蔽掉呢?

class Date
{
public:/*Date(){_year = 1;_month = 2;_day = 3;}*/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;	d1.Print();return 0;
}

在这里插入图片描述
代码中出现错误的原因在于,为Date类定义了一个接收三个参数的构造函数,但是没有定义默认构造函数(无参数构造函数)。接着,在main函数中,尝试使用无参数的方式构造d1对象:Date d1;。这在类定义中是非法的,因为一旦定义了自己的构造函数(不管有多少参数),C++编译器就不会自动生成默认构造函数

我们这里也可以通过缺省参数来实现:
在这里插入图片描述
在这里插入图片描述

十分好用

2.1构造函数其他特性

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

class Date
{
public:/*// 如果用户显式定义了构造函数,编译器将不再生成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;d1.Print();return 0;
}

在这里插入图片描述
这个默认生成的函数并没有做什么事情

我们可能会产生疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??

C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,默认生成的构造函数,对内置类型不做处理,自定义类型会去调用它的默认构造函数

我们看下面这串代码:

class A
{
public:A(){cout << "A()" << endl;_a = 0;}
private:int _a;
};class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private://内置类型int _year ;int _month ;int _day;//自定义类型A _aa;
};
int main()
{Date d1;return 0;
}

默认生成的构造函数,对内置类型不做处理,自定义回去调用他的默认构造
在这里插入图片描述
我们发现调用了A的构造
在这里插入图片描述

C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。

class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}	
private:		int _year =1;int _month =2;int _day;A _aa;
};

在声明的位置给缺省值在这里还是声明
在这里插入图片描述

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

思考下面代码能否编译成功?

class Date
{
public:Date(){_year = 1900;_month = 1;_day = 1;}Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};
// 以下测试函数能通过编译吗?
int main()
{Date d1;return 0;
}

答案是不可以

Date类中定义了两个构造函数,看起来目的是提供一个默认构造函数和一个带默认参数值的构造函数。然而,这里的设计存在冲突,因为两个构造函数都可以作为默认构造函数,这导致了一个重定义的问题

在C++中,如果构造函数的所有参数都有默认值,它就可以被视为无参数调用时的候选构造函数,也就是说,它可以被当作默认构造函数。因此,这个类设计在逻辑上等同于提供了两个默认构造函数,这在C++中是不允许的,会导致编译错误

问题在于,当尝试创建一个不传递任何参数的Date对象(如Dated1;),编译器将无法确定应该调用哪个构造函数,因为两个构造函数都满足调用条件

3.构析函数

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?

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

3.1特性:

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

写法如下:

typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:DataType* _array;int _capacity;int _size;
};
int main()
{Stack st1;st1.Push(1);st1.Push(2);return 0;
}

其中:

~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}

为析构函数,我们定义一个栈,如果不写析构函数,则会发生内存泄漏

在这里插入图片描述

c语言中,我们主动调用Destroy函数

关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数

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;
}

在这里插入图片描述
在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类

本节内容到此结束!感谢大家观看!!!

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

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

相关文章

c语言函数大全(C开头)

c语言函数大全(C开头) There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog content is all parallel goods. Those who are worried about being cheated should leave quickly. 函数名…

sql——对于行列转换相关的操作

目录 一、lead、lag 函数 二、wm_concat 函数 三、pivot 函数 四、判断函数 遇到需要进行行列转换的数据处理需求&#xff0c;以 oracle 自带的表作为例子复习一下&#xff1a; 一、lead、lag 函数 需要行列转换的表&#xff1a; select deptno,count(empno) emp_num from…

MongoDB 入门简介

什么是 MongoDB&#xff1f; MongoDB 是一个基于分布式文件存储的开源数据库系统。它是一个 NoSQL&#xff08;Not only SQL&#xff0c;意为不仅仅是SQL&#xff09;数据库&#xff0c;使用文档&#xff08;BSON格式&#xff0c;类似于JSON&#xff09;来存储数据。MongoDB 以…

【工具】DataX 数据同步工具

简介 DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、Hologres、DRDS, databe…

基于java+springboot+vue实现的图书借阅系统(文末源码+Lw+ppt)23-328

摘 要 伴随着我国社会的发展&#xff0c;人民生活质量日益提高。于是对系统进行规范而严格是十分有必要的&#xff0c;所以许许多多的信息管理系统应运而生。此时单靠人力应对这些事务就显得有些力不从心了。所以本论文将设计一套“期待相遇”图书借阅系统&#xff0c;帮助商…

代码随想录训练营第55天 | LeetCode 583. 两个字符串的删除操作、​​​​​​LeetCode 72. 编辑距离、总结

目录 LeetCode 583. 两个字符串的删除操作 文章讲解&#xff1a;代码随想录(programmercarl.com) 视频讲解&#xff1a;LeetCode&#xff1a;583.两个字符串的删除操_哔哩哔哩_bilibili 思路 ​​​​​​LeetCode 72. 编辑距离 文章讲解&#xff1a;代码随想录(programm…

哪些行业需要在线制作电子证书系统?

哪些行业需要在线制作电子证书系统&#xff1f; 1、教育机构&#xff1a;学校和培训机构需要为学生和培训者颁发证书&#xff0c;您的系统可以帮助他们快速生成和管理这些证书。 2、企业及政府部门&#xff1a;用于员工培训、资质认证等&#xff0c;提高内部管理效率。 3、专…

小白如何兼职赚得第一桶金?六大网络赚钱方式让你轻松开启副业之旅

小白如何兼职赚得第一桶金&#xff1f;六大网络赚钱方式让你轻松开启副业之旅 无需担忧&#xff0c;以下是一些精心挑选的线上兼职建议&#xff0c;将助你迅速开启赚钱之旅。 1&#xff0c;参与网络调查&#xff1a;各大市场调研公司及品牌商常常需要了解消费者心声&#xff0c…

在JavaScript中垂直过滤

垂直过滤是一种常见的数据处理技术&#xff0c;通过该技术可以筛选出符合特定条件的数据并进行展示。在JavaScript中&#xff0c;我们可以利用数组方法和条件判断语句来实现垂直过滤功能。下面是一个简单的示例&#xff0c;演示如何利用JavaScript实现一个基本的垂直过滤功能。…

06|Java集合框架初学者指南:List、Set与Map的实战训练

Java集合框架是Java语言的核心部分,它提供了丰富的类和接口,用来高效地管理和操作大量数据。这个强大的工具箱包括多种集合类型,其中最为常用的是List、Set和Map。 1.List - 有序且可重复的数据清单 概念: List就像一个购物清单,你可以按照加入顺序存放和检索项目,而且同…

[BT]BUUCTF刷题第7天(3.25)

第7天 Web&#xff08;共5题&#xff09; [BJDCTF2020]Easy MD5 打开网站发现只有一个输入框&#xff0c;F12后也没有明显提示&#xff0c;但是在数据包中看到Hint&#xff1a;select * from admin where passwordmd5($pass,true)&#xff0c;意思是在admin表中查找password为…

oracle切换ADG后JVM组件查询报错ORA-29516处理

近期&#xff0c;某用户将数据库系统从EXADATA切换到普通X86 LINUX架构服务器上运行时&#xff0c;使用JAVA组件时报错ORA-29516: Aurora assertion failure: Assertion failure at jol.c:11157 joez mt-index botch; mt_index 65535, vtbl_len 12, static_len 2 对于此报错…

Java中的代理模式(动态代理和静态代理)

代理模式 我们先了解一下代理模式&#xff1a; 在开发中&#xff0c;当我们要访问目标类时&#xff0c;不是直接访问目标类&#xff0c;而是访问器代理类。通过代理类调用目标类完成操作。简单来说就是&#xff1a;把直接访问变为间接访问。 这样做的最大好处就是&#xff1a…

吴恩达机器学习-可选实验室:Softmax函数

文章目录 CostTensorflow稀疏类别交叉熵或类别交叉熵祝贺 在这个实验室里&#xff0c;我们将探索softmax函数。当解决多类分类问题时&#xff0c;该函数用于Softmax回归和神经网络。 import numpy as np import matplotlib.pyplot as plt plt.style.use(./deeplearning.mplstyl…

面向低成本巡线机器人的PID控制器优化——文末源码

目录 介绍 测试 电子元器件 系统特征 控制器设计 位置误差的计算 比例控制 积分控制 微分控制 改进的PID控制器 测试轨迹 源码链接 本文对经典PID控制器的改进和开环控制机制的发展进行了讨论&#xff0c;以提高差动轮式机器人的稳定性和鲁棒性。为了部署该算法&am…

【DP】动态规划基本解题步骤(求解台阶问题)

dp数组的定义和下标递推公式dp数组如何初始化&#xff0c;初始化也需要注意遍历顺序打印dp数组&#xff08;出现问题 对于高度为 n 的台阶&#xff0c;从下往上走&#xff0c;每一步的阶数为 1&#xff0c;2&#xff0c;3 中的一个。问要走到顶部一共有多少种走法 分析&#…

python中良好的编码规范

遵循PEP 8的常见规范&#xff1a; 缩进&#xff1a; 使用4个空格来缩进代码块&#xff0c;而不是使用制表符。 命名规范&#xff1a; 变量名应该使用小写字母&#xff0c;单词之间用下划线 _ 分隔&#xff08;snake_case&#xff09;。类名应该使用驼峰命名法&#xff08;Camel…

C++ 模板知识大全

模板 泛型编程 我们如何实现一个交换函数 我们实现了两种类型的交换函数&#xff0c;但是其实除了类型不一样&#xff0c;其他地方都是一样的。 void swap(int& a, int& b) {int tmp a;a b;b tmp; }void swap(char& a, char& b) {int tmp a;a b;b tmp…

关于DCMM评估的办理条件你知道多少?

DCMM&#xff08;数据管理能力成熟度评价模型&#xff09;评估划分为五个等级&#xff0c;自低向高依次为初始级、受管理级、稳健级、量化管理级和优化级&#xff0c;不同等级代表企业数据管理和应用的成熟度水平不同&#xff0c;证书自颁发之日起有效期3年 DCMM申报基础条件 …

香港科技大学(广州)先进材料学域可持续能源与环境学域智能制造学域博士招生宣讲会——北京专场(暨全额奖学金政策)

三个学域代表教授亲临现场&#xff0c;面对面答疑解惑助攻申请&#xff01;可带简历现场咨询和面试&#xff01; &#x1f4b0;一经录取&#xff0c;享全额奖学金1.5万/月&#xff01; 报名链接&#xff1a; https://www.wjx.top/vm/wF2Mant.aspx# 地点&#xff1a;中关村皇冠…