试分析推断下述代码的输出结果:
#include <stdio.h>
#include <stdlib.h>
#include <exception>
using namespace std;
void* operator new(size_t size)
{
printf("my new -> %u\n", size);
return malloc(size);
}
void operator delete(void *p)
{
printf("mydelete\r\n");
return free(p);
}
class A
{
public:
A()
{
printf("A\n");
throw int(-1);
}
~A()
{
printf("~A\n");
}
};
int main()
{
A* p = NULL;
try
{
p = new A;
}
catch (int& i)
{
printf("exception -> %d\n", i);
}
return 0;
}
要知道上述代码的输出结果,可能需要知道C++的new到底做了什么,以及如果析构函数抛出异常的话,C++是如何处理这种情况的。
实际上,上述代码的输出结果是:
my new -> 1
A
mydelete
exception -> -1
实际上,当new一个对象时,C++首先需要分配对象的内存,然后才会调用对象的析构函数,而且C++会调用new(sizeof(T))函数分配内存。其大致过程如下所示:
//new 实际过程大致是是:
A* p = (A*)malloc(sizeof(A));
try
{
A();
}
catch(...)
{
// 保证异常后不会造成内存泄露
// 不会调用析构函数(对象都没创建成功,析构函数没意义了)
delete p;
throw exception;
}
此外,当new过程中发现构造函数抛出异常而且构造函数没能处理该异常的话,那么C++就会使得该对象的创建失败,同时会释放已经分配好的内存,但是需要注意的是,此时C++不会去掉用对象的析构函数。
所以由上述知识我们就可以具体分析上述代码的输出结果了。
因此,对于构造函数抛出异常的情况,我们可做下述分析:
#include <stdio.h>
#include <stdlib.h>
#include <exception>
using namespace std;
// 重载new
void* operator new(size_t size)
{
printf("my new -> %u\n", size);
return malloc(size);
}
// 重载delete
void operator delete(void *p)
{
printf("mydelete\r\n");
return free(p);
}
class A
{
public:
A()
{
printf("A\n");
// 析构函数抛出异常
// C++自动会释放已分配的内存但是不会调用析构函数
throw int(-1);
}
~A()
{
printf("~A\n");
// 析构函数不推荐抛出异常,如果一定要,要自我实现异常处理
}
};
int main()
{
A* p = NULL;
try
{
// 此处实际调用 operator new (sizeof(A))
// 如果构造函数抛出了异常new的返回值为NULL
p = new A;
}
catch (int& i)
{
printf("exception -> %d\n", i);
}
return 0;
}