C++中catch(…)如何使用

本文介绍C++中的catch(...)特殊用法,它可以捕获多种类型的异常,增强程序的可靠性和安全性。
C++异常处理模型的trycatch使用语法,其中catch关键字是用来定义catch block的,它后面带一个参数,用来与异常对象的数据类型进行匹配。注意catch关键字只能定义一个参数,因此每个catch block只能是一种数据类型的异常对象的错误处理模块。如果要想使一个catch block能抓获多种数据类型的异常对象的话,怎么办?C++标准中定义了一种特殊的catch用法,那就是” catch(…)”。

感性认识

  1、catch(…)到底是一个什么样的东东,先来个感性认识吧!看例子先:

int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是int,值为1)
throw 1;
}
//catch( int& value )
//注意这里catch语句
catch( …)
{
cout << "在 catch(…) block 中, 抛出的int类型的异常对象被处理" << endl;
}
}

  2、哈哈!int类型的异常被catch(…)抓获了,再来另一个例子:

int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是double,值为0.5)
throw 0.5;
}
//catch( double& value )
//注意这里catch语句
catch( …)
{
cout << "在 catch(…) block 中, double类型的异常对象也被处理" << endl;
}
}

  3、同样,double类型的异常对象也被catch(…)块抓获了。是的,catch(..)能匹配成功所有的数据类型的异常对象,包括C++语言提供所有的原生数据类型的异常对象,如int、double,还有char*、int*这样的指针类型,另外还有数组类型的异常对象。同时也包括所有自定义的抽象数据类型。例程如下:

int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是char*)
char* p=0;
throw p;
}
//catch( char* value )
//注意这里catch语句
catch( …)
{
cout << "在 catch(…) block 中, char*类型的异常对象也被处理" << endl;
}
}


int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是int[])
int a[4];
throw a;
}
//catch( int value[] )
//注意这里catch语句
catch( …)
{
cout << "在 catch(…) block 中, int[]类型的异常对象也被处理" << endl;
}
}

  4、对于抽象数据类型的异常对象。catch(…)同样有效,例程如下:

class MyException
{
public:
protected:
int code;
};

int main()
{
try
{
cout << "在 try block 中, 准备抛出一个异常." << endl;
//这里抛出一个异常(其中异常对象的数据类型是MyException)
throw MyException();
}
//catch(MyException& value )
//注意这里catch语句
catch( …)
{
cout << "在catch(…) block中, MyException类型的异常对象被处理" << endl;
}
}
对catch(…)有点迷糊?
1、究竟对catch(…)有什么迷糊呢?还是看例子先吧!
void main()
{
int* p = 0;

try
{
// 注意:下面这条语句虽然不是throw语句,但它在执行时会导致系统
// 出现一个存储保护错误的异常(access violation exception)
*p = 13; // causes an access violation exception;
}
catch(...)
{
//catch(…)能抓获住上面的access violation exception异常吗?
cout << "在catch(…) block中" << endl;
}
}

  请问上面的程序运行时会出现什么结果吗?catch(…)能抓获住系统中出现的access violation exception异常吗?朋友们!和我们的主人公阿愚一样,自己动手去测试一把!
结果又如何呢?实际上它有两种不同的运行结果,在window2000系统下用VC来测试运行这个小程序时,发现程序能输出"在catch(…) block中"的语句在屏幕上,也即catch(…) 能成功抓获住系统中出现的access violation exception异常,很厉害吧!但如果这个同样的程序在linux下用gcc编译后运行时,程序将会出现崩溃,并在屏幕上输出”segment fault”的错误信息。

  主人公阿愚有点急了,也开始有点迷糊了,为什么?为什么?为什么同样一个程序在两种不同的系统上有不同的表现呢?其原因就是:对于这种由于硬件或操作系统出现的系统异常(例如说被零除、内存存储控制异常、页错误等等)时,window2000系统有一个叫做结构化异常处理(Structured Exception Handling,SEH)的机制,这个东东太厉害了,它能和VC中的C++异常处理模型很好的结合上(实际上VC实现的C++异常处理模型很大程度上建立在SEH机制之上的,或者说它是SEH的扩展,后面文章中会详细阐述并分析这个久富盛名的SEH,看看catch(…)是如何神奇接管住这种系统异常出现后的程序控制流的,不过这都是后话)。而在linux系统下,系统异常是由信号处理编程方法来控制的(信号处理编程,signal processing progamming。在介绍unix和linux下如何编程的书籍中,都会有对信号处理编程详细的介绍,当然执著的主人公阿愚肯定对它也不会放过,会深入到unix沿袭下来的信号处理编程内部的实现机制,并尝试完善改进它,使它也能够较好地和C++异常处理模型结合上)。

  那么C++标准中对于这种同一个程序有不同的运行结果有何解释呢?这里需要注意的是,window2000系统下catch(…)能捕获住系统异常,这完全是它自己的扩展。在C++标准中并没有要求到这一点,它只规定catch(…)必须能捕获程序中所有通过throw语句抛出的异常。因此上面的这个程序在linux系统下的运行结果也完全是符合C++标准的。虽然大家也必须承认window2000系统下对C++异常处理模型的这种扩展确实是一个很不错的完善,极大得提高了程序的安全性。

