C++placement構文の理解

6782 ワード

最近のグループ読書活動はplacement newとplacement deleteについてもっと深く理解させた.
new式について
C++は、newキーワードとdeleteキーワードを提供し、それぞれメモリ領域の申請と解放に使用され、new式の構文は以下の通りである.
new new-type-id ( optional-initializer-expression-list )

本の上で、new表現は2つのことをします
  • スタック(heap)空間に空間を申請し、大きさはsizeof(new-type-id)
  • に等しい.
  • 出願の空間上にオブジェクトを構築する、すなわち、呼び出しオブジェクトの構築関数
  • また、ユーザーがカスタムメモリ領域でオブジェクトを構築する場合は、次の構文で別のnew式を呼び出すことができます.
    new ( expression-list ) new-type-id ( optional-initializer-expression-list )
    

    具体的には、次のように使用されます.
    void *buffer = malloc(sizeof(ClassA));
    ClassA *ptr = new(buffer)ClassA();
    

    第2のnew式は、第1の特例のように、ステップ2を完了するだけで、ステップ1をユーザに残す.
    delete式について##
    C++が提供するdelete式の構文:
    delete type_pointer;
    

    本によると、deleteは2つのことを完成したという.
  • 呼び出しオブジェクトの構造関数,
  • オブジェクトが存在するメモリ領域を解放し、システム
  • に戻る
    特に、type_pointerをNULLに等しくするとdelete式も正しく機能するが、同じポインタに対してdeleteを繰り返し呼び出すと未定義のエラーが発生する
    標準operator newとoperator delete##
    C++標準のnew expression内部呼び出しは標準のoperator new()であり、その定義は以下の通りである.
    void * operator new (std::size_t) throw(std::bad_alloc);
    
    operator new()は、標準的なallocation、すなわちメモリ割り当てを完了するオペレータまたは関数である.
    同様に、C++標準のdelete expression内部呼び出しは標準のoperator delete()であり、その定義は以下の通りである.
    void operator delete (void *) throw();
    
    operator delete()オペレータまたは関数でもあり、標準的なdeallocation、すなわちメモリ解放を完了します.
    placement newとplacement delete##
    C++標準のnew式は大部分の需要を完成することができて、しかしすべてではありませんて、例えば:どのように既存のメモリ空間の上でオブジェクトを作成して、標準のnew式はできなくて、C++も直接rawメモリの上でオブジェクトの構造関数を呼び出すことを支持しなくて、そこでplacement newは発生して、名前placementをつけて、その歴史の出所を説明して、 で新しいオブジェクトを構築します.
    もちろん、その場で作成するオブジェクトは一部であり、placement newにはより広範なエピタキシャルがあり、placement new expressionとplacementoperator new()は、通常、placement newとして一般的に混同され、概念が混同されている.
    placementoperator new()とは、まず関数であり、標準operator new()のリロード関数である.wikiでは次のように定義されています.
    The "placement"versions of the new and delete operators and functions are known as placement new and placement delete.
    placement new expressionとは、C++構文に属し、標準的なnew expressionと同様である.異なることに、内部呼び出しは対応するplacement operator new()であり、wikiでは次のように定義されている.
    Any new expression that uses the placement syntax is a placement new expression
    標準new expressionが標準operator new()を呼び出しているように、placement new expressionが呼び出しているのはplacementバージョンのoperator new()で、簡単にストレートに見え、混ざっても問題ないようですが、placement deleteを見てみるとplacementoperator delete()関数を表しています.placement delete expressionは存在しないので、私たちが呼び出すと
    delete pObject;
    

    私たちが呼び出したのは標準operator delete()です.なぜplacement delete expressionがないのか、C++の創設者は次のように説明しています.
    The reason that there is no built-in "placement delete"to match placement new is that there is no general way of assuring that it would be used correctly.
    placement delete expressionがない以上、なぜplacementoperator delete()が必要なのか、重要な原因は、C++がplacementoperator delete()とplacementoperator new()をペアにする必要があるからである.次のように仮定します.
    placement new expressionコンストラクションオブジェクトを呼び出すと、コンストラクション関数に異常が放出されます.この場合、C++は対応するplacementoperator delete()のみを呼び出し、placementoperator new()で取得したメモリリソースを解放します.そうしないと、メモリが漏洩します.次の例では、
    #include 
    #include 
    
    struct A {} ;
    struct E {} ;
    
    class T {
    public:
        T() { throw E() ; }
    } ;
    
    void * operator new ( std::size_t, const A & )
        {std::cout << "Placement new called." << std::endl;}
    void operator delete ( void *, const A & )
        {std::cout << "Placement delete called." << std::endl;}
    
    int main ()
    {
        A a ;
        try {
            T * p = new (a) T ;
        } catch (E exp) {std::cout << "Exception caught." << std::endl;}
        return 0 ;
    }
    

    placement operator new()とplacement operator delete()
    上記はexpressionとoperatorの違いを定義だけで、どのような関数がplacementoperator new()とplacementoperator delete()と呼ばれ、標準operator new()およびoperator delete()との違いにカスタムパラメータが追加する.ただし、以下のルールを守らなければならない.
    placementoperator new()の場合、その最初の関数パラメータはstd::size_tで、申請のメモリのサイズを表す必要があります.placementoperator delete()の場合、その最初の関数パラメータはvoid *である必要があり、解放するオブジェクトポインタを示す.例:
    void * operator new (std::size_t) throw(std::bad_alloc);    //     
    void * operator new (std::size_t, const std::nothrow_t &) throw(); // placement   
    void operator delete (void *) throw(); //     
    void operator delete (void *, const std::nothrow_t &) throw(); // placement   
    

    nothrowバージョンのnewは、placement newとplacement deleteによって実現されるC++標準でもあることに注意してください.対応するnothrowバージョンのnew expressionは次のとおりです.
    T *t = new(std::nothrow) T;
    

    ユーザのカスタムスペース上でオブジェクトを構築することはplacement newの本意であり、C++標準でdefault placementとしても使用される.
    void * operator new (std::size_t, void * p) throw() { return p ; }
    void operator delete (void *, void *) throw() { }
    

    対応するplacement new expressionは、次のように使用されます.
    void *buffer = malloc(sizeof(ClassA));
    ClassA *ptr = new(buffer)ClassA();
    

    种植体系统
    実際のアプリケーションでは、メモリプールが最も多く使用されています.説明する価値があります.メモリプールの定義は次のように仮定します.
    class Arena {
    public:
        void* allocate(size_t);
        void deallocate(void*);
        // ...
    };
    

    placement newとplacement deleteを定義することによってArenaに作用する
    void* operator new(size_t sz, Arena& a)
    {
        return a.allocate(sz);
    }
    void operator delete(void *ptr, Arena& a)
    {
        return a.deallocate(ptr);
    }
    

    オブジェクトの作成:
    Arena a();
    X *p = new(a)X;
    

    今の問題はどのようにdeleteを呼び出して、私達が呼び出すことができないためです
    delete p; 
    

    これにより、operator delete()のplacementバージョンではなく、標準のArenaしか有効になりません.そのためには、次のようにするしかありません.
    p->~X();
    operator delete(p, a);
    

    あるいは2つの操作を1つのtemplateにカプセル化します.
        template void destroy(T* p, Arena& a)
        {
            if (p) {
                 p->~T();       // explicit destructor call
                 a.deallocate(p);
            }
        }
    

    そしてこのように呼び出します
    destroy(p,a);
    

    リファレンス
  • http://en.wikipedia.org/wiki/Placement_syntax
  • http://www.stroustrup.com/bs_faq2.html#placement-delete
  • http://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new