新,operator newとplacement new

6234 ワード

一、newとdeleteのプロセス:
すべての説明を行う前に、まず2つの点を理解します.
第一に、newもdeleteもC++のキーワードです
第二に、newはリロードされず、その動作は常に一致している(deleteは同じで、順序は逆):(1)operator newを先に呼び出してメモリを割り当てる
(2)呼び出しコンストラクタでそのメモリ内のオブジェクトを初期化する
(3)該当するポインタを返す
 
二、new、operator newとplacement newの関係
 
1.new:リロードできません.その動作は常に一致しています(deleteは同じで、順序は逆):(1)operator newを呼び出してメモリを割り当てます.
(2)呼び出しコンストラクタでそのメモリを初期化する
 
2.operator new:operator+のように再ロードできます.クラスにoperator newがリロードされていない場合は、スタックの割り当てを完了するためにグローバル::operator newを呼び出します.同理operator new[],operator delete,operator delete[]もリロードできます.
 
3.placement new:operator newのリロードバージョンです.役割は、あらかじめ定義されたメモリの場所にオブジェクトを構築することです.以下詳しく説明します.
 
三、placement new詳細
次の文章はplacement newの使用を詳しく説明します-by趙湘寧.
事前に定義されたメモリの場所でオブジェクトを構築する方法について、C++の質問をすることがよくあります.予め定義されたメモリバッファでオブジェクトを構築するには、多くの有用なアプリケーションがあります.たとえば、カスタムゴミ収集器は、大きな事前割り当てメモリバッファを使用して、ユーザーがこのバッファでオブジェクトを構築することができます.これらのオブジェクトが不要になった場合、ストレージスペースは自動的に回収されます.
この技術は時間重視の応用にも有用である.予め割り当てられたメモリバッファでオブジェクトを構築するのは、プログラム割り当て操作自体が貴重な時間を無駄にしないため、「時間定数」操作です.また、システムに十分なメモリがない場合、動的メモリ割り当てが失敗する可能性があることにも注意してください.したがって,タスクを重視するアプリケーションに対しては,十分なバッファを予め割り当てることが避けられない場合がある.
多くのアプリケーションでは、所定の時間に異なるタイプのオブジェクトを構築する必要があります.このような例を考えてみると、GUIアプリケーションはユーザーの入力に応じて、毎回、異なるダイアログボックスを表示し、メモリの割り当てと解放を繰り返すことができ、このアプリケーションは事前にメモリバッファを作成し、このバッファの中で異なるタイプのオブジェクトを繰り返し構築し、破棄することができる.
C++は、予め決定されたメモリ位置でオブジェクトを構築するタスクを容易にするためのいくつかの特徴を提供します.これらの特徴には、「位置決めnew」(placement new)操作と呼ばれる特殊な形式のnewオペレータと、明示的な解析処理が含まれています.実装方法は以下の通りです.
ステップ1:指定したタイプのオブジェクトを格納するのに十分なメモリバッファを割り当てます.異なるタイプのオブジェクトを構築するたびに、少なくとも最大のオブジェクトが占める空間のサイズでバッファを割り当てる必要があります.プリ割り当てされたバッファは、使用可能なメモリ領域に割り当てられた純粋な文字配列です.
char * buff = new char [sizeof (Foo) ];  

バッファが割り当てられると、バッファに各タイプのオブジェクトを構築できます.このためには、特別バージョンのnewオペレータ(「位置決めnew」)を使用して、バッファアドレスをplacement newのパラメータとします.placement newを使用するには、標準ヘッダファイルを含める必要があります.次のコード・スライスでは、placement new操作を使用してメモリ・アドレスbuff上にクラス型Fooのオブジェクトを構築します.
#include <new>
Foo * pfoo = new (buff) Foo; //  new   buff      Foo   

Placement newは、以前に割り当てられたバッファアドレスをパラメータとし、このバッファに所定のタイプのオブジェクトを構築します.彼は、通常のポインタの使用と変わらないオブジェクトを構築するポインタを返します.
        unsigned int length = pfoo->size();
        pfoo->resize(100, 200);
        length = pfoo->size();  
