C++的new、operator new、placement new

C++的new、operator new、placement new

  • 最近在学习STL中空间配置器的源码实现时发现了一个小知识点(palcement new)这个东西,这个具体是什么我们慢慢聊。
  • 这篇文章主要是对C++中对new/delete以及相关的知识做了一个总结。
  1. new delete的实现原理

newdelete是C++用来动态内存开辟及释放的操作符,与C语言的mallocfree不同。

那么在使用new/delete时候会发生什么事情呢?

  • new
    • 调用operator new分配足够的空间
    • 在申请的空间上调用相关对象的构造函数
    • 不可以被重载
  • delete
    • 调用相关对象的析构函数
    • 调用operator delete释放分配的空间
    • 不可以被重载

以上就是new/delete在使用的时候发生的事情,那么什么又是operator new/operator delete呢?接着往下看。

  1. operator newoperator 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 newoperator delete以实现对内存管理的不同要求,在某些特定的场合下,可以进一步改善它的性能。

  1. placement new
  • placement new 是重载operator new 的一个标准、全局的版本,它不能够被自定义的版本代替(不像普通版本的operator newoperator 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;
      }
      
  1. STL里是如何使用这个placement new
  • 在 C++ 里,当我们调用 newdelete 进行对象的创建和销毁的时候,也同时会有内存配置操作和释放操作:

    • 对于 new 来说,编译器会先调用 ::operator new 分配内存;然后调用 Obj::Obj() 构造对象内容。
    • 对于 delete 来说,编译器会先调用 Obj::~Obj() 析构对象;然后调用 ::operator delete 释放空间。
  • 为了精密分工,STL的空间配置器决定将这两个阶段操作区分开来。

    • 对象构造由 ::construct() 负责;对象释放由 ::destroy() 负责。
    • 内存配置由 alloc::allocate() 负责;内存释放由 alloc::deallocate() 负责;
    • 这里我们来看一下constructdestroy的实现就知道了
// 将初值 __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。

以上就是newdelete的相关知识。

相关链接
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

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值