欢迎来到CILMY23的博客
本篇主题为: 构造函数、析构函数在局部、静态以及全局的调用和释放顺序
个人主页:CILMY23-CSDN博客
系列专栏:Python | C++ | C语言 | 数据结构与算法
感谢观看,支持的可以给个一键三连,点赞关注+收藏。
目录
写在前头:在讲解构造函数和析构函数在局部、全局、静态的顺序前,我们先回顾一下C/C++中的内存管理
本篇主要研究静态区(数据段),局部,和全局之间的调用顺序,并且以学生类为例进行测试。
一、局部域中的调用和释放顺序
为了让学生类能够被我们调用和释放的时候识别,我们将做一些优化:
我们使用全缺省的构造函数来进行区分,给予ID值
class Student
{
public:
Student(int ID = 1)
{
_ID = ID;
cout << "Student()" << _ID << endl;
}
~Student()
{
cout << "~Student()" << _ID << endl;
}
private:
int _ID;
};
在main域里:
int main()
{
Student stu1(1);
Student stu2(2);
Student stu3(3);
return 0;
}
结果:
解释:
int main()中的对象创建顺序:
- 首先 main 函数开始执行,stu1和stu2,stu3会在创建时调用对应的构造函数。
- 这些局部变量在 main 函数结束时会自动销毁,它们会自动调用对应的析构函数,析构函数会在 main 的作用域结束后被调用。
因此顺序是:
main 函数开始 --> stu1 构造 --> stu2 构造 -->stu3 构造 --> main 函数结束 --> stu3 析构 --> stu2 析构 -->stu1 析构
我们发现后定义的会先析构,满足"后进先出",构造的顺序是从先定义先构造,
二、局部和静态
我们现在将 stu3 对象加上关键字 static ,static 会影响 stu3 对象的生命周期,它是一个静态变量,分配在全局/静态数据区域。它的生命周期从第一次进入其定义的作用域开始,并在程序终止时结束。
int main()
{
Student stu1(1);
Student stu2(2);
static Student stu3(3);
return 0;
}
结果:
我们发现,局部优先调用对应的析构函数,满足“后定义先析构”,静态则是最后调用对应的析构函数进行资源清理。
那如果涉及一个函数呢?
void func()
{
Student stu3(3);
static Student stu4(4);
}
int main()
{
Student stu1(1);
Student stu2(2);
func();
return 0;
}
结果:
解释:
int main()中的对象创建顺序:
- 首先 main 函数开始执行,stu1和stu2会在创建时调用对应的构造函数。(这些局部变量在 main 函数结束时会自动销毁,它们会自动调用对应的析构函数,析构函数会在 main 的作用域结束后被调用。)
- 然后是调用func()函数,在 func 函数中,创建了两个 Student 对象:stu3 和 stu4。
- 局部对象stu3 是一个局部变量,(它在 func 的作用域内创建,并在 func 结束时销毁)
- 而 stu4 是一个静态变量,这意味着它的生命周期从首次创建开始,持续到程序结束。其构造函数在 func 首次调用时被执行,而析构函数直到程序结束才会被调用。
因此顺序是:
main 函数开始 --> stu1 构造 --> stu2 构造 --> func 被调用 --> stu3 构造 --> stu4 构造 --> func 结束 --> stu3 析构 --> main 函数结束 --> stu1 析构 --> stu2 析构 --> 程序结束--> stu4 析构
所以,如果有函数还是一样的,局部会优先调用对应的析构函数,满足"后进先出",静态最后调用析构函数
三、局部变量、全局变量、静态变量的生命周期和析构顺序
我们分别在函数的定义前后都增加了全局对象,stu5 , stu7,静态对象 stu6
Student stu5(5);
static Student stu6(6);
void func()
{
Student stu3(3);
static Student stu4(4);
}
Student stu7(7);
int main()
{
Student stu1(1);
Student stu2(2);
func();
return 0;
}
结果:
解析:
全局域:
- Student stu5(5):这是在全局范围内的对象,在程序开始时就会创建,直到程序结束时才销毁。
- static Student stu6(6):这是一个全局静态对象,它的构造函数会在程序开始时调用,它的生命周期与程序相同,直到程序结束时才销毁。
- Student stu7(7):这个对象在全局范围内,和 stu5 类似,在程序开始时创建,在程序结束时销毁。
main 函数域:
- Student stu1(1) 和 Student stu2(2):这是在 main 函数中的局部变量,它们在 main 函数开始时创建,并在 main 结束时销毁。
func 函数域:
- Student stu3(3):这是 func 函数中的局部变量,它的构造函数在 func 被调用时创建,并在 func 结束时销毁。
- static Student stu4(4):这是 func 中的静态变量,它的构造函数在 func 第一次调用时创建,并在程序结束时销毁。
因此顺序是:
stu5 构造 --> stu6 构造 --> stu7 构造 --> main 函数开始 --> stu1 构造 --> stu2 构造 --> func 被调用 --> stu3 构造 --> stu4 构造 --> func 结束 --> stu3 析构 --> main 函数结束 --> stu2 析构 --> stu1 析构 --> 程序结束--> stu4 析构 --> stu7 析构 --> stu6 析构 --> stu5 析构
总结:
- 在局部域中,后定义的先析构,满足"后进先出",先定义的先构造
- 局部和静态:局部优先调用对应的析构函数,满足"后进先出",静态最后调用析构函数
- 局部,静态,全局:构造顺序是从 全局、静态 --> 局部、静态,析构顺序是 局部 --> 全局、静态("后进先出")
感谢各位同伴的支持,本期构造函数、析构函数、静态函数以及全局的调用和释放顺序就讲解到这啦,如果你觉得写的不错的话,可以给个一键三连,点赞关注+收藏,若有不足,欢迎各位在评论区讨论。