持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情
1️⃣前言
今天的笔记内容是:栈解旋与异常变量的生命周期
2️⃣栈解旋
🔋理解
异常被抛出后,从进入try块起到异常被抛掷前,这期间在栈上构造的所有对象都会被自动析构。析构的顺序和构造的顺序相反。这一过程被称为栈的解旋。
通俗来讲就是在try块中,当发生了异常并被抛出时,异常抛出前创建的对象都会被自动析构,不然的话创建的对象会一直存在,占用着内存。
🔋示例
看下代码会更加清楚
#include <iostream>
#include <string>
using namespace std;
class myClass{
public:
myClass(string name) {
m_name = name;
cout<< m_name << "对象被创建了!" << endl;
}
~myClass() {
cout<< m_name << "对象被析构了!" << endl;
}
string m_name;
};
void func1(){
myClass b("b");
myClass c("c");
throw -1; // 函数1抛出异常
}
void func2() {
myClass a("a");
func1(); // 调用函数1
}
int main()
{
try {
func2(); // 调用函数2
}
catch(int) {
cout << "这里是异常处理!" << endl;
}
return 0;
}
运行结果为:
3️⃣异常变量的生命周期
先来看一段代码:
#include <iostream>
#include <string>
using namespace std;
class myException{
public:
myException() {
cout<< "构造函数调用了!" << endl;
}
myException(const myException& e) {
cout<< "拷贝构造函数调用了" << endl;
}
~myException() {
cout<< "析构函数调用了!" << endl;
}
};
void func1(){
throw myException(); // 抛出异常匿名对象
}
void func2() {
try{
func1();
}
catch(myException myExcept){
cout << "异常捕获并处理!" << endl;
}
}
int main()
{
func2();
return 0;
}
运行结果如下:
分析如下:
- 主函数中调用了函数
func2(),执行流程就跳到func2()的函数体中;func2()中有try-catch语句块,其中try语句调用了func1()函数,所以执行流程又跳到了func1()的函数体中;func1()中抛出了自定义的异常类对象myException(),此时构造函数调用了;- 异常抛出后,执行流程就跳到
func2()中的catch语句块中,异常被catch捕获了;- 此时调用了拷贝构造函数,将匿名对象
myException()拷贝给了myExcept;- 接着是异常的处理(在上述代码中是输出语句的执行)
- 最后是栈解旋,即析构函数的自动调用,且析构的顺序和构造的顺序相反。
如果,在上述代码中,将catch中的捕获语句改为引用类型,如下所示:
void func2() {
try{
func1();
}
catch(myException& myExcept){
cout << "异常捕获并处理!" << endl;
}
}
则代码运行的结果如下:
也就是说没有调用拷贝构造函数。直接将抛出的异常匿名对象赋值给引用对象,也就是起别名。此时抛出的异常对象的生命周期就发生了变化。即生命周期交给引用对象所托管(这种效率会较高些)。
4️⃣写在最后
好了,本篇笔记就到写这,欢迎大家到评论区一起讨论!