欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

C++ 中的异常处理机制 - 重新抛出异常

最编程 2024-10-06 20:31:14
...
有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用
函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理
double Division(int a, int b)
{
 // 当b == 0时抛出异常
 if (b == 0)
 {
 throw "Division by zero condition!";
 }
 return (double)a / (double)b;
}
void Func()
{
 // 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。
 // 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再
 // 重新抛出去。
 int* array = new int[10];
 try {
 int len, time;
 cin >> len >> time;
 cout << Division(len, time) << endl;
 }
 catch (...)
 {
 cout << "delete []" << array << endl;
 delete[] array;
 throw;
 }
 // ...
 cout << "delete []" << array << endl;
 delete[] array;
}
int main()
{
 try
 {
 Func();
 }
 catch (const char* errmsg)
 {
 cout << errmsg << endl;
 }
 return 0;
}
int* array = new int[10];
 try {
 int len, time;
 cin >> len >> time;
 cout << Division(len, time) << endl;
 }
 catch (...)
 {
 cout << "delete []" << array << endl;
 delete[] array;
 throw;
 }
 // ...
 cout << "delete []" << array << endl;
 delete[] array;
}

上面就是一场重新抛出的核心逻辑。其中直接throw就表示捕捉到了什么异常,就会抛出什么异常。

catch ( const char* errmsg )
{
cout << errmsg << endl ;
}
在外界(main)中,重新处理了异常。
如果没有重新抛异常,那么函数内的数组将不会正确释放,将会发生内存泄漏!

异常安全

构造函数完成对象的构造和初始化最好不要在构造函数中抛出异常,否则可能导致对象不
完整或没有完全初始化。
析构函数主要完成资源的清理最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内
存泄漏、句柄未关闭等)。
C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄
漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII(智能指针,将一个指针交给对象管理,对象自动调用析构)来解决以上问题。

异常抛出,会跳跃到catch语句处,也就意味着,如果此时异常语句下面存在delete语句,那么将会存在内存泄露问题吗?(CPP)

在C++中,异常的抛出会导致程序的控制流跳转到最近的匹配的catch块。如果在异常抛出之前调用了new操作符分配了内存,并且在异常抛出之后没有相应的delete调用释放内存,那么确实可能发生内存泄露。

考虑以下代码示例:


void someFunction() {

    MyObject* pObj = new MyObject();

    // 假设这里发生了异常,并且没有被处理

    throw std::exception(); // pObj没有被释放



    // 如果没有异常,正常执行下面的代码

    delete pObj; // pObj在这里被释放

}



int main() {

    try {

        someFunction();

    }

    catch (...) {

        // 处理异常

    }

    return 0;

}

在上面的代码中,如果在new MyObject()之后和delete pObj;之前发生了异常,那么pObj指向的内存不会被释放,导致内存泄露。

异常规范

1. 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的
后面接throw(类型),列出这个函数可能抛掷的所有异常类型。
2. 函数的后面接throw(),表示函数不抛异常。
3. 若无异常接口声明,则此函数可以抛掷任何类型的异常。
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();
// C++11 中新增的noexcept,表示不会抛异常
thread() noexcept;
thread (thread&& x) noexcept;

当然,这不是绝对的,这只是一种规范,并不是规矩。

只不过noexcept关键字之后,如果抛出了异常,也不允许catch!

推荐阅读