C++的new、operator new、placement new
- 最近在学习STL中空间配置器的源码实现时发现了一个小知识点(
palcement new
)这个东西,这个具体是什么我们慢慢聊。 - 这篇文章主要是对C++中对
new/delete
以及相关的知识做了一个总结。
new
和delete
的实现原理
new
和delete
是C++用来动态内存开辟及释放的操作符,与C语言的malloc
和free
不同。
那么在使用new/delete
时候会发生什么事情呢?
new
- 调用
operator new
分配足够的空间 - 在申请的空间上调用相关对象的构造函数
- 不可以被重载
- 调用
delete
- 调用相关对象的析构函数
- 调用
operator delete
释放分配的空间 - 不可以被重载
以上就是new/delete
在使用的时候发生的事情,那么什么又是operator new/operator delete
呢?接着往下看。
operator new
和operator delete
- 首先我们需要知道
new/delete
是操作符,而operator new/operator delete
是系统提供的全局函数,new
在底层调用operator new
来申请空间,delete
在底层通过operator delete
来释放空间。这两个函数类似于C语言的malloc/free
。 - 下面贴出这两个函数的实现,从中不难发现这两个函数来对
malloc/free
进行了封装:
/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试
执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。
*/
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申请内存失败了,这里会抛出bad_alloc 类型异常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void *pUserData)
{
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg( pUserData, pHead->nBlockUse );
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return;
}
/*
free的实现
*/
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
-
operator new
特点(operator delete
类似)-
可以被重载
-
重载的时候返回类型必须是
void *
-
重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为
size_t
。 -
重载时,可以带其它参数
-
/* * 以下是例子 */ #include <iostream> #include <string> using namespace std; class Test { public: Test() { cout << "constructor of Test" << endl; } ~Test() { cout << "destructor of Test" << endl; } void* operator new(size_t size, int num) { cout << "operator new size = "<< size <<endl; cout << "num = " << num << endl; return ::operator new(size); } void operator delete(void* ptr) { cout << "operator delete" << endl; ::operator delete(ptr); } }; int main() { Test *px = new(10)Test; delete px; return 0; }
-
-
为了效率,我们可以重载
operator new
和operator delete
以实现对内存管理的不同要求,在某些特定的场合下,可以进一步改善它的性能。
placement new
-
placement new
是重载operator new
的一个标准、全局的版本,它不能够被自定义的版本代替(不像普通版本的operator new
和operator delete
能够被替换)。 -
函数原型
-
void *operator new( size_t, void * p ) throw() { return p; }
-
-
placement new
的执行忽略了size_t
参数,只返还第二个参数。其结果是允许用户把一个对象放到一个特定的地方,达到调用构造函数的效果。 -
placement new
只是operator new
重载的一个版本。它并不分配内存,只是返回指向已经分配好的某段内存的一个指针。因此不能删除它,但需要调用对象的析构函数。 -
使用方法:
-
首先先分配一块内存
-
在这块内存上构造一个对象
-
// 举个例子 #include <cstdlib> #include <iostream> #include <cstdio> class Test { public: Test() : num_(0) { std::cout << "Test()" << std::endl; } Test(int num) : num_(num) { std::cout << "Test(int num = 0)" << std::endl; } ~Test() { std::cout << "~Test()" << std::endl; } void printNum() { std::cout << "num = " << num_ << std::endl; } private: int num_; }; /* Placement new的原型 void *operator new( size_t, void * p ) throw() { return p; } */ void testPlacement(char* prk) { Test* t1 = new(prk)Test(10); Test* t2 = new(prk + sizeof(Test))Test; printf("&t1 = %p\n", t1); printf("&t2 = %p\n", t2); t1->printNum(); t2->printNum(); t1->~Test(); t2->~Test(); } int main(int argc, char* argv[]) { char* prk = (char*)malloc(sizeof(Test) * 5); testPlacement(prk); free(prk); return 0; }
-
- STL里是如何使用这个
placement new
-
在 C++ 里,当我们调用
new
和delete
进行对象的创建和销毁的时候,也同时会有内存配置操作和释放操作:- 对于
new
来说,编译器会先调用::operator new
分配内存;然后调用Obj::Obj()
构造对象内容。 - 对于
delete
来说,编译器会先调用Obj::~Obj()
析构对象;然后调用::operator delete
释放空间。
- 对于
-
为了精密分工,STL的空间配置器决定将这两个阶段操作区分开来。
- 对象构造由
::construct()
负责;对象释放由::destroy()
负责。 - 内存配置由
alloc::allocate()
负责;内存释放由alloc::deallocate()
负责; - 这里我们来看一下
construct
与destroy
的实现就知道了
- 对象构造由
// 将初值 __value 设定到指针所指的空间上。
template <class _T1, class _T2>
inline void _Construct(_T1* __p, const _T2& __value) {
new ((void*) __p) _T1(__value); // placement new,调用 _T1::_T1(__value);
}
template <class _T1>
inline void _Construct(_T1* __p) {
new ((void*) __p) _T1();
}
// 第一个版本,接受一个指针,准备将该指针所指之物析构掉。
template <class _Tp>
inline void _Destroy(_Tp* __pointer) {
__pointer->~_Tp();
}
// 从上可以看出这里使用了placement new。
以上就是new
与delete
的相关知识。
相关链接
https://blog.csdn.net/tonglin12138/article/details/85699115
https://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html
https://zhuanlan.zhihu.com/p/331809729