C++析构函数的使用--异常安全
2009-05-13 18:59
295 查看
第一个例子:
void f()
{
FILE* __f;
__f=fopen(“filename”,”r+”);
if ( __f)
{
// file use ①
fclose(__f); //② 关闭文件
}
}
如果在//①file use过程中出现了错误,抛出了异常,程序就会异常回退,不会执行到②,文件也就没有关闭。解决这类问题可以使用异常处理:
void f()
{
FILE* __f;
__f=fopen(“filename”,”r+”);
if(__f)
{
try
{
// file use ①
}
catch(...)
{
fclose(__f); //② 关闭文件
throw; // 重新抛出异常
}
fclose(__f);//② 关闭文件
}
}
这种方案的缺点:
1.影响性能
2.背离异常设计的原则,异常处理代码和正常的代码没有很好的分离。只不过是:
if(error)
{
//错误处理
}
的偷懒写法。
在delphi 、java、 C#中引入了finally关键字,使这种设计方案更为流行;C#中的using只不过简化了try finally的写法。
更为合理的方案:
class file
{
FILE* __f;
Public:
file(char*filename,char* mode)
{
__f=fopen(filename,mode);
if (!__f) throw exception("open error");// 文件打开失败,抛出异常,表示类构造失败,这样在异常回退中就不会调用析构函数
}
~file()
{
fclose(__f); // 析构函数,关闭文件
}
operator FILE* ()
{
return __f;
}
};
void f()
{
file f1(“filename”,”r+”); //如果这边抛出异常,表示f1没有构造成功,就不会调用f1的析构
//①use file
};
如果f1构造成功,不管//①use file会不会出错,f1离开它的做用域时都将调用它的析构函数。
当然真正编码中,应该使用标准库中的ifstream和ofstream类。
第二个例子:
class A{
char* s;
int length;
public:
A(int n):length(n){
s=new char
;
}
A(const A&);
A& operator=(const A& a){ //①
if(this!=&a){
delete[] s;
length=a.length;
s=new char[length]; //②
//...
}
}
~A(){
delete[] s;
}
};
在①赋值运算符重载中,如果②抛出异常(bad_alloc),已经delete[] s
,但是析构函数还会再一次delete[]s
。这当然不是我们想要的。重新设计①:
A& operator=(constA& a){
if(this!=&a){
char* p;
p=new char[length];
//...
length=a.length;
delete[] s;
// delete 操作是不会抛异常的
s=p;
}
}
void f()
{
FILE* __f;
__f=fopen(“filename”,”r+”);
if ( __f)
{
// file use ①
fclose(__f); //② 关闭文件
}
}
如果在//①file use过程中出现了错误,抛出了异常,程序就会异常回退,不会执行到②,文件也就没有关闭。解决这类问题可以使用异常处理:
void f()
{
FILE* __f;
__f=fopen(“filename”,”r+”);
if(__f)
{
try
{
// file use ①
}
catch(...)
{
fclose(__f); //② 关闭文件
throw; // 重新抛出异常
}
fclose(__f);//② 关闭文件
}
}
这种方案的缺点:
1.影响性能
2.背离异常设计的原则,异常处理代码和正常的代码没有很好的分离。只不过是:
if(error)
{
//错误处理
}
的偷懒写法。
在delphi 、java、 C#中引入了finally关键字,使这种设计方案更为流行;C#中的using只不过简化了try finally的写法。
更为合理的方案:
class file
{
FILE* __f;
Public:
file(char*filename,char* mode)
{
__f=fopen(filename,mode);
if (!__f) throw exception("open error");// 文件打开失败,抛出异常,表示类构造失败,这样在异常回退中就不会调用析构函数
}
~file()
{
fclose(__f); // 析构函数,关闭文件
}
operator FILE* ()
{
return __f;
}
};
void f()
{
file f1(“filename”,”r+”); //如果这边抛出异常,表示f1没有构造成功,就不会调用f1的析构
//①use file
};
如果f1构造成功,不管//①use file会不会出错,f1离开它的做用域时都将调用它的析构函数。
当然真正编码中,应该使用标准库中的ifstream和ofstream类。
第二个例子:
class A{
char* s;
int length;
public:
A(int n):length(n){
s=new char
;
}
A(const A&);
A& operator=(const A& a){ //①
if(this!=&a){
delete[] s;
length=a.length;
s=new char[length]; //②
//...
}
}
~A(){
delete[] s;
}
};
在①赋值运算符重载中,如果②抛出异常(bad_alloc),已经delete[] s
,但是析构函数还会再一次delete[]s
。这当然不是我们想要的。重新设计①:
A& operator=(constA& a){
if(this!=&a){
char* p;
p=new char[length];
//...
length=a.length;
delete[] s;
// delete 操作是不会抛异常的
s=p;
}
}
相关文章推荐
- Exception C++中讨论的异常安全堆栈的,封装内存管理的版本,使用私有继承的方式复用
- More Effective C++----异常 & (9)使用析构函数防止资源泄漏
- 如何编写异常安全的C++代码
- C++析构函数的定义和使用
- 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获
- C++ 异常安全
- C++箴言:防止因异常而离开析构函数
- C++箴言:争取异常安全的代码
- 【中级】C++析构函数抛出异常
- c++对象的异常安全
- [链接]如何编写异常安全的C++代码
- C++析构函数使用虚函数原因
- c++异常安全和copy and swap策略
- C++之构造函数、析构函数抛出异常的问题
- 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接 异常解决方法
- 开始使用C++异常
- C++中禁止异常信息传递到析构函数外
- 如何编写异常安全的C++代码
- C++构造函数和析构函数中抛出异常的注意事项
- C/C++问答(3):关于构造和析构函数使用多态