当前位置: 首页 > news >正文

C++类和对象(中):构造函数与析构函数的核心逻辑

一.类的默认成员函数

默认成员函数就是用户没有显式实现,编译器会自动生成的成员函数。一个类,我们不写的情况下编译器会默认生成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最后两个取地址重载不重要,我们稍微了解⼀下即可。其次就是C++11以后还会增加两个默认成员函数,移动构造和移动赋值,这个我们后面再分享。默认成员函数很重要,也比较复杂,我们要从两个方面去学习:

  • 第一:我们不写时,编译器默认生成的函数行为是什么,是否满足我们的需求。
  • 第二:编译器默认生成的函数不满足我们的需求,我们需要自己实现,那么如何自己实现?

二.构造函数 

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象(我们常使用的局部对象是栈帧创建时,空间就开好了),而是对象实例化时初始化对象。构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能,构造函数自动调用的特点就完美的替代的了Init。

构造函数的特点:

  1. 函数名与类名相同。
  2. 无返回值。 (返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)
  3. 对象实例化时系统会自动调用对应的构造函数。
  4. 构造函数可以重载。
  5. 如果类中没有显式定义构造函数,则C++编译器会自动生成⼀个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
  6. 无参构造函数、全缺省构造函数、我们不写构造时编译器默认生成的构造函数,都叫做默认构造函数。但是这三个函数有且只有⼀个存在,不能同时存在。无参构造函数和全缺省构造函数虽然构成函数重载,但是调用时会存在歧义。要注意很多人会认为默认构造函数仅仅是编译器默认生成的那个。实际上无参构造函数、全缺省构造函数也是默认构造函数,总结一下就是不传实参就可以调用的构造就叫默认构造。
  7. 我们不写,编译器默认生成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始化是不确定的,看编译器。对于自定义类型成员变量,要求调用这个成员变量的默认构造函数初始化。如果这个成员变量,没有默认构造函数,那么就会报错,我们要初始化这个成员变量,需要用初始化列表才能解决,初始化列表,我们后续再来学习了解。

特别说明:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的原生数据类型, 如:int/char/double/指针等,自定义类型就是我们使用class/struct等关键字自己定义的类型。

--上述的这些特点在后续的举例说明中都会体现出来的 

举例说明:(注意看注释)

#include<iostream>
using namespace std;class Date 
{
public:
//如果这里不自己显示构造函数,编译器会自动生成一个无参的默认构造函数
//默认构造函数:无参构造函数,全缺省构造函数,编译器默认生成的构造函数
//三种默认构造函数只能同时存在一个(第三个的原因不用多说,前两个是因为构成重载但传参啥都不传时会歧义)//1.自己实现的无参数的构造函数(默认构造函数的一种)Date()//函数名和类名相同,无返回值{_year = 1;_month = 1;_day = 1;}//2.带参构造函数(可以和1同时存在)(不能和3同时存在,函数签名相同,构成了重定义)(不属于默认构造函数)Date(int year, int month, int day){_year = year;_month = month;_day = day;}////3.全缺省构造函数(很好用,结合了1,2的功能)(但是不能和1或者2同时存在)(默认构造函数的一种)//Date(int year = 1, int month = 1, int day = 1)//{//	_year = year;//	_month = month;//	_day = day;//}//总结:两种使用方案:1+2或者直接用3void Print(){cout << _year << "/" << _month << "/" << _day << '\n';}
private:int _year;int _month;int _day;
};int main()
{Date d1; //会自动初始化,调用1或者3,不能同时存在(我上面给3注释掉了,因为2和3也不能同时存在)Date d2(2025, 7, 31);//会调用3,不会有歧义//Date d3();//这种写法是错误的// 参考一下 Data func();你觉得它是对象还是函数呢?//注意:如果通过无参构造函数创建对象时,对象后面不用跟括号。//否则编译器无法区分这里是函数声明还是实例化对象d1.Print();//1,1,1d2.Print();//2025,7,31return 0;
}

 我们不写,编译器默认生成的构造对内置类型初始化没要求:(看注释)

