placement new

来源:互联网 发布:c json转实体类 编辑:程序博客网 时间:2024/06/07 01:05

       上一篇,我分析了 new , delete 现在来分析一下 placement new

        placement new 的语法: new(address)  constructed_function(..)

        placement new 是以已分配的内存为this,来调用函数。在需要重复分配内存的情况下,

        使用placement new 的效率是比较高的。

        ok ! 下面我们来看一看,placement new的例子吧。

        class test0{};

        class test1

        {

        public:

              test1(int i){};

        }

        void main

       {

             test0* ptest0 = new test0[10];       //能够使用,分配内存,调用test0无参构造函数。

             //test1* ptest1 = new test1[10];       //不能够使用,因为test1没有无参的构造函数。

             test1*  ptest1= (test1*)new byte[sizeof(test1)*10];  //先分配内存.

             for(int i = 0; i < 10; i++)

            {         new(ptest1+i) test1(1);     }                       //调用构造函数

       }

       ok ! placement new 的例子还很多。写这篇文章,主要是记录一下自己对placement new 的使用。

#include <stdlib.h>#include <stdio.h>class class111{public:class111(){m_name = "class111";printf("class111()\n");}~class111(){printf("~class111()\n");}void name(){printf("%s\n", m_name);}private:char* m_name;};class class222{public:class222(){m_name = "class222";printf("class222()\n");}~class222(){printf("~class222()\n");}void name(){printf("%s\n", m_name);}private:char* m_name;};class class333{public:class333(){m_name = "class333";printf("class333()\n");}~class333(){printf("~class333()\n");}void name(){printf("%s\n", m_name);}private:char* m_name;};class test{public:test(){m_pC1 = new class111();m_pC2 = new class222();m_pC3 = new class333();}~test(){delete m_pC1;delete m_pC2;delete m_pC3;}void memberNames(){m_pC1->name();m_pC2->name();m_pC3->name();}private:class111* m_pC1;class222* m_pC2;class333* m_pC3;};void main(){test t;t.memberNames();}

从上例中,我们可以看到 ,test的构造函数去,new成员变量,析构函数去delete成员变量。而这三个成员变量是随着test对象的存在,而存在,随着test对象的消亡

而消亡。所以,我们完全没有必要,三次new,和三次delete,我们只需一次new,和一次delete.这样就比较高效了。

#define LOGOUT(a)class test{public:test(){m_pBlock = (char*)malloc(sizeof(class111)+ sizeof(class222)+ sizeof(class333));if (nullptr == m_pBlock){LOGOUT("内存分配失败");return;}m_pC1 = new(m_pBlock) class111();m_pC2 = new(m_pBlock+sizeof(class111)) class222();m_pC3 = new(m_pBlock + sizeof(class111) + sizeof(class222)) class333();   //好恶心}~test(){if (nullptr == m_pBlock)return;m_pC1->~class111();m_pC2->~class222();m_pC3->~class333();free(m_pBlock);}void memberNames(){if (nullptr == m_pBlock)return;m_pC1->name();m_pC2->name();m_pC3->name();}private:char*     m_pBlock;class111* m_pC1;class222* m_pC2;class333* m_pC3;};void main(){test t;t.memberNames();}

虽然改善了性能,但感觉有点恶心,通用性不高。而且对于一个懒惰的程序员而言,要在每个类都写这样的代码,真心不爽。
所以,我写了一些模板,和宏。让编译器去做那些繁杂的事情。修改后的代码,如下。
#define LOGOUT(a)class test{DECLARE_SMART_MEM(test);public:test(){USE_SMART_MEM(test, m_pC1, m_pC2, m_pC3);if (IS_TRUE(SMART_MEM_FAIL(test))){LOGOUT("内存分配失败");return;}m_pC1->class111::class111();   //m_pC1 = new(m_pC1) class111(); 我喜欢这样写,因为不需要手动delete,所以m_pC2->class222::class222();   //                               我不喜欢出现newm_pC3->class333::class333();}~test(){if (IS_TRUE(SMART_MEM_FAIL(test)))return;m_pC1->~class111();m_pC2->~class222();m_pC3->~class333();}void memberNames(){if (IS_TRUE(SMART_MEM_FAIL(test)))return;m_pC1->name();m_pC2->name();m_pC3->name();}private:class111* m_pC1;class222* m_pC2;class333* m_pC3;};void main(){test t;t.memberNames();}

非常的简洁,哦,你可能会说,会不会内存泄漏啊。呵呵呵,我会告诉你不会。不需要 new ,也不需要delete. 为什么不需要delete尼。因为我采用了auto_ptr的原理
嗯,核心代码。

