在 C++ 中,如果一个类的构造函数抛出异常(例如内存分配异常),我们应该如何处理,这是一个非常常见的问题。 考虑以下代码片段。 这里 A 类的构造函数抛出了一个异常。所以处理这种情况的最佳方法是在一个 try 块中实例化 A。如果在 A 的构造函数中抛出异常,i 将被堆栈展开销毁,并且抛出的异常将被捕获...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
class MyException(string str){
private:
string msg;
public:
MyException(string str){
msg = str;
}
void printerrmsg(){
cout<<msg.c_str()<<endl;
}
}
class A{
private: int i;
//if exception is thrown in the constructor of A, i will de destroyed by stack unwinding
//and the thrown exception will be caught
A()
{
i = 10;
throw MyException(“Exception thrown in constructor of A()”);
}
};
void main(){
try{
A();
}
catch(MyException& e){
e.printerrmsg();
}
}
|
现在还有另外一个问题... 假设我们需要在构造函数中分配一些动态内存... 在执行此操作时,构造函数抛出一个内存异常... 因此,引用基于堆的内存的基于堆栈的指针将被销毁,因为堆栈展开... 所以我们将有一些没有被任何指针引用的内存,因此我们无法访问它... 所以很明显这是一个内存泄漏... 那么,我们如何处理这个问题...
在现代 C++ 中处理这种情况的最佳方法是使用 auto_ptr/shared_ptr... 所以解决方案看起来像下面这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
#include <iostream>
#include <string>
#include <memory>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
using namespace std;
class SomeClass{
public:
SomeClass(){}
~SomeClass(){};
};
typedef boost::shared_ptr<SomeClass> pSomeClass;
typedef boost::shared_ptr<char> pChar;
typedef boost::shard_array<char> charBuff;
class MyException{
public:
MyException(string str){
msg = str;
}
void printerrmsg(){
cout<<msg.c_str()<<endl;
}
private:
string msg;
};
class A{
private:
int i;
pChar m_ptrChar;
pSomeClass m_ptrSomeClass;
charBuff m_pcharBuff;
//if exception is thrown in the constructor of A, i will be destroyed by stack unwinding
//and the thrown exception will be caught
public:
A():m_ptrChar(new char),m_ptrSomeClass(new SomeClass),m_pcharBuff(new char[100])
{
i = 10;
throw MyException("Exception at A's constructor");
}
};
int main(){
try{
A objA;
}
catch(MyException& e){
e.printerrmsg();
}
return 1;
}
|
在 Symbian C++ 中,它通过一个叫做两阶段构造函数的概念来处理...(它之所以出现,是因为早期的 Symbian C++ 中没有模板概念,因此没有 auto_ptr)... 在这个过程中,如果我们想在堆上创建一个动态内存分配,并用 *pMem 指向它,那么在构造的第一阶段,我们将 *pMem 初始化为 NULL。 显然这不会抛出异常... 然后我们将这个 pMem 推入清理堆栈(这是 Symbian C++ 的一个新概念)... 并且在构造的第二阶段,我们分配 pMem 指向的内存... 因此,如果构造函数失败,我们仍然在清理堆栈中保留了 pMem 的引用... 我们只需要弹出它并销毁它... 因此没有内存泄漏的机会...