5

newC++での配置構文について質問があります。次の2つのコードスニペットは機能的に同等であり、交換可能に使用できますか(最初のコードスニペットが適切な場合、2番目のコードスニペットを使用する必要があることを意味するものではありません)。

#1

T* myObj = new T();
// Do something with myObj
delete myObj;

#2

char* mem = new char[sizeof(T)];
T* myObj = new (mem) T();
// Do something with myObj
myObj->~T();
delete[] mem;

このような配置構文を使用しているときに、特に注意する必要があることはありますか?

4

5 に答える 5

11

Tコンストラクタまたはスローのデストラクタの場合は動作が異なるため、これらは同等ではありません。

new T()例外をさらに伝播させる前に、割り当てられたメモリを解放します。char* mem = new char[sizeof(T)]; T* myObj = new (mem) T();しません(そして、それが解放されることを確実にするために明示的に何かをしない限り、リークが発生します)。同様に、スローdelete myObjするかどうかに関係なく、常にメモリの割り当てを解除します。~T()

の正確な同等物は次のT* myObj = new T();/*other code*/delete myObj;ようになります。

//When using new/delete, T::operator new/delete
//will be used if it exists.
//I don't know how do emulate this in
//a generic way, so this code just uses
//the global versions of operator new and delete.
void *mem = ::operator new(sizeof(T));
T* myObj;
try {
    myObj = new (mem) T();
}
catch(...) {
    ::operator delete(mem);
    throw;
}
/*other code*/
try {
    myObj->~T();
    ::operator delete(mem);
}
catch(...) {
    //yes there are a lot of duplicate ::operator deletes
    //This is what I get for not using RAII ):
    ::operator delete(mem);
    throw;
}
于 2012-05-14T14:59:14.940 に答える
8

生のメモリを割り当てているので、より近いものは次のようになります。

void *mem = operator new(sizeof(T));
T *myobj = new(mem) T();

// ...

myobj->~T();
operator delete(mem);

::operator new特定のクラスに対してオーバーロードした場合、これはそのクラスを使用することに注意してください。 'をoperator new使用すると、そのクラスnew char []は無視されます。

編集:ここで例外の可能性を無視していることを追加する必要があります。@Mankarseの答えは(私には)その部分をかなりうまくカバーしているようです。

于 2012-05-14T14:58:21.087 に答える
0

基本的なレベルでは、両方の状況で同じことを行っています。つまり、ヒープ上で新しいオブジェクトをインスタンス化し、メモリを解放していますが、2番目のケースでは、(明らかに)新しい配置を使用しています。オペレーター。

この場合、Mankarseが説明したようにコンストラクターがスローした場合の違いを除いて、両方とも同じ結果になります。

また、2番目のケースは実際の使用方法の例ではないため、これらを同じように使用できるとは言えませんplacement newplacement newメモリプールのコンテキストで使用する方がはるかに理にかなっています。独自のメモリマネージャを作成している場合Tは、割り当てられたメモリに複数のオブジェクトを配置できます(私のテストでは、約25%節約する傾向があります) CPU時間)。より現実的なユースケースがある場合placement newは、心配する必要のあることがもっとたくさんあります。

于 2012-05-14T15:00:57.777 に答える
0

さて、あなたはあなたのTオブジェクトのためにメモリを事前に割り当てています。そして、それは大丈夫なはずです。それでも、割り当てられた領域をもう一度再利用するのは理にかなっています。そうしないと遅くなります。

于 2012-05-14T15:01:15.453 に答える
0

はい。あなたの例はこれを示すには単純すぎますが、事前に割り当てたメモリ「mem」は、「myObj」内に格納されているオブジェクトを管理する必要があります。

おそらくもっと良い方法を言えば、シナリオ#1はヒープにスペースを割り当て、そのスペースにオブジェクトを構築します。シナリオ#2は、ヒープ「mem」にスペースを割り当て、そのスペース内のどこかにオブジェクトを作成します。

次に、2番目のオブジェクトを「mem」内の別の場所に配置します。複雑になりますよね?

myObjの構築と破棄は、両方のシナリオで同じように発生します(Mankarseが指摘したように、コンストラクターが例外をスローする場合を除く)が、シナリオ#1では、アロケーターがメモリ管理を処理します。シナリオ#2で。

したがって、「mem」を適切に管理するように注意してください。一般的なアプローチの1つは次のとおりです。

template<class T> void destroy(T* p, Arena& a)
{
        if (p) {
                p->~T();        // explicit destructor call
                a.deallocate(p);
        }
}
于 2012-05-14T15:02:12.230 に答える