/**author: zyb*   \brief  smart_mem*/#pragma once#include <stdlib.h>#include <new.h>#include "commonMacro.h"#define _MYLIBSTART namespace mylib{#define _MYLIBEND}_MYLIBSTARTtemplate<class _Ty>struct is_pointer{static constexpr bool value = false;};template<class _Ty>struct is_pointer<_Ty*>{static constexpr bool value = true;};template<class _Ty>class auto_free{public:typedef auto_free<_Ty> _Myt;auto_free(_Ty* _ptr = 0) noexcept{_Myptr = _ptr;}~auto_free() noexcept{if (NOT_NULLPTR(_Myptr))free(_Myptr);}_Myt& operator= (_Ty* _ptr) noexcept{if (NOT_NULLPTR(_Myptr))free(_Myptr);_Myptr = _ptr;return *this;}bool isnullptr() const noexcept{return IS_NULLPTR(_Myptr);}private:_Ty* _Myptr;};template<class _Ty>constexpr int pointer2class_size(_Ty* pParam)  noexcept{(pParam);return sizeof(_Ty);}template<class _Ty, class... _Types>constexpr int params_size(_Ty& first, _Types&... params)  noexcept{static_assert(is_pointer<_Ty>::value, "params must be pointer");return pointer2class_size(first) + params_size(params...);}constexpr int params_size()  noexcept{return 0;}template<class _Ty, class... _Types>inline void init_params(char* pBlock, _Ty& first, _Types&... params) noexcept{first = (_Ty)pBlock;init_params(pBlock + pointer2class_size(first), params...);}inline void init_params(char* pBlock) noexcept{(pBlock);}template<class _Ty, class... _Types>void* smart_mem_alloc(_Ty& first, _Types&... params) noexcept{char* pBlock =(char*)malloc(params_size(first, params...));if (IS_NULLPTR(pBlock))return nullptr;init_params(pBlock, first, params...);return (void*)pBlock;}_MYLIBEND#undef _MYLIBSTART#undef _MYLIBEND#define DECLARE_SMART_MEM(classname)\private:\mylib::auto_free<void> m_##classname##_blockptr#define USE_SMART_MEM(classname,...)\m_##classname##_blockptr =\mylib::smart_mem_alloc(__VA_ARGS__)#define SMART_MEM_FAIL(classname)\m_##classname##_blockptr.isnullptr()

我不知道,你是否能看懂上面的代码,因为,需要你掌握c++11特性的知识,还需要,你对std库的源码,有一定的了解。
为什么用模板尼,主要是为了部分代码的结果,能在编译时就产生。而不是运行时。这份代码,看起来挺多的,但他的执行
效率很高。通用性很强。而且我直接定义了三个宏。能够更方便的在 “ 适合 ” 的各个类中使用。
auto_free,吸取auto_ptr 的部分特性,能够自动释放内存。 param_size能在编译时,直接计算出,需要分配的内存大小。
使用malloc  free 是摈弃掉 new 的异常,因为new 其实内部就是使用malloc函数 我在前一篇已经详细分析过。
来看一下,是如何高效的吧。
反汇编:

 test()
00D41080  push        ecx 
00D41081  push        esi 
00D41082  mov         esi,ecx 
00D41084  push        edi 
  USE_SMART_MEM(test, m_pC1, m_pC2, m_pC3);
00D41085  push        0Ch                         //param_size直接计算出三个变量的总大小。sizeof(class111) = 4  sizeof(class222) = 4  sizeof(class333) = 4   4+4+4 = 0ch
 {
00D41087  mov         dword ptr [esi],0  
00D4108D  call        dword ptr [__imp__malloc (0D42054h)]    //malloc(0ch) 分配内存
00D41093  mov         edi,eax 
00D41095  add         esp,4 
00D41098  test        edi,edi 
00D4109A  je          test::test+2Bh (0D410ABh)                        
00D4109C  lea         eax,[edi+4] 
00D4109F  mov         dword ptr [esi+4],edi 
00D410A2  mov         dword ptr [esi+8],eax 
00D410A5  add         eax,4 
00D410A8  mov         dword ptr [esi+0Ch],eax                    
00D410AB  mov         eax,dword ptr [esi] 
00D410AD  test        eax,eax 
00D410AF  je          test::test+3Bh (0D410BBh) 
00D410B1  push        eax 
00D410B2  call        dword ptr [__imp__free (0D4204Ch)] 
00D410B8  add         esp,4 
00D410BB  mov         dword ptr [esi],edi                               //保存分配的内存
  if (IS_TRUE(SMART_MEM_FAIL(test)))
00D410BD  test        edi,edi 
00D410BF  je          test::test+83h (0D41103h)                //跳转为分配失败
  {
   LOGOUT("内存分配失败");
   return;
  }

上面的汇编语句就是 上面代码的反汇编。所占的汇编语句,没有源码看起来多。这就是采用模板,在预编译时,更好的优化了代码。
ok ,终于敲完了。睡觉。。。。。。。。。


原创粉丝点击