之前有一篇文章介绍了 new 的一些用法 c++ new 在指定内存上创建对象,今天结合源码来学习一下 new 更详细的用法。相关的源码:gcc git
1,void* operator new (std::size_t size);
我们可以在头文件
_GLIBCXX_NODISCARD void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
__attribute__((__externally_visible__));
原型上表明此 new 是会抛出异常的,而异常的类型及声明如下:
/**
* @brief Exception possibly thrown by @c new.
* @ingroup exceptions
*
* @c bad_alloc (or classes derived from it) is used to report allocation
* errors from the throwing forms of @c new. */
class bad_alloc : public exception
{
public:
bad_alloc() throw() { }
#if __cplusplus >= 201103L
bad_alloc(const bad_alloc&) = default;
bad_alloc& operator=(const bad_alloc&) = default;
#endif
// This declaration is not useless:
// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118
virtual ~bad_alloc() throw();
// See comment in eh_exception.cc.
virtual const char* what() const throw();
};
如果你要自己写异常处理函数,头文件里也有类型说明,必须是这样的函数指针类型:
/** If you write your own error handler to be called by @c new, it must
* be of this type. */
typedef void (*new_handler)();
/// Takes a replacement handler as the argument, returns the
/// previous handler.
new_handler set_new_handler(new_handler) throw();
而且还提供了设置异常处理函数的接口,它的返回值是前异常处理函数指针。下面来看一个例子:
#include
using namespace std;
void outOfMemory()
{
cout << "no enough memory" << endl;
}
struct Mem
{
int array[9999999999999L];
};
int main()
{
set_new_handler(outOfMemory);
Mem *p = new Mem();
delete p;
return 0;
}
结果是这样的,异常函数被循环调用:
为什么是这种情况呢?那 new 里到底做了什么呢?我们看一下源码:
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
void *p;
/* malloc (0) is unpredictable; avoid it. */
if (__builtin_expect (sz == 0, false))
sz = 1;
while ((p = malloc (sz)) == 0)
{
new_handler handler = std::get_new_handler ();
if (! handler)
_GLIBCXX_THROW_OR_ABORT(bad_alloc());
handler ();
}
return p;
如果分配不到足够的空间,则异常处理函数会一直被调用,直到能分配到足够的空间。
2,void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) noexcept;
上面的 new 有异常时抛出异常,而这个函数是不会抛出异常的。如:
#include
using namespace std;
void outOfMemory()
{
cout << "no enough memory" << endl;
}
struct Mem
{
int array[9999999999999L];
};
int main()
{
// set_new_handler(outOfMemory);
Mem *p = new(std::nothrow) Mem();
delete p;
return 0;
}
带 nothrow 和不带的区别:
但是如果设置了异常处理函数,即使是 new(std::nothrow) 也会抛出异常,如:
#include
#include
using namespace std;
void outOfMemory()
{
static int count = 0;
cout << "no enough memory" << endl;
if(count++ == 10)
{
assert(0);
}
}
struct Mem
{
int array[9999999999999L];
};
int main()
{
set_new_handler(outOfMemory);
Mem *p = new(std::nothrow) Mem();
delete p;
return 0;
}
设置的异常处理函数被调用了:
源码是这样的:
#include
#include
#include "new"
extern "C" void *malloc (std::size_t);
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz, const std::nothrow_t&) noexcept
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 206. operator new(size_t, nothrow) may become unlinked to ordinary
// operator new if ordinary version replaced
__try
{
return ::operator new(sz);
}
__catch (...)
{
// N.B. catch (...) means the process will terminate if operator new(sz)
// exits with a __forced_unwind exception. The process will print
// "FATAL: exception not rethrown" to stderr before exiting.
//
// If we propagated that exception the process would still terminate
// (because this function is noexcept) but with a less informative error:
// "terminate called without active exception".
return nullptr;
}
}
这里我们看到不抛出异常的函数是调用了上面那个抛出异常的函数的。所以一旦你设置了异常处理函数,就不能正常抛出异常了,因为只有当 handler = nullptr 时才会抛出异常。
while ((p = malloc (sz)) == 0)
{
new_handler handler = std::get_new_handler ();
if (! handler)
_GLIBCXX_THROW_OR_ABORT(bad_alloc());
handler ();
}