//我们不写,编译器默认生成的构造对内置类型初始化没要求
#include<iostream>
using namespace std;class Date
{
public://如果这里不自己显示构造函数,编译器会自动生成一个无参的默认构造函数void Print(){cout << _year << "/" << _month << "/" << _day << '\n';}
private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Print();//打印出来结果是随机值return 0;
}

--这个地方很奇怪,会让很多人怀疑这里编译器到底有没有自动生成一个默认构造函数,但是肯定是生成了的

我们不写,编译器默认生成的构造,对于自定义类型成员变量,要求调用这个成员变量的默认构造函数初始化:(注意注释) 

//我们不写,编译器默认生成的构造,对于自定义类型成员变量,要求调用这个成员变量的默认构造函数初始化
//比如之前我们写过的两个栈实现队列
#include<iostream>
using namespace std;
class Stack 
{
public:Stack(int n=4)//在这里还是要自己实现一个的,不然会出跟上面一样的问题,我们来定义一个全缺省的{_a = (int*)malloc(n * sizeof(int));if (_a == nullptr){perror("malloc fail!");exit(1);}_top = 0;_capacity = n;}
private://内置类型int* _a;int _top;int _capacity;
};class MyQueue
{
public://编译器默认生成MyQueue的构造函数调用了Stack的构造函数,完成了两个成员的初始化
private://自定义类型Stack _pushst;Stack _popst;//内置类型,但很奇怪,混在这里它却能处理,这里大家可以自己去试试//int size = 0;
};int main()
{MyQueue q;return 0;
}

调试可以看出全部都初始化了,而且我还发现这里自定义类型和内置类型混在一起时,将内置类型也处理了,大家可以自己试试。但我们还是严格按照定义来。

三.析构函数

析构函数与构造函数功能相反,析构函数不是完成对对象本身的销毁,比如局部对象是存在栈帧的,函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会自动调用析构函数,完成对象中资源的清理释放工作。析构函数的功能类比我们之前Stack实现的Destroy功能,就像Date没有Destroy,其实就是没有资源需要释放,所以严格说Date是不需要析构函数的。

析构函数的特点:

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值。 (这里跟构造类似,也不需要加void)
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  4. 对象生命周期结束时,系统会自动调用析构函数。
  5. 跟构造函数类似,我们不写编译器自动生成的析构函数对内置类型成员不做处理,自定类型成员会调用他的析构函数。
  6. 还需要注意的是我们显示写析构函数,对于自定义类型成员也会调用他的析构,也就是说自定义类型成员无论什么情况都会自动调用析构函数。
  7. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,如Date;如果默认生成的析构就可以用,也就不需要显示写析构,如MyQueue;但是有资源申请时(而且这里涉及内置类型的处理),一定要自己写析构,否则会造成资源泄漏,如Stack。
  8. 一个局部域的多个对象,C++规定后定义的先析构。

--上述这些特点在后续的举例说明中都会讲述 

举例说明:(注意看注释)

#include<iostream>
using namespace std;
class Stack 
{
public:Stack(int n=4)//在这里还是要自己实现一个的,不然会出跟上面一样的问题,我们来定义一个全缺省的{_a = (int*)malloc(n * sizeof(int));if (_a == nullptr){perror("malloc fail!");exit(1);}_top = 0;_capacity = n;}//析构函数(跟构造函数写法类似,前面加个~)//在这里也需要自己定义,不然他也不处理内置类型,会造成内存泄漏~Stack(){if (_a){free(_a);_a = nullptr;}_top = 0;_capacity = 0;}
private://内置类型int* _a;int _top;int _capacity;
};class MyQueue
{
public://编译器默认生成MyQueue的构造函数调用了Stack的构造函数,完成了两个成员的初始化//编译器默认生成MyQueue的析构函数调用了Stack的析构函数,释放的Stack内部的资源//显示写析构,也会自动调用Stack的析构/*~MyQueue(){}*/private://自定义类型Stack _pushst;Stack _popst;//内置类型,但很奇怪,混在这里它却能处理,这里大家可以自己去试试//int size = 0;
};int main()
{Stack s;MyQueue q;//析构不用显示写出来,对象生命周期结束时自动调用//后定义的先析构,所以这里先析构q,再析构s.可以调试观察return 0;
}

