云计算百科
云计算领域专业知识百科平台

小学生学C++编程语法知识(C++构造函数、析构函数可以抛出异常吗?)


C++构造函数、析构函数可以抛出异常吗?

我们先回忆一下:

  • 构造函数:负责“出生”,创建一个对象。

  • 析构函数:负责“离开”,销毁一个对象,清理资源。


比如:

class Student
{
public:

Student()
{
cout << "学生出生了" << endl;
}

~Student()
{
cout << "学生毕业了" << endl;
}
};


程序:

Student s;

执行:

学生出生了

学生毕业了


一、什么是异常?

生活中:

小明去学校:

出门

坐公交

突然下大雨

无法到学校

“大雨”就是一种特殊情况。


程序中:

int a = 10;
int b = 0;

cout << a / b;

除数不能为 0。

程序遇到了无法正常处理的问题。

这种情况叫:

异常(Exception)


C++中使用:

throw

抛出异常:

throw 错误信息;


使用:

try
{

}
catch()
{

}

接住异常。


二、构造函数可以抛异常吗?

答案:

✅ 可以!

而且非常常见。


1. 为什么构造函数需要抛异常?

因为构造函数负责创建对象。

例如:

我们创建一个文件对象:

class File
{
public:

File(string name)
{
if(name=="")
{
throw "文件名为空";
}
}
};


使用:

try
{
File f("");
}
catch(const char* e)
{
cout << e;
}


输出:

文件名为空


为什么?

因为:

创建对象
|
|

发现无法创建
|
|

抛异常

这个对象根本没有成功出生。


三、构造函数抛异常后,对象存在吗?

这是重点。

答案:

❌ 不存在!


例如:

class Student
{
public:

Student()
{
cout<<"开始创建"<<endl;

throw "创建失败";
}

};

int main()
{
try
{
Student s;
}
catch(…)
{

}
}

执行:

开始创建

然后异常。

对象没有创建成功。

所以:

Student s;

不存在。


四、构造函数抛异常,会调用析构函数吗?

答案:

❌ 不会调用这个对象的析构函数

为什么?

因为:

构造失败

对象没有出生

没有死亡

不用析构

比如:

class A
{
public:

A()
{
cout<<"构造"<<endl;
throw 1;
}

~A()
{
cout<<"析构"<<endl;
}

};

int main()
{
try
{
A a;
}
catch(…)
{

}
}

输出:

构造

没有:

析构


五、但是已经创建成功的成员,会析构吗?

这里是一个重要知识。

看:

class B
{
public:

B()
{
cout<<"B构造"<<endl;
}

~B()
{
cout<<"B析构"<<endl;
}
};

class A
{

B b;

public:

A()
{
cout<<"A构造"<<endl;

throw 1;
}

~A()
{
cout<<"A析构"<<endl;
}

};

执行:

try
{
A a;
}
catch(…)
{

}

过程:

创建A
|
创建成员b
|
B成功出生
|
A构造函数失败
|
A没有创建成功
|
销毁已经创建的b

输出:

B构造
A构造
B析构

注意:

没有:

A析构

但是:

B析构


原因:

对象A没有完整出生。

但是它里面已经出生的成员,需要清理。


六、析构函数可以抛异常吗?

答案:

技术上:

✅ 可以


但是:

实际开发:

❌ 强烈不推荐!


为什么?

因为析构函数什么时候运行?

通常:

程序正常
|
对象离开作用域
|
析构

例如:

{
Student s;

}

离开大括号:

调用析构


但是如果:

析构函数里面抛异常:

~Student()
{
throw "错误";
}

会发生什么?


七、最危险情况:异常里面又遇到析构异常

看:

try
{
Student s;

throw "外部错误";

}
catch(…)
{

}

过程:

发生异常
|
开始寻找catch
|
需要销毁对象s
|
调用s析构函数
|
析构函数又throw
|
两个异常同时存在

C++不知道怎么办。

于是:

程序直接结束:

std::terminate()


简单理解:

两个小朋友同时喊:

小明:
老师,我有问题!

小红:
老师,我也有问题!

老师:

???

程序只能停止。


八、所以C++规定:

析构函数最好:

不要抛异常


原因:

析构函数的任务:

清理东西,不应该制造新的麻烦。


例如:

正确:

~File()
{
close();
}

错误:

~File()
{
throw "关闭失败";
}


九、现代C++推荐写法

C++11以后:

析构函数默认:

noexcept

意思:

这个函数保证不会抛异常。


例如:

class Student
{

public:

~Student() noexcept
{

}

};


如果析构函数真的可能失败怎么办?

不要:

~Student()
{
throw;
}

而应该:

提前处理:

student.close();


例如:

文件:

file.close();

网络:

socket.shutdown();

资源:

提前释放。


十、总结表

函数能否抛异常推荐
构造函数 ✅ 可以 常用
析构函数 ⚠️ 可以 不要
构造失败 对象不存在 不会调用自己的析构
成员已经创建 会调用成员析构 保证资源释放
析构抛异常 可能导致程序终止 避免

给小学生的一句话总结:

构造函数像“出生检查”,发现不能出生,可以报告错误(抛异常)。

析构函数像“打扫卫生”,最好安安静静完成任务,不要制造新的问题。

在C++工程中:

  • 构造函数抛异常:✅ 很正常

  • 析构函数抛异常:❌ 大忌


这也是为什么很多大型C++项目要求:

构造函数可以失败,析构函数必须成功。

这和前面讲的RAII资源管理思想(对象负责管理资源)是紧密相关的,也是C++面向对象非常核心的设计思想。


赞(0)
未经允许不得转载:网硕互联帮助中心 » 小学生学C++编程语法知识(C++构造函数、析构函数可以抛出异常吗?)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!