C++中除了沿用C的alloc系列函数之外,还可以用new/new []来分配内存(这句是废话),我们在使用new这个operator的时候多是直接使用,而没有额外引用头文件。
写C语言代码写习惯的人都会在alloc函数之后,对指针做NULL判断,以检查内存分配是否成功,那么在C++中是否也需要以同样的方式来检查内存分配是否成功呢?根据实际code验证结果,答案是new成功返回即表示内存分配成功,不需检查指针是否为NULL,但是仍然会出现内存分配失败的情况,此时需要捕捉bad_alloc这种exceptin来知晓内存分配失败,bad_alloc是在new这个C++头文件(#include <new>)中声明的。废话先说到这里,下面以两段代码来作说明
1 #include <iostream> 2 #include <new> 3 #include <cstdlib> 4 5 using namespace std; 6 7 int main(int argc, char *arg[]) 8 { 9 char *buffer = NULL; 10 11 try 12 { 13 buffer = new char[90000000000ul]; // 故意分配很大的一块内存,导致操作失败 14 15 cout<<"The buffer address is: "<<static_cast<void*>(buffer)<<endl; 16 } 17 catch (const bad_alloc &ex) 18 { 19 cout<<"exception: "<<ex.what()<<endl; 20 } 21 catch (...) 22 { 23 cout<<"unknown exception"<<endl; 24 } 25 26 if (NULL != buffer) 27 { 28 delete[] buffer; 29 } 30 31 return 0; 32 }
对上面的代码进行编译和运行,结果如下
Administrator@attention /e/Code $ g++ stl_new.cpp -lstdc++ -o stl_new.exe Administrator@attention /e/Code $ ./stl_new.exe exception: std::bad_alloc Administrator@attention /e/Code $
可以看出分配失败时,的确throw出了bad_alloc,代码catch到这个exception即可知道内存分配失败。细心看上面的代码是有#include <new>这一行的,而C++的new头文件对new这个operator声明如下
void* operator new(std::size_t) throw (std::bad_alloc); void* operator new[](std::size_t) throw (std::bad_alloc); void operator delete(void*) throw(); void operator delete[](void*) throw(); void* operator new(std::size_t, const std::nothrow_t&) throw(); void* operator new[](std::size_t, const std::nothrow_t&) throw(); void operator delete(void*, const std::nothrow_t&) throw(); void operator delete[](void*, const std::nothrow_t&) throw();
其中很明显new和new[]这两个operator都被声明会throw出std::bad_alloc(入参只有size的那种,后面均指这种,不重复说明),那是不是因为引用new头文件的缘故而使用了这个特定类型的new了呢?
经过实际代码验证,答案为“不是”,实际上代码里去掉#include <new>这一行同样是编译OK的,且运行结果也一样。由此结论如下:
new这个operator是C++语言内置的,其本身在内存分配失败会throw出一个bad_alloc,因此我们在使用new进行内存/对象分配时,就必须使用catch来捕捉这种可能的bad_alloc来检查分配是否失败;若不做如此处理,被throw出来的bad_alloc会未在这一层被捕捉而一直向上冒泡,若我们的代码中一直不进行catch,则会一直冒泡到CRT,而CRT对这些未被捕捉异常,处理方式是调用abort()来直接结束整个程序,最终的后果便是程序异常退出。这里再罗嗦的贴一段代码来说明这一点
1 #include <stdio.h> 2 3 int main(int argc, char *argv[]) 4 { 5 char *buffer = NULL; 6 7 buffer = new char[90000000000ul]; // 故意分配很大的一块内存 8 printf("The buffer address is: %p\n", buffer); 9 10 if (NULL != buffer) 11 { 12 delete[] buffer; 13 } 14 15 return 0; 16 }
最终编译和运行结果为
Administrator@attention /e/Code $ ./new.exe terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.
对此自己的感受便是,在C++代码中使用new,应该将其包括在try{}catch(){}中,以知道分配是否成功和避免未捕捉的异常而导致程序异常退出。
但实际实际上在非嵌入式的环境中,比如桌面环境,内存都比较充裕(加上操作系统虚拟内存机制),内存分配失败几乎是微乎其微,而导致很多的try{}catch(){}形同虚设,而使代码有些dirty。并且倘若真的是出现内存分配失败的问题,那多是意味着软已经出现了严重的错误(比如内存泄漏),就算在这里catch到本次分配失败,也对于整个系统恢复也没什么作用,因此很多代码里都是直接使用new,而没有将其包括在try{}catch(){}之中。
对此个人感觉是还是严谨的使用try{}catch(){}为好,毕竟内存分配失败了,也算是一种错误,应该即可就对此进行捕捉处理,将错误影响范围尽可能限制在出错的位置,这样避免错误扩大,也方便debug排错。