--调试观察到当对象生命周期结束时确实都销毁掉了,另外大家可以自己调试去观察一下后定义的先析构这个特点(其实跟栈有点像)

感兴趣的朋友可以写一下下面这道题:

四.括号匹配问题的优化(利用当前阶段所学知识)

对比一下用C++和C实现的Stack解决之前括号匹配问题isValid,我们会发现有了构造函数和析构函数确实方便了很多,不会再忘记调用Init和Destory函数了(下面的两个实现都需要借用一下栈这个数据结构先,我这里就不展现出来了)。

以前用C语言实现的:

bool isValid(char* s) {ST st;STInit(&st);char* pi = s;while (*pi != '\0'){if (*pi == '(' || *pi == '[' || *pi == '{'){STPush(&st, *pi);}else {//右括号取栈顶元素进行匹配//栈不为空才能取if (STEmpty(&st)){STDestory(&st);return false;}char top = STTop(&st);if ((top == '(' && *pi != ')')|| (top == '[' && *pi != ']')|| (top == '{' && *pi != '}')){STDestory(&st);return false;}//本次匹配就出栈STPop(&st);}pi++;}//为空有效,非空无效bool ret = STEmpty(&st) ? true : false;STDestory(&st);return ret;
}

用最新加了构造和析构的C++版本Stack实现:

bool isValid(char* s) {Stack st;char* pi = s;while (*pi != '\0'){if (*pi == '(' || *pi == '[' || *pi == '{'){st.Push(&st, *pi);}else {//右括号取栈顶元素进行匹配//栈不为空才能取if (st.Empty(&st)){return false;}char top = st.Top(&st);if ((top == '(' && *pi != ')')|| (top == '[' && *pi != ']')|| (top == '{' && *pi != '}')){return false;}//本次匹配就出栈st.Pop(&st);}pi++;}//栈为空,返回真,说明数量都匹配 左括号多,右括号少匹配问题return st.Empty();
}

对比图示如下:

http://www.dtcms.com/a/504355.html

相关文章:

  • 数据结构--顺序表的测试
  • Intel HD Graphics 2000可以在window 11 中使用openGL
  • 昆明网站建设网站一建论坛建工教育网
  • 建站宝盒设置wordpress 跳转到首页
  • PySide6 文本编辑(QTextEdit)查找功能进阶 读取并记录上一次状态
  • Vivado 增量综合检查点错误的排查与解决
  • 学院的网站怎么做自己怎么制作网页游戏
  • 哪家企业的网站做的好榆林微网站建设
  • 【嵌入式】环形缓冲区缓和读写速度差的原因分析
  • 聊城开发区建设局网站郑州聚商网络科技有限公司
  • 上海网站设计制作报价门头沟高端网站建设
  • 随州网站推广哪家权威织梦导航网站模板
  • Docker安装Elasticsearch、kibana、IK分词器
  • 银河麒麟V10 Docker安装
  • 聊城开发区建设局网站国家建设部官方网站赵宏彦
  • 十堰英文网站建设网站的空间域名
  • 【SpringCloud(6)】Gateway路由网关;zuul路由;gateway实现原理和架构概念;gateway工作流程;静态转发配置
  • 什么网站做简历模板网站开发用哪些技术
  • 电商网站主题html国庆节网页制作代码
  • 聊城网站制作公司wordpress通过标签获取文章
  • 备案添加网站网络服务提供商
  • 操作系统:进程的短程调度
  • 广西建设厅招投标中心网站wordpress怎么发文章
  • 做旅游攻略的网站代码获取客户信息的渠道有哪些
  • 包装设计网站排行榜前十名网站建设费按几年摊销
  • html5网站开发原理官网是怎么做的
  • 大学网站建设管理办法信息化网站建设的风险识别
  • 0基础学CV(6)|目标分割之DeepLabv3训练自己的数据集
  • 重庆网站建设哪家做的好2345官网
  • 5.1 传输层提供的服务 (答案见原书 P225)