为什么要用catch(…)这个东东?

  程序员朋友们也许会说,这还有问吗?这篇文章的一开始不就讲到了吗?catch(…)能够捕获多种数据类型的异常对象,所以它提供给程序员一种对异常对象更好的控制手段,使开发的软件系统有很好的可靠性。因此一个比较有经验的程序员通常会这样组织编写它的代码模块,如下:

void Func()
{
try
{
// 这里的程序代码完成真正复杂的计算工作,这些代码在执行过程中
// 有可能抛出DataType1、DataType2和DataType3类型的异常对象。
}
catch(DataType1& d1)
{
}
catch(DataType2& d2)
{
}
catch(DataType3& d3)
{
}
// 注意上面try block中可能抛出的DataType1、DataType2和DataType3三
// 种类型的异常对象在前面都已经有对应的catch block来处理。但为什么
// 还要在最后再定义一个catch(…) block呢?这就是为了有更好的安全性和
// 可靠性,避免上面的try block抛出了其它未考虑到的异常对象时导致的程
// 序出现意外崩溃的严重后果,而且这在用VC开发的系统上更特别有效,因
// 为catch(…)能捕获系统出现的异常,而系统异常往往令程序员头痛了,现
// 在系统一般都比较复杂,而且由很多人共同开发,一不小心就会导致一个
// 指针变量指向了其它非法区域,结果意外灾难不幸发生了。catch(…)为这种
// 潜在的隐患提供了一种有效的补救措施。
catch(…)
{
}
}

  还有,特别是VC程序员为了使开发的系统有更好的可靠性,往往在应用程序的入口函数中(如MFC框架的开发环境下CXXXApp::InitInstance())和工作线程的入口函数中加上一个顶层的trycatch块,并且使用catch(…)来捕获一切所有的异常,如下:

BOOL CXXXApp::InitInstance()
{
if (!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
return FALSE;
}

AfxEnableControlContainer();

// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.

#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif


// 注意这里有一个顶层的trycatch块,并且使用catch(…)来捕获一切所有的异常
try
{
CXXXDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
}
catch(…)
{
// dump出系统的一些重要信息,并通知管理员查找出现意外异常的原因。
// 同时想办法恢复系统,例如说重新启动应用程序等
}

// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}

  通过上面的例程和分析可以得出,由于catch(…)能够捕获所有数据类型的异常对象,所以在恰当的地方使用catch(…)确实可以使软件系统有着更好的可靠性。

<think>嗯,用户想了解C++中try-catch语句的用法。首先,我得回忆一下C++的异常处理机制。记得异常处理主要通过try、throw和catch这三个关键字来实现的。用户可能想知道基本语法和实际例子,所以需要先解释清楚每个部分的作用。 首先,try块是用来包裹可能抛出异常的代码。当异常被抛出时,程序会跳转到对应的catch块进行处理。throw则是用来抛出异常对象的,可以是内置类型,比如int或者字符串,但通常推荐使用标准异常类,比如std::exception的子类,这样更符合最佳实践。 然后,catch块需要捕获特定类型的异常。如果有多个catch块,它们应该按照从具体到一般的顺序排列,确保正确的异常被捕获。例如,先捕获std::runtime_error,再捕获更一般的std::exception,最后用catch(...)处理未知异常。这样能避免更具体的异常被一般化的catch块截获。 用户提供的引用资料里提到,throw可以显式抛出异常,比如throw std::runtime_error("错误信息")。同时,引用[3]中的例子用了throw "error",这是一个字符串常量,属于char*类型。不过,在C++中更推荐使用标准异常类型,这样可以利用多态性,方便处理。所以可能需要提醒用户尽量避免使用原始类型,转而使用标准库中的异常类。 另外,异常重新抛出也是一个重要点,即在一个catch块中使用throw; 不跟任何表达式,可以将当前异常继续传递给上层调用栈处理。这在需要部分处理异常后再传递时很有用。 然后需要考虑异常处理的汇编实现,引用[3]中的汇编代码可能展示了异常处理底层的机制,但用户可能不需要深入到汇编层面,所以解释时保持高层抽象即可。 用户可能还会关心最佳实践,比如何时使用异常,如何设计异常安全的代码。比如,在资源管理中使用RAII,确保即使发生异常,资源也能正确释放。构造函数和析构函数中的异常处理注意事项也需要提及,比如析构函数默认不应抛出异常。 此外,可能需要比较C++的异常处理与其他语言(如Java或Python)的不同之处,但用户的问题集中在C++上,所以重点放在C++的特性即可。 最后,生成的相关问题需要覆盖基本用法、最佳实践、性能影响、与返回错误码的比较,以及自定义异常的实现。这样用户可以有进一步的学习方向。 总结回答结构应该是:基本语法、示例代码、注意事项、最佳实践、相关问题的引导。确保每个部分都有清晰的解释和示例,帮助用户逐步理解如何正确使用try-catch。</think>### C++中`try-catch`语句的用法详解 #### 一、基本语法与执行流程 C++通过`try`、`throw`、`catch`三个关键字实现异常处理: ```cpp try { // 可能抛出异常的代码 throw exception_object; // 抛出异常对象 } catch (exception_type1 e) { // 处理类型1的异常 } catch (exception_type2 e) { // 处理类型2的异常 } // ...更多catchcatch (...) { // 捕获所有未处理的异常 // 通用异常处理 } ``` - **执行流程**:当`try`块中代码抛出异常时,程序立即停止当前执行,按顺序匹配`catch`块。匹配成功后执行对应处理代码,完成后继续执行后续非异常处理代码[^1][^2]。 --- #### 二、代码示例与解析 1. **基础用法示例**: ```cpp #include <iostream> #include <stdexcept> int main() { try { int x = 0; if (x == 0) { throw std::runtime_error("除数不能为0"); // 抛出标准异常 } std::cout << 10 / x << std::endl; } catch (const std::runtime_error& e) { std::cerr << "错误信息: " << e.what() << std::endl; // 输出错误描述 } return 0; } ``` 输出结果: ``` 错误信息: 除数不能为0 ``` 2. **多类型异常处理**: ```cpp try { throw 42; // 抛出整数类型异常 } catch (int e) { std::cout << "捕获整数异常: " << e << std::endl; } catch (const char* msg) { std::cout << "捕获字符串异常: " << msg << std::endl; } ``` --- #### 三、关键注意事项 1. **异常类型匹配**: - `catch`块按声明顺序匹配,应优先处理具体类型(如`std::invalid_argument`),最后处理通用类型(如`std::exception`或`...`) - 使用`const&`捕获异常可避免拷贝开销(如`catch (const std::exception& e)`) 2. **标准异常体系**: ```mermaid graph TD std::exception --> std::runtime_error std::exception --> std::logic_error std::runtime_error --> std::overflow_error std::logic_error --> std::invalid_argument ``` 推荐优先使用标准异常类,而非原始数据类型(如`int`或`char*`)[^2] 3. **重新抛出异常**: ```cpp catch (const std::exception& e) { std::cerr << "记录日志: " << e.what(); throw; // 重新抛出当前异常 } ``` --- #### 四、最佳实践指南 1. **RAII资源管理**: ```cpp class FileHandler { public: FileHandler(const std::string& path) : file(fopen(path.c_str(), "r")) { if (!file) throw std::runtime_error("文件打开失败"); } ~FileHandler() { if (file) fclose(file); } private: FILE* file; }; void process_file() { try { FileHandler fh("data.txt"); // 异常安全资源管理 // 文件操作... } catch (const std::exception& e) { // 自动调用fh的析构函数 } } ``` 2. **性能优化**: - 避免在频繁调用的代码路径中使用异常(如循环体内部) - 异常处理的时间成本比普通返回高约10-100倍(视编译器优化而定) 3. **构造函数异常**: - 构造函数失败时应抛出异常(无法通过返回值报告错误) - 析构函数默认标记为`noexcept`,若抛出异常会导致`std::terminate` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值