上两篇C++57个入门知识点_19_ 构造函数的作用及写法(作用:用于对象初始化,定义时候就定义对象初值;写法:函数名是类名;不写函数返回值;参数可以有也可以没有;使用:CStudent stu(“张三“),C++57个入门知识点_20_ 构造函数的调用(不同参数构造函数调用方法;默认无参构造函数;C++语法中的关键字:explicit显示调用、=default使用默认构造、=delete禁止使用某函数)我们介绍C++类的构造函数的相关内容,本篇开始将会介绍与构造函数紧密联系的函数-析构函数
总结:
C++中语法定义,主程序在结束前会自动跳入对象的析构函数,在析构函数中使用free()
函数就可实现对象资源的释放。
1. 析构函数定义:完成的资源的反初始化,资源的释放;
2. 析构函数写法: ~CStudent() {}
- 类名前加~
- 通常是由编译器决定调用时机,不需要手动调用
- 析构函数没有参数和返回值
3. 析构函数不能重载(结合函数重载需要满足函数名相同,参数列表不同分析)
4. 申请的内存,必须要使用free()函数释放掉,否则就会造成 内存泄漏
(内存没有得到有效释放)
5. 类中的成员变量char m_szName[255];
,代表了255个字节大小的char类型数组,为了想让其不是固定的大小,可以使用char* m_szName;
,指针代表指向的地址,不会限制住m_szName的大小。
1. 析构函数定义
构造函数用于完成对象的初始化(对象成员的赋值),析构是构造相反的过程,其作用为:完成的资源的反初始化,资源的释放
C++的语法就是在模仿人的动作行为,举例子:人有生就会有死,对象有创建的过程,也就会有消亡的过程。
利用以下代码分析:在CStudent stu1;
时就会跳入构造函数CStudent () {}
,完成成员的初始化。
#include <iostream>
class CStudent {
public:
CStudent() {
printf("CStudent()\r\n");
}
void SetName(const char* pszName)
{
strcpy_s(m_szName, pszName);//m_szName并未定义大小,存在溢出风险,系统提示使用strcpy_s
}
private:
int m_nStudID;//学号
char m_szName[255];//255个字节的缓冲区,姓名
};
int main(int argc, char* argv[])
{
CStudent stu1;
return 0;
}
我们对程序进行修改,类中的成员变量char m_szName[255];
,代表了255个字节大小的char类型数组,为了想让其不是固定的大小,可以使用char* m_szName;
,指针代表指向的地址,不会限制住m_szName的大小。
使用malloc()
函数申请动态堆内存,其使用可参考C语言基础入门48篇_46_malloc与free(malloc申请堆返回void要指针强转、free释放堆,只需堆内存首地址、malloc配合sizeof增加可读性、注意出{}作用域导致无法释放)
#include <iostream>
class CStudent {
public:
CStudent() {
//为了m_szNam创建一个堆空间(分配动态内存)
//c语言中在堆上创建动态内存,(char*)malloc(255)中255代表大小,malloc(255)返回一个void*,使用(char*)进行强转
m_szName = (char*)malloc(255);
}
void SetName(const char* pszName)
{ }
private:
int m_nStudID;
char* m_szName;
};
int main(int argc, char* argv[])
{
CStudent stu1;
return 0;
}
在构造函数中使用m_szName = (char*)malloc(255);
给char* m_szName;
动态分配了255个字节的堆空间内存
继续运行,到return 0
即main函数
结束的地方,堆内存依然存在,什么时候会不存在呢?
根据之前C语言中的知识可知,需要利用free()
函数进行释放
#include <iostream>
class CStudent {
public:
CStudent() {
//为了m_szNam创建一个堆空间(分配动态内存)
//c语言中在堆上创建动态内存,(char*)malloc(255)中255代表大小,malloc(255)返回一个void*,使用(char*)进行强转
m_szName = (char*)malloc(255);
free(m_szName);
}
void SetName(const char* pszName)
{ }
private:
int m_nStudID;//学号
char* m_szName;
};
int main(int argc, char* argv[])
{
CStudent stu1;
return 0;
}
运行后:内存得到释放,内存中的dd
代表了内存释放了
2. 析构函数写法及性质
2.1 析构函数写法: ~CStudent() {}
- 类名前加~
- 通常是由编译器决定调用时机,不需要手动调用
- 析构函数没有参数和返回值
2.2 析构函数不能重载(结合函数重载需要满足函数名相同,参数列表不同分析)
申请的内存,必须要使用free()函数释放掉,否则就会造成内存泄漏
(内存没有得到有效释放),问题就来了,什么时候调用free()
函数最为合适?
不再使用stu1对象
的时候去释放,但是如果让程序员去判断释放的时机就会显得十分麻烦,所以C++提供了一种语法,称为 析构函数
:在类的对象即将死亡时,就会调用析构函数。
#include <iostream>
class CStudent {
public:
CStudent() {
//为了m_szNam创建一个堆空间(分配动态内存)
//c语言中在堆上创建动态内存,(char*)malloc(255)中255代表大小,malloc(255)返回一个void*,使用(char*)进行强转
m_szName = (char*)malloc(255);
//free(m_szName);
}
~CStudent() {
printf("~CStudent()\r\n");
}
void SetName(const char* pszName){}
private:
int m_nStudID;//学号
char* m_szName;
};
int main(int argc, char* argv[])
{
CStudent stu1;
return 0;
}
F10单步调试:return 0
之后就会退出main()函数
,因此在这步之前需要进行析构
- return 0之前申请好动态内存
- 随后跳入析构函数
- 将
free(m_szName);
放入析构函数中是最合适的,也就可以实现内存的释放
3.学习视频地址:C++57个入门知识点_21_ 析构函数的调用
4.学习笔记:
#include <iostream>
//构造与析构
//析构函数
//1.作用:完成的资源的反初始化,资源的释放
//2.析构函数写法: ~CStudent() {}
// (1)类名前加~
// (2)通常是由编译器决定调用实际,不需要手动调用
// (3)析构函数没有参数和返回值
//3.析构函数不能重载
class CStudent {
public:
//无参构造函数
CStudent() {
//为了m_szNam创建一个堆空间(分配动态内存)
//m_szName = (char*)malloc(255);//c语言中在堆上创建动态内存
//free(m_szName);//c语言描述释放内存,内存没有得到有效的释放,就是内存泄漏
}
//析构函数
~CStudent() {
free(m_szName);//将参数的资源释放
}
void SetName(char* pszName)
{
//存在缓冲区溢出的风险,pszName并不知道指向的字符串的大小,若大于255,则可能将原来字符串末尾覆盖
//strcpy_s(m_szName,pszName);//m_szName并未定义大小,存在溢出风险,系统提示使用strcpy_s
}
private:
int m_nStudID;//学号
//指针代表指向的地址,不会限制住m_szName的大小,相对于预定义好的缓冲,避免了溢出的风险
char* m_szName;
};
int main(int argc,char* argv[])
{
CStudent stu;
return 0;
}
5.特别注意:
(1) 使用指针的好处:
//指针代表指向的地址,不会限制住m_szName的大小,相对于预定义好的缓冲,避免了溢出的风险
char* m_szName;
(2) C语言中堆上创建动态内存及释放:
//m_szName = (char*)malloc(255);//c语言中在堆上创建动态内存
//free(m_szName);//c语言描述释放内存,内存没有得到有效的释放,就是内存泄漏
(3) 析构函数的调用顺序:
在主函数return 0;之前进行析构