new/delete都做了什么事
下面是一个很简单new/delete的应用,我们对段代码进行反汇编看看,new都做了什么
struct A
{
A(int a) :a(a) {}
int a;
};
int main()
{
A* p = new A{ 1 };
delete p;
p = nullptr;
}
可以看到,new的过程分了两步,一个是用operator new来分配了大小为4的堆空间,然后调用了类A的构造函数。当然这只是分配空间成功的情况,如果分配失败,会发生什么呢?
看看operator new的源码或许对new有个更加深入的认识:
_Ret_notnull_ _Post_writable_byte_size_(_Size)
_VCRT_ALLOCATOR void* __CRTDECL operator new(
size_t _Size
);
_Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size)
_VCRT_ALLOCATOR void* __CRTDECL operator new(
size_t _Size,
std::nothrow_t const&
) noexcept;
_Ret_notnull_ _Post_writable_byte_size_(_Size)
_VCRT_ALLOCATOR void* __CRTDECL operator new[](
size_t _Size
);
_Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size)
_VCRT_ALLOCATOR void* __CRTDECL operator new[](
size_t _Size,
std::nothrow_t const&
) noexcept;
到这里我们知道,operator new/operator new []
是函数,是函数的话我们就可以尝试继承它。
struct A
{
A() {};// { std::cout << "constructor" << std::endl; }
~A() {};// { std::cout << "destructor" << std::endl; }
void* operator new [](size_t size)
{
std::cout << "operator new size " << size << std::endl;
return ::operator new(size);
}
void operator delete [](void* point)
{
std::cout << "operator delete" << std::endl;
::operator delete(point);
}
private:
int a;
};
int main()
{
A* p = nullptr;
try
{
//这里new一个适合大小的
p = new A[(std::numeric_limits::max() >> 1) / sizeof(A)];
}
catch (std::bad_alloc& e)
{
std::cout << e.what() << std::endl;
}
if (p)
{
delete[] p;
p = nullptr;
}
}
默认情况下会抛出异常,并且能被std::bad_alloc捕获。当然这只是默认情况,我们是可以对分配失败进行一些特殊处理。
通过源码我们发现operator new/operator new []
重载了带有std::nothrow_t const&
的接口。函数还修饰成了noexcept,这表明了不会再往外抛异常。并且查看std::nothrow_t
源码发现,声明了全局的std::nothrow
供我们使用。所以我们就有新的用法。把上面例子的new[]改成 new(std::nothrow)[],类A重载带std::nothrow_t const&
参数的接口。可以分别实施带noexcept和不带noexcept的情况。
void* operator new [](size_t size, std::nothrow_t const&) noexcept
{
std::cout << "operator new size" << size << "with std::nothrow" << std::endl;
return ::operator new(size);
}
void operator delete [](void* point, std::nothrow_t const&)
{
std::cout << "operator delete with std::nothrow" << std::endl;
::operator delete(point);
}
// ...
p = new(std::nothrow) A[(std::numeric_limits::max() >> 1) / sizeof(A)];
默认抛出bad_alloc异常,如果有noexcept且有nothrow返回NULL,如果有noexcept没有nothrow则直接dump。
new/delete与operator new/operator delete的区别
new:
- 是操作符,所以不可能被重载
- 调用operator new分配空间,并且调用匹配的构造函数。如果operator new分配空间分配空间失败,则不会再调用构造函数。
operator new
- 可以被重载,但是要满足这些条件:返回类型必须声明为void*;第一个参数类型必须为表示要求分配空间的字节大小,类型为size_t(实际大小跟编译器有关)。
- 重载时可以带其他的参数。
- 如果分配空间失败则:
如果有new_handler,则调用new_handler,否则
默认抛出bad_alloc异常,如果有noexcept且有nothrow返回NULL,如果有noexcept没有nothrow则直接dump。
delete 与 delete operator类似
operator new与operator delete和C语言中的malloc与free对应,只负责分配及释放空间。但使用operator new分配的空间必须使用operator delete来释放,而不能使用free,因为它们对内存使用的登记方式不同。反过来亦是一样。