このオブジェクトが不要になった場合、その構造関数を明示的に呼び出して空間を解放する必要があります.このことをするにはいくつかのテクニックがあります.多くの人がオブジェクトが自動的に破棄されると勘違いしているので、間違っています.事前に割り当てられたバッファで別のオブジェクトを構築する前に、またはバッファを解放する前に、構造関数を明示的に呼び出すことを忘れた場合、プログラムは予想できない結果を生じます.明示的なアナライザ宣言は次のとおりです.
pfoo->~Foo(); //          

すなわち、明示的なアナライザは、通常のメンバー関数呼び出しと同様に、名前が通常のメンバー関数と少し異なるだけです.オブジェクトが破棄されると、割り当てられたメモリに別のオブジェクトを再構築できます.実際には、このプロセスは、オブジェクトを構築し、破棄し、割り当てられたバッファを再利用して新しいオブジェクトを構築することを制限なく繰り返すことができます.
事前定義されたバッファが不要になった場合、またはアプリケーションが閉じた場合、事前定義されたバッファを解放する必要があります.delete[]を使用して、事前に定義されたバッファが文字配列であるため、このタスクを完了します.次のコードには、最終バッファの解放を含む完全な例のすべてのステップが含まれます.
#include <new>

void placement_demo(){ 
    //1.      
    char * buff = new char [sizeof (Foo) ];  

    //2.    placement new
    Foo * pfoo = new (buff) Foo;  

    //    
   unsigned int length = pfoo->size();  
    pfoo->resize(100, 200);

    //3.         
    pfoo->~Foo();  

    //4.         
    delete [] buff;  
} 

 
例2:
class CTest {
     /*           */
};

//       
CTest * pTest = new Test;

//              (CTest         default constuctor)
CTest * p10Tests = new Test[ 10]; 

 
この書き方は多くの場合よく機能しますが、newを使用するのは煩わしい場合があります.例えば、配列を再割り当てたい場合や、事前に割り当てられたメモリにオブジェクトを構築したい場合などです.
 
 
たとえば、1つ目の場合、配列を再割り当てする効率は低いです.
//      10      
CTest * pTests = new Test[ 10];


//         11   
CTest * pNewTests = new Test[ 11];

// . . .                     
for ( int i = 0; i < 10; i++)
    pNewTests[ i] = pTests[ i];

delete pTests;

pTests = pNewTests; 

割り当てられたメモリにオブジェクトを作成する場合は、デフォルトのnewオペレータでは実行できません.この問題を解決するにはplacement newで構築することができます.割り当てられたメモリに新しいオブジェクトを作成できます.
 
//bufferはvoidポインタ(void*)
//かっこ[]で囲む部分はオプション
[CYourClass * pValue = ] new( buffer) CYourClass[( parameters)];
 
次の例を示します.
#include <new>

class CTest {
public:
    CTest() {}
    CTest(int) {}
};

int main(int argc, char* argv[]) {

    //          ,           
   char strBuff[ sizeof(CTest) * 10 + 100];

    CTest * pBuffer = (CTest *)strBuff;

    //     
    CTest * pFirst = new (pBuffer) CTest;

    //     
    CTest * pSecond = new (pBuffer + 1) CTest;

    //       ;
   //         
   new (pBuffer + 2) CTest(5);
 
    //       
   CTest * pFourth = new (pBuffer + 3) CTest(10);

    //     
   CTest * pFifth = new (pBuffer + 4) CTest();

    //       (    )
   CTest * pMultipleElements = new (pBuffer + 5) CTest[5];

    return 0;
} 

  
 
独自のメモリバッファを持っている場合や、独自のメモリ割り当てポリシーを実現している場合にplacement newが役立ちます.実際にSTLではplacement newを広く使用してコンテナにメモリを割り当てる.各コンテナクラスには、オブジェクトの構築/解析に使用するディスペンサ(allocator)を説明するテンプレートパラメータがあります.
 
 
placement newを使用する場合は、次の点を覚えておいてください.
 
ヘッダファイル#includeを追加
 
placement newで配列内の要素を構築できます.
 
placement newで割り当てられたオブジェクトを構築するには、構造関数を手動で呼び出す必要があります(「placement delete」は存在しません).構文は次のとおりです.
pFirst->~CTest();
pSecond->~CTest();
 
 
参照ドキュメント:
三者関係:http://www.cnblogs.com/wanghetao/archive/2011/11/21/2257403.html
placement new詳細:http://blog.csdn.net/michaelgs/article/details/862971