【C/C++值班室】Exception思维

Jinhao 2004-07-11 09:35:48
您的代码中有Exception吗?作为C++中最具争议的东西,就连在使用上也不是一件容易的事,您怎么看待异常呢?
如果想判断一个对象是否构造成功,我们可以用以下的三种方法
1、
struct A
{
A(int& i)
{
//其他代码
if(构造失败)
i = 0;
else
i = 1;
}
};

int i;
A a(i);
if(i==0)
cout<<”构造失败”<<endl;

2、
class A
{
public:
A()
{
//其他代码
if(构造失败)
isok_ = false;
else
isok_ = true;
}
bool isok() const
{ return isok_; }
private:
bool isok_;
};

A a;
if(!a.isok())
cout<<”构造失败”<<endl;

3、
class my_exception: public std::exception{};

struct A
{
A()
{
//其他代码
if(构造失败)
throw my_exception();
}
};

try
{
A a;
}
catch(my_exception& ex)
{ cout<<”构造失败”<<endl; }

综观三种方法,我们来做个简单的分析。
第一种,该构造函数提供了一个用于返回错误的标志变量,虽然可以判断是否构造成功,但是这个多余的变量更像是一个累赘。
第二种,构造函数上没有歧义了。但是它并没有降低复杂度。更重要的是,它和第一种方法有个通病,那就是析构函数会被正常调用。换句话说,这样的对象构造失败并不是语言所支持的,而是程序员自己的逻辑规定。
第三种,这就完全没有上面两种方法的问题,在构造函数中抛出异常,就意味这这个对象未构造成功,这是被语言所支持的,这样一来,class A的析构函数将不会作用于对象a。这一特性可以让我们不再为这类安全性操心了。

在某些函数中,我们可以通过按值返回错误码,但在某些情况下这类方法并不顶用,这样我们就可以考虑异常。

从上面的表现来看,并没有体现出异常带来复杂度。但是,当遇到资源管理时,其中就有很多事情也许会被我们忽略。
void test()
{
int* p= new int;
//其他代码
delete p;
}

如果中间的代码抛出异常,最好的情况就是内存泄露及带来不安全因素。我们应该加入异常处理
void test()
{
int *p =NULL;
try
{
p = new int;
//其他代码
}
catch(...)
{
delete p;
throw; //异常中立,保证了前一版本test函数的行为
}
delete p;
}
但这也许并不算最爽的解决方案。我们可以利用RAII技巧。来简化这个操作
template<typename T>
class auto_new
{
public:
auto_new():ptr_(NULL)
{
try
{
ptr_ = new T;
}
catch(std::bad_alloc)
{
//异常处理
}
}
~auto_new()
{
delete ptr_;
}
operator T*()
{ return ptr_; }
private:
T* prt_;
}

void test()
{
auto_new<int> p;
//其他的代码
}
这样就不用担心异常发生时带来的资源回收问题。当然,对于简单的资源,我们可以采用auto_ptr<>。
也许各位看官会认为,这样的做法也并没有降低复杂度,似乎反而增大了工作量。是的,但是这样的代码可以使我们更放心。

异常也不是十全十美的,它自身也存在很多的缺陷,比如它的运行成本比较高,如果正常的控制结构可以处理错误,那么就不应该去使用异常。异常的一个作用就是当某个部分出现异常状况,那么我们可以通过异常来通知另一个部分。例如,当程序出现异常,那么我们可以把这个异常层层往上传递到函数的调用点,而其他的错误处理方式并不这么方便。虽然异常可以在两个部分进行传递,但是它并不是跨线程的,我们不能在两个线程间传递异常。例如下面的代码就是错误的。

DWORD CALLBACK threadfunc(void*)
{
//……
throw int(); //抛出异常
}

int main(){
try
{
DWORD tid;
HANDLE hdl = CreateThread(NULL, 0, &threadfunc, NULL, 0, &tid);
Sleep(500);
CloseHandle(hdl);
}
catch(...)
{ cout<<"catched"<<endl;}
}

threadfunc抛出的异常我们根本无法接收到。这样也说明了一个问题,当我们不确定线程函数中的代码是否会抛出异常的时候,我们都必须在其中加入try 块,以保证异常安全。例如上面的代码就应该写成下面这个样

DWORD CALLBACK threadfunc(void*)
{
try
{
//其他代码 //不确定这里是否会抛出异常
}
catch(...)
{}
}

int main(){
DWORD tid;
HANDLE hdl = CreateThread(NULL, 0, &threadfunc, NULL, 0, &tid);
Sleep(500);
CloseHandle(hdl);
}
//The End
【辣子鸡丁|2004.7.1】
http://blog.csdn.net/jinhao/archive/2004/07/11/39220.aspx
...全文
829 56 打赏 收藏 转发到动态 举报
写回复
用AI写文章
56 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jinhao 2005-07-03
  • 打赏
  • 举报
回复
C++异常不是干这个玩意的 -_-!

你说的应该是 windows seh中的 __try...__catch
antijpn 2005-07-02
  • 打赏
  • 举报
回复
不太清楚有没有这样的异常类,但是用catch(...)肯定是可以捕捉到的(而且连函数ret addr异常都可以搞定……)
Wolf0403 2005-06-27
  • 打赏
  • 举报
回复
大米,嘘~~~~
Access Voliation 不是 C++ exception。。。是不是 SEH 我就不清楚了
antijpn 2005-06-26
  • 打赏
  • 举报
回复
其实1楼的有个说法不正确,访问0地址,也有异常,并且可以捕捉到……
umbrella1984 2005-06-25
  • 打赏
  • 举报
回复
感觉C++的异常很别扭,应该学学JAVA的,看着舒服,语法也好。
angelboycn 2004-07-20
  • 打赏
  • 举报
回复
高手过招,水平不够的小菜就不要发表言论,UP一下就好~免得污染全局名字空间。。。
jiang8360 2004-07-20
  • 打赏
  • 举报
回复
up
peter9606 2004-07-19
  • 打赏
  • 举报
回复
up
xyz_mw 2004-07-19
  • 打赏
  • 举报
回复
学习
languagec 2004-07-16
  • 打赏
  • 举报
回复
Wolf0403 2004-07-16
  • 打赏
  • 举报
回复
拍我的砖不是水平~你看刀子和鸡丁天天拍我,还是一群……sigh,不说。

鸡丁:看看高手过招
http://www.allaboutprogram.com/viewtopic.php?t=2069
lbaby 2004-07-15
  • 打赏
  • 举报
回复
从此帖开始学习c++
一定要拍废人D砖...
Jinhao 2004-07-15
  • 打赏
  • 举报
回复
to 平沙落雁
这段时间你消失到哪里去了?
howler 2004-07-13
  • 打赏
  • 举报
回复
异常只是处理程序发生严重错误时用,在正常的代码中不要使用异常。
xjp6688 2004-07-13
  • 打赏
  • 举报
回复
好贴,建议多出这样的帖子,感谢C/C++值班室的成员们!!
erwinrommel 2004-07-13
  • 打赏
  • 举报
回复
鸡兄,我来up你了!
goatish 2004-07-12
  • 打赏
  • 举报
回复
好贴要顶!~~
sharkhuang 2004-07-12
  • 打赏
  • 举报
回复
顶一下
zhouqingyuan 2004-07-12
  • 打赏
  • 举报
回复
UP,只要分!
Jinhao 2004-07-12
  • 打赏
  • 举报
回复
作为C++语言标准化的东西,为什么不用呢?首先应该考虑的就是这些东西,如果实在是不适合就放弃
加载更多回复(36)

64,636

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