上篇:C++57个入门知识点_21_ 析构函数的调用(主程序结束前自动跳入对象析构函数,析构函数中使用free()函数就可实现对象资源的释放;内存泄漏:内存没有得到释放;不指定大小的数组定义方法:指针)介绍了程序中何时调用析构函数及使用析构函数可以实现对象资源的释放,本篇在上篇基础上进行知识补充。
总结:
- 构造和析构函数可以手动调用的,但是可能会存在重复构造和析构导致内存混乱的问题;
- 构造和析构函数的访问权限一般均为公有的;
- 构造和析构函数的调用时机:
局部对象
:构造-声明该对象时构造,析构-对象出{}作用域时调用析构;全局对象
:构造-进入到main函数之前,析构-出main函数之后;
C++中main函数是程序的入口点,实际上可以通过类的构造和析构,在main函数前后执行代码
1. 构造和析构函数的手动调用
上篇我们知道析构函数是编译器自动调用的,我们手动也是可以调用析构函数的,因为析构函数本身也是普通的函数,调用方法也是和普通的成员函数一致,使用方法如下:
int main(int argc, char* argv[])
{
CStudent stu1;
stu1.~CStudent();
return 0;
}
手动调用析构函数之后,编译器还会再次调用,但是会存在重复释放的问题
,报错如下:
因此一般情况下不需要手动调用析构函数。
2. 构造和析构函数的访问权限
通常情况下,构造与析构均为公有的。
前面我们知道,C++类中有访问权限的概念,在类外是否可以访问的区别,构造和析构一般都为公有的,是否可以私有呢?
有以下代码:
#include <iostream>
class CStudent {
private:
CStudent() {
//为了m_szNam创建一个堆空间(分配动态内存)
//c语言中在堆上创建动态内存,(char*)malloc(255)中255代表大小,malloc(255)返回一个void*,使用(char*)进行强转
m_szName = (char*)malloc(255);
}
~CStudent() {
free(m_szName);
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;
}
运行结果:在不创建对象的情况下编译通过,创建对象的情况下编译无法创建对象
3. 构造和析构函数的调用时机
构造和析构函数的调用时机分为两种情况,分别为
局部对象
和
全局对象
。
3.1局部对象的构造和析构的调用时机
我们知道构造和析构函数时编译器自动调用的,但是我们需要知道编译器什么时候会调用。
以下代码中,CStudent stu1;
是在int main(){}函数的块作用域内部
,是存放在栈上的,称为栈上的局部对象
。其构造和析构过程为:
- 构造-声明该对象时构造;
- 析构-对象出{}作用域时调用析构
例如main函数
中,块作用域{CStudent stu;}
中,一出块作用域,对象stu1
就会被析构
对以下代码进行单步调试:
#include <iostream>
class CStudent {
public:
CStudent() {
printf("CStudent()\r\n");
m_szName = (char*)malloc(255);
}
~CStudent() {
free(m_szName);
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;
}
运行结果:CStudent stu1;
声明时调用构造函数,在出包含CStudent stu1;
的{}
块作用域前调用析构函数。
3.2 全局对象的构造和析构的调用时机
根据全局变量的概念,在全局定义一个对象就被称为 全局对象
。
全局对象在哪都可能被使用,在运行完main之后才析构,属于来得早走得晚,其调用时机如下:
- 构造-进入到main函数之前;
- 析构-出main函数之后;
例如:全局对象CStudent g_Stu;
我们按照之前定义全局变量的方式,在类域外定义全局对象
#include <iostream>
CStudent g_Stu;
class CStudent {
public:
CStudent() {
printf("CStudent()\r\n");
m_szName = (char*)malloc(255);
}
~CStudent() {
free(m_szName);
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;
}
运行结果如下:
这是因为CStudent类
是定义在CStudent g_Stu;
之后的,编译器在一行行去扫描的时候,不认识CStudent g_Stu;
,将其放在类定义之后就可以了。
修改后代码如下:
#include <iostream>
class CStudent {
public:
CStudent() {
printf("CStudent()\r\n");
m_szName = (char*)malloc(255);
}
~CStudent() {
free(m_szName);
printf("~CStudent()\r\n");
}
void SetName(const char* pszName){}
private:
int m_nStudID;//学号
char* m_szName;
};
CStudent g_Stu;
int main(int argc, char* argv[])
{
printf("enter main\r\n");
printf("leave main\r\n");
return 0;
}
单步调试及运行结果如下:可以看到进入main(){}
之前就会构造,出main(){}
之后才会析构
C++中main函数是程序的入口点,实际上可以通过类的构造和析构,在main函数前后执行代码
。
4. 学习视频地址:C++57个入门知识点_22_ 构造函数和析构函数的调用时机