exception 的实现

博客介绍了异常处理的动态和静态两种方法。动态方法由编译器插入setjmp和longjmp代码,影响性能但无需系统相关支持;静态方法生成查找表,开销低但与操作系统相关。还给出了动态方法的伪代码及编译器插入代码的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

据我所知有2种方法,动态和静态
动态是编译器帮你插入一堆代码(关键性的代码就是setjmp和
longjmp),在你每个throw catch的地方都插入这些代码,会
影响整体性能(这个不需要什么系统相关的东西就能实现)

静态的就是生成一个expection handler的查找表,然后每次
抛出的时候就去查那么一查,如果没有异常情况的话,这个方式
开销低,但是这个就要和操作系统相关了,如果虚拟机就
。。。嘿嘿。


然后,简单的说一下动态方法,我具体代码懒的写(其实怕写不出)
给一点伪代码:

struct Exception{
char* name;
int typecode;
//...others
};

int HashFun(char*)//这个函数把字符串搞成int,是唯一对应的
{...}

Exception* CreateException(char* name)//新建一个exception
{..}

void FreeException(Exception*)//销毁它!
{..}

//--------------------------------------------------------
struct ExceptionHandler
{
int typecode;
jmp_buf location; //这里是比较重要的。。。
};
struct ExceptionHandlerStack
{ int size;//现在list的大小
  ExceptionHandler list[MAX]; //一个Handler的链
  ExceptionHandlerStack(); //初始化工作
~ExceptionHanclerStack();//销毁工作。。。
  static ExceptionHandler* Push(char*name)  //根据name,用hash函数生成typecod
e,然后填入  链最后一个Handler里面,然后返回它
  static Pop();//嗯。。。很明显
  static void throwException(Exception *ex)//抛出异常
  {
     在链里面找啊找,找到了typecode相同的Handler就
     longjmp(list[size].location,(int)ex); //关键函数。。。
  }
};

好基本实现差不多了,然后讲编译器插入的代码
本来是:
void Fun()
{
try{
    Excetpion *ex;
    ex = CreateException("name");
     throwException(ex);
}catch(Exception *ex)
{
   dosomething(ex);
}
好了,编译器改成。。。
void Fun()
{
ExceptionHandler* hd;
int ret;
hd = ExceptionhandlerList::push("name");
ret = setjmp(hd->location);
if(ret ==0)
{ Excetpion* ex;
  ex = CreateException("name);
  ExcetpionhandlerList::throwException(ex);
}else
{  Exception* ex;
   ex = (exception*)ret;
   dosomething(ex);
  FreeException(ex);
  }
ExceptionHanclerList::pop(hd);
}
<think>嗯,用户想知道C++中异常类的具体实现细节。我需要先回忆一下C++异常处理的基本机制。C++的异常处理主要依赖于编译器生成的代码和运行时库的支持。首先,当抛出异常时,编译器会构造一个异常对象,通常是在堆上分配内存,然后将这个对象的指针传递给异常处理机制。 接下来要考虑的是异常类的层次结构。C++标准库中的异常类,比如std::exception,以及其派生类如std::runtime_error,通常包含虚函数what()来返回错误信息。这些类的实现可能使用Pimpl惯用法来隐藏实现细节,这样可以在不改变头文件的情况下修改内部数据。比如,用户引用的资料[^3]提到Pimpl通过不透明指针隐藏私有成员,这可能应用在异常类中,保持ABI的稳定性。 然后,需要解释异常处理的内存管理。抛出异常时,编译器可能会调用__cxa_allocate_exception来分配内存,并在异常处理完毕后使用__cxa_free_exception释放。这些函数属于Itanium C++ ABI的一部分,虽然具体实现可能因编译器而异,但大体流程相似。另外,用户提到的引用[^3]中的Pimpl用法,可能在这里用于管理异常对象的内部数据,避免暴露细节。 还要考虑异常传播的机制。当异常被抛出时,运行时系统会展开堆栈,寻找匹配的catch块。这涉及到栈展开表和unwind库,这些通常由编译器生成。每个函数可能有相关的元数据来指导栈展开,这部分在用户的问题中没有直接提到,但属于实现细节的重要部分。 用户提供的引用中有关于线程池插件式组件和Pimpl的示例,特别是引用详细说明了如何隐藏实现细节。这可能和异常类的设计有关,比如使用Pimpl来封装异常的内部信息,确保头文件简洁,同时允许内部实现的变化。 另外,需要提到不同编译器实现差异。比如,GCC和Clang遵循Itanium ABI,而MSVC使用不同的结构。这可能影响异常对象的布局和处理方式,但用户可能更关心通用原理而非具体编译器的细节。 最后,应该总结实现要点,比如内存分配、层次结构、传播机制,并生成相关问题,帮助用户进一步学习。同时,确保引用正确的来源,如用户提供的引用关于Pimpl的部分,以及可能涉及的其他引用,比如异常处理的内存管理部分。</think>### C++异常类的实现细节分步说明 #### 1. 异常对象内存管理 当抛出异常时,编译器通过以下步骤分配内存: - 调用`__cxa_allocate_exception`分配内存块(通常为`sizeof(异常类) + 头信息)` - 通过placement new构造异常对象 - 内存分配策略可能使用特殊内存池(非标准实现) 示例代码结构: ```cpp // 编译器生成的伪代码 void* __cxa_allocate_exception(size_t size) { return malloc(size + exception_header_size); } void __throw_impl() { void* mem = __cxa_allocate_exception(sizeof(MyException)); new (mem) MyException("error"); // placement new _Unwind_RaiseException(mem); // 启动栈展开 } ``` #### 2. 异常类层次结构 标准库异常类使用继承体系: ``` std::exception ├─ std::logic_error │ └─ std::invalid_argument └─ std::runtime_error └─ std::system_error ``` 实现要点: - 使用Pimpl惯用法隐藏错误信息存储细节 - 虚函数表支持多态访问 - what()方法返回const char*类型错误信息 #### 3. 异常传播机制 栈展开过程依赖两大核心组件: 1. **Unwind库**:实现`_Unwind_RaiseException`等函数 2. **LSDA(Language Specific Data Area)**: - 编译器生成的.eh_frame段数据 - 包含清理代码地址和作用域信息 - 使用DWARF调试格式编码 #### 4. 捕获处理流程 ```cpp try { throw MyException(); } catch(const std::exception& e) { // 匹配过程 // 处理代码 } ``` 匹配过程: 1. 遍历异常处理表 2. 检查类型匹配(通过type_info) 3. 执行栈展开和清理操作 #### 5. 跨模块处理 - 异常类型必须满足二进制兼容性 - 使用Pimpl惯用法保持ABI稳定[^3] - 类型信息通过type_info对象比较
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值