C++的类与对象(三):构造函数、析构函数、对象的销毁顺序

目录

类的6个默认成员函数

构造函数

语法

特性

析构函数

特性

对象的销毁顺序​​​​​​​​​​​​​​


类的6个默认成员函数

问题:一个什么成员都没的类叫做空类,空类中真的什么都没有吗?

基本概念:任何类在什么都不写时,编译器会自动生成以下六个默认成员函数(无参的)

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

注意事项:如果我们自己实现了这些函数,那么编译器就不会生成默认的成员函数 

构造函数

产生原因:初始化容易被遗忘,且有时候会太麻烦

作用:完成初始化工作(Init)

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(2022, 7, 5);d1.Print();Date d2;d2.Init(2022, 7, 6);d2.Print();return 0;
}

        对于Data类,之前我们会通过Init公有方法给对象设置日期,但每次创建对象时都需要调用该方法设置信息就显得有点麻烦,C++的构造函数就可以做到在对象创建时,就将信息设置进去

语法

1、构造函数是特殊的成员函数

2、构造函数的主要任务是在类实例化对象时初始化对象,而不是去开空间创建对象

3、构造函数的函数名与所在类的类名相同(类名是A,构造函数的名字就叫A,客随主便)

4、构造函数无返回值(不是void,而是连void都没)

5、对象实例化时编译器会自动调用合适的构造函数(对象后没括号就调无参构造函数,有实参就调用带参构造函数)

#include <iostream>
using namespace std;class Date{public:// 1.无参构造函数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();return 0;
}

6、构造函数可以重载

#include <iostream>
using namespace std;class Date
{
public:// 1.无参构造函数Date(){_year = 1;_month = 1;_day = 1;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}// 2.带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{Date d1; // 调用无参构造函数d1.Print();Date d2(2015, 1, 1); // 调用带参的构造函数d2.Print();return 0;
}

7、构造参数没有形参时,对象实例化时不能在对象后加括号

8、 对象实例化时必须调用构造函数,没有或者没有合适的构造函数就会报错(d1原本想要调用无参的构造函数,但是只有带参的构造函数,有了带参的构造参数编译器就算像提供不带参的默认构造参数也没办法了)

#include <iostream>
using namespace std;class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}//带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};int main()
{Date d1;d1.Print();return 0;
}

9、无参的构造函数和全缺省的构造函数在语法上因为函数重载可以同时存在,但是在实践中不可以,会造成调用歧义

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

特性

1(重点)、实例化对象时我们不写构造函数(包括默认构造函数和普通的构造函数),编译器就会生成默认构造函数,从而初始化对象,我们写了构造参数编译器就不会生成默认构造参数(普通的构造参数是指带参的构造参数)

2、编译器生成的默认构造参数什么也不干,为什么?(我们程序员没有定义一个构造参数是我们的问题,但是你编译器既然为我们自动生成了一个默认构造参数,你总得让它起点作用吧,就是只让成员变量初始化为0也行啊)

#include <iostream>
using namespace std;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;
}

这是因为,C++将数据分为了内置(基本)类型和自定义类型:

  • 内置类型:语言自身定义的类型(int、char、double、任何类型的指针等)
  • 自定义类型:程序员根据自己需求定义的类型(struct、class等)

3、C++98规定编译器生成的默认构造函数(程序员没提过时),对内置类型不做处理,对自定义类型会去调用它的!!默认构造函数!!(一定是默认构造函数而不是普通的构造函数)

#include <iostream>
using namespace std;//编译器对自定义类型会调用它的默认构造
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;d1.Print();return 0;
}

大致步骤如下:
①Date d1:实例化对象d1

②Data{}:编译器自动生成默认构造函数,并开始对成员变量开始初始化 

③__aa:_aa的类型是A类,属于自定义类型,调用A类的提供的!默认!构造函数

如果A类也没提供默认构造函数,那么_aa也是随机值(虽然编译器也可以为A提供但是没用):

#include <iostream>
using namespace std;class A
{
public:int getA() // 添加一个公共方法以获取 _a 的值{return _a;}private:int _a;};class Date
{
public:void Print(){cout << "Year: " << _year << ", Month: " << _month << ", Day: " << _day << endl;cout << "Value of '_aa' member variable in class 'Date': " << _aa.getA() << endl; // 打印_aa对象内部_a变量的值}private:int _year;int _month;int	_day;A _aa;
};int main()
{Date d1;d1.Print();return 0;
}

④_year、_month、_day:是int类型,属于内置类型,不做任何操作,结果为随机值

为什么在调试时,先初始化对象_aa?

答:C++中,类的成员变量初始化顺序是由它们在类中声明的顺序决定的,而不是由它们在构造函数初始化列表中出现的顺序决定

注意事项:有些新的编译器会对内置类型也做处理,但是C++的标准没有规定 

4、C++11规定内置类型的成员变量声明时可给默认值(为C++98打补丁,没给的依然是随机值)

#include <iostream>
using namespace std;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 = 2024;int _month =3 ;int _day;A _aa;
};int main()
{Date d1;d1.Print();return 0;
}

5、 只有无参 / 全缺省构造函数、编译器默认生成的构造函数都叫默认构造函数,其余的都是构造函数而不是默认构造函数,默认构造函数只能有一个 

//默认构造参数只能有一个:
#include <iostream>
using namespace std;
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;
}

//成员变量在声明时未给缺省值
#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month , int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << this << endl;cout << " ~Date()" << endl;}private:int _year;int _month;int _day;};int main()
{Date d1;d1.Print();return 0;
}

//成员变量在声明时提供缺省值:
#include <iostream>
using namespace std;
class Date
{
public:Date(int year = 1, int month , int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << this << endl;cout << " ~Date()" << endl;}private:int _year = 1;int _month = 1;int _day =1;
};int main()
{Date d1;d1.Print();return 0;
}

为什么成员变量在声明时已经给了缺省值,为什么Data还要再给?

虽然为 Date 类中的私有成员变量 _year_month, 和 _day 提供了默认值(1, 1, 1),但这些缺省值仅在默认构造函数未初始化成员变量时才会被用于初始化成员变量。实例化d1时,类中定义了一个貌似是全缺省的构造函数,因此编译器不会自动生成默认构造函数(那个位置上已经有人了,只是那个人有点缺陷),且因为该貌似的全缺省构造函数缺少两个缺省值所以报错

#include <iostream>
using namespace std;class Time
{
public://带参构造函数,不是默认构造函数Time(int hour){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
private:int _hour;int _minute;int _second;
};class Date
{
private://声明给缺省值int _year;int _month;int _day;Time _t;
};int main() 
{Date d;return 0;
}

为什么会提示_t没有默认构造函数?

答:实例化对象d时,Date类没给构造函数,编译器自动生成默认构造函数(无参),_year、_month、_day是内置类型不做处理,_t是自定义类型会调用Time类的默认构造函数,但是Time类中已经有了一个带参的构造函数(普通构造函数)所以Time类中不会有默认构造函数了(只要我们写了构造函数,即使它是带参的普通构造函数,编译器就不会给我们提供默认构造参数了)故报错

 结论:绝大多数场景下都需要自己实现构造函数,且更推荐用全缺省构造函数​​​​​​​

析构函数

 产生原因:需要人为销毁的空间容易忘记销毁,内存泄漏可能不会报错

#include <iostream>
using namespace std;
class Date
{
public:void Print(){cout << _year << "-" << _month << "-" << _day << endl;}~Date(){cout << this << endl;cout << " ~Date()" << endl;}private://声明给缺省值int _year = 1;int _month = 1;int _day = 1;};void func()
{Date d2;
}class Stack
{
public:Stack(size_t capacity = 4){_array = (int*)malloc(sizeof(int*) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(int data){// CheckCapacity();_array[_size] = data;_size++;}//~Stack()//{//    cout << "~Stack()" << endl;//    if (_array)//    {//        free(_array);//        _array = nullptr;//    }//    _size = _capacity = 0;//}private:int* _array;int _capacity;int _size;
};int main()
{func();Date d1;Stack st1; //内存泄漏return 0;
}

        如果Stack类中没有自定义的析构函数就会出现内存泄漏,因为我们即没有定义Destory函数去销毁在堆上开辟的空间,析构函数也不起作用

作用:完成清理工作(Destory)

基本概念:析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的,而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

特性

1、析构函数名是在类名前加上字符~

2、析构函数无参数无返回值类型

3、一个类只能有一个析构函数,若未显示定义,编译器会自动生成默认的析构函数

4、析构函数不能重载(清理一遍数据就应该被清理完成)

5、对象生命周期结束(函数结束)时,程序会自动调用析构函数

#include <iostream>
using namespace std;
class Date
{
public:~Date(){cout << this << endl;cout << " ~Date()" << endl;}private://声明给缺省值int _year = 1;int _month = 1;int _day =1;
};void func()
{Date d2;//实例化对象d2
}//函数结束,调用析构函数销毁数据int main()
{func();  Date d1;//实例化对象d1return 0;
}//函数结束,调用析构函数销毁数据

6、编译器生成的析构函数,对内置成员变量不做处理,对自定成员变量会调用它的析构函数

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

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

答:main函数中创建了Date类的对象d,d中包含了四个成员变量,其中_year、_month、_day是内置类型的成员变量,销毁时不需要人为清理资源main函数结束后系统直接回收,而_t是Time类的对象,为了销毁_t就需要Data类的析构函数,但是我们并没有给Date类定义析构函数,所以编译器就会为Date类提供一个默认析构函数,该函数对于自定义类型的成员变量就会调用它(_t)的析构函数(Time提供的析构函数)

7、后定义的先析构(定义->开辟帧栈->压栈,栈后进先出)

#include <iostream>
using namespace std;
class Date
{
public:~Date(){cout << this << endl;cout << " ~Date()" << endl;}private://声明给缺省值int _year = 1;int _month = 1;int _day = 1;};class Stack
{
public:Stack(int capacity = 4){_array = (int*)malloc(sizeof(int*) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(int data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){cout << this << endl;cout << "~Stack()" << endl;if (_array){free(_array);_array = nullptr;}_size = _capacity = 0;}
private:int* _array;int _capacity;int _size;
};int main()
{Date d1;Stack st1;return 0;
}

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

 9、自定义类型的本质还是内置类型

对象的销毁顺序

从左至右:局部变量(后定义的先析构)——>局部的静态——>全局(后定义的先析构)

当成栈来理解,先进后出 

#include <iostream>
using namespace std;
class Date
{
public://全缺省构造函数Date(int year = 1){_year = year;}~Date(){cout << " ~Date()->" << _year << endl;}private://声明给缺省值int _year;int _month;int _day;};Date d5(5);//全局
Date d6(6);//全局
static Date d7(7);//全局
static Date d8(8);//全局void func()
{Date d3(3);//局部static Date d4(4);//局部的静态
}int main() 
{Date d1(1);//局部Date d2(2);//局部func();return 0;
}

#include <iostream>
using namespace std;
class Date
{
public://全缺省构造函数Date(int year = 1){_year = year;}~Date(){cout << " ~Date()->" << _year << endl;}private:int _year;int _month;int _day;};int main()
{Date d1(1);//局部Date d2(2);//局部static Date d3(3);//局部的静态return 0;
}

~over~

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

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

相关文章

2024年最便宜的阿里云服务器购买图文教程,2核2G61元,2核4G99元

2024年&#xff0c;阿里云推出了多款价格非常便宜的云服务器和轻量应用服务器&#xff0c;其中轻量应用服务器2核2G3M带宽50G系统盘只要61元/1年&#xff0c;2核4G4M带宽60G系统盘只要165元/1年。云服务器2核2G3M带宽40G系统盘只要99元/1年&#xff0c;2核4G5M带宽80G系统盘只要…

功能测试转自动化测试好不好转型?

手工测试做了好多年&#xff0c;点点点成了每天必须做的事情。但是随着自动化测试趋势的日渐明显&#xff0c;以及受到薪资、技能的双重考验&#xff0c;掌握自动化测试成为了必备技能。 手工转自动化测试&#xff0c;不是一蹴而就的。“预先善其事&#xff0c;必先利其器”&a…

FTP,SFTP,FTPS,SSL,TSL简介,区别,联系,使用场景说明

文章目录 简介FTPFTPSSFTP加密场景选择FTPS还是SFTPFTP、SFTP、FTPS区别、联系和具体使用场景如何使用FTP、SFTP和FTPSSSLTLSSSL和TLS区别和联系&#xff0c;以及使用场景SSL和TLS技术上的区别一些问题隐式的TLS&#xff08;FTPS/SSL&#xff09;或者显式的TLS&#xff08;FTPS…

LeetCode 707. 设计链表 (JAVA)

1.题目 2. 思路分析 1.我们要设置一个虚拟头节点&#xff0c;因为这个虚拟头节点对于增加节点操作和删除节点操作都很方便。 2.仔细读题&#xff0c;题目中说链表中的节点下标是从0开始的。也就是说第一个节点下标为0。 3.增加节点和删除节点的操作我们都要获取到它前一个的节…

洗衣洗鞋店小程序对接水洗唛打印,一键预约,支付无忧

随着社会的进步和科技的发展&#xff0c;我们的生活幸福感与日俱增。为了让我们从琐碎中解脱出来&#xff0c;干洗店洗鞋店行业也日新月异。今天&#xff0c;我为大家推荐这款优秀的干洗店小程序系统&#xff0c;让您的洗衣洗鞋服务体验更上一层楼。 干洗店管理系统是一款专为洗…

HTML静态网页成品作业(HTML+CSS)——家乡漳州介绍设计制作(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

css clip-path polygon属性实现直角梯形

2024.3.8今天我学习了如何用css实现直角梯形的效果&#xff0c; 效果&#xff1a; 具体实现原理&#xff1a; 一、需要三个div&#xff1a; 外面一个大的div&#xff0c;里面左右两个小的div 我们需要先把第一个div变成直角梯形&#xff1a; 大概是这样&#xff0c;设置好之…

conda PackagesNotFoundError

参考&#xff1a;https://wenku.csdn.net/answer/3u2f0vgdaj 报错&#xff1a;Solving environment: failed PackagesNotFoundError: The following packages are not available from current channels: 解决方法 ##添加channels conda config --add channels anaconda conda …

【CV论文阅读】【计算机视觉中的Transformer应用综述】(1)

0.论文摘要 摘要——自然语言任务的Transformer model模型的惊人结果引起了视觉社区的兴趣&#xff0c;以研究它们在计算机视觉问题中的应用。在它们的显著优点中&#xff0c;与递归网络例如长短期记忆&#xff08;LSTM&#xff09;相比&#xff0c;Transformer能够模拟输入序…

golang学习随便记16-反射

为什么需要反射 下面的例子中编写一个 Sprint 函数&#xff0c;只有1个参数&#xff08;类型不定&#xff09;&#xff0c;返回和 fmt.Fprintf 类似的格式化后的字符串。实现方法大致为&#xff1a;如果参数类型本身实现了 String() 方法&#xff0c;那调用 String() 方法即可…

C++ 有哪些流行的开发框架或库?

这是我在知乎上回答的一个问题&#xff0c;不到两周收藏数超过 500&#xff0c;点赞还不到 100&#xff0c;看来 C 程序员还是不少&#xff0c;且大家都想进步。 不过从“收藏”就是“学过”的这种风气来看&#xff0c;大概率只是扫一眼&#xff0c;然后放到收藏里吃灰了。 下面…

《手把手教你》系列技巧篇(三十)-java+ selenium自动化测试- Actions的相关操作下篇(详解教程)

1.简介 本文主要介绍两个在测试过程中可能会用到的功能&#xff1a;Actions类中的拖拽操作和Actions类中的划取字段操作。例如&#xff1a;需要在一堆log字符中随机划取一段文字&#xff0c;然后右键选择摘取功能。 2.拖拽操作 鼠标拖拽操作&#xff0c;顾名思义就是&#xff…

CubeMX使用教程(5)——定时器PWM输出

本篇我们将利用CubeMX产生频率固定、占空比可调的两路PWM信号输出 例如PA6引脚输出100Hz的PWM&#xff1b;PA7引脚输出500Hz的PWM&#xff0c;双路同时输出 我们还是利用上一章定时器中断的工程进行学习&#xff0c;这样比较方便 首先打开CubeMX对PA6、PA7进行GPIO配置 注&a…

SpringMVC03、HelloSpring

3、HelloSpring 3.1、配置版 新建一个Moudle &#xff0c; springmvc-02-hello &#xff0c; 添加web的支持&#xff01; 确定导入了SpringMVC 的依赖&#xff01; 配置web.xml &#xff0c; 注册DispatcherServlet <?xml version"1.0" encoding"UTF-8…

Linux操作系统-07-Linux安装应用

一、使用rpm安装应用&#xff08;不推荐&#xff09; 先下载到本地&#xff0c;以.rpm文件名结尾&#xff0c;下载完成后&#xff0c;再安装 rpm -qa | grep mysql #查询当前系统是否有下载过mysql包 先上传mysql的rpm安装包到linux的opt目录 安装 rpm -ivh …

[项目设计] 从零实现的高并发内存池(三)

&#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[高并发内存池] ❤️ 前置学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 ​ 目录 4.CentralCache实现 4.1 CentralCache整体架构 4.2 围绕Span的相关设计…

【代码随想录 | 数组 01】二分查找

文章目录 1.二分查找1.1题目1.2思路&#xff08;核心&#xff1a;区间的定义&#xff09;1.3左闭右闭1.4左闭右开1.5总结 1.二分查找 1.1题目 704.二分查找—力扣题目链接 题目&#xff1a;给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 …

C语言分析基础排序算法——插入排序

目录 插入排序 直接插入排序 希尔排序 希尔排序基本思路解析 希尔排序优化思路解析 完整希尔排序文件 插入排序 直接插入排序 所谓直接插入排序&#xff0c;即每插入一个数据和之前的数据进行大小比较&#xff0c;如果较大放置在后面&#xff0c;较小放置在前面&#x…

LeetCode-102.题: 二叉树的层序遍历(原创)

【题目描述】 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] 【题目链接…

【ICCV21】Swin Transformer: Hierarchical Vision Transformer using Shifted Windows

文章目录 0. Abstract1. Introduction2. Related Work3. Method3.1 Overall Architecture3.2 Shifted Window based Self-Attention3.3 Architecture Variants 4. Experiments4.1 Image Classification on ImageNet-1K4.2 Object Detection on COCO4.3 Semantic Segmentation o…