2011-11-24 6 views
7

以下のプログラムを検討してください。複雑なケースから簡素化されています。 Objクラスの仮想デストラクタを削除しない限り、以前に割り当てられたメモリを削除すると失敗します。仮想デストラクタが存在する場合にのみ、プログラムの出力から2つのアドレスが異なる理由はわかりません。プレースメントnew []のこの使用で何が問題になっていますか? do

// GCC 4.4 
#include <iostream> 

using namespace std; 

class Arena { 
public: 
    void* alloc(size_t s) { 
     char* p = new char[s]; 
     cout << "Allocated memory address starts at: " << (void*)p << '\n'; 
     return p; 
    } 

    void free(void* p) { 
     cout << "The memory to be deallocated starts at: " << p << '\n'; 
     delete [] static_cast<char*> (p); // the program fails here 
    } 
}; 

struct Obj { 
    void* operator new[](size_t s, Arena& a) { 
     return a.alloc(s); 
    } 

    virtual ~Obj() {} // if I remove this everything works as expected 

    void destroy(size_t n, Arena* a) { 
     for (size_t i = 0; i < n; i++) 
      this[n - i - 1].~Obj(); 
     if (a) 
      a->free(this); 
    } 
}; 


int main(int argc, char** argv) { 
    Arena a; 

    Obj* p = new(a) Obj[5](); 
    p->destroy(5, &a); 

    return 0; 
} 

仮想デストラクタが存在する場合にこれは私の実装では、プログラムの出力です:

割り当てられたメモリアドレスは、開始時刻:0x8895008 割り当てを解除するメモリの開始時刻:0x889500c

実行に失敗しました(終了値1)

どのようなプログラムを想定しているのですか? o。私が言ったように、Arenaはさまざまなタイプのメモリのためのインターフェイスであるより複雑なケースから来ています。この例では、メモリはちょうど割り当てられ、ヒープから割り当てが解除されます。

+0

注*マッチングの配置配列は、新しい式が例外をスローする場合には削除する必要があります。 –

答えて

5

thisはあなたがサイズsは5つのObjインスタンスよりも大きいがあることがわかりますchar* p = new char[s];ラインでnewによって返されたポインタではありません。差異(sizeof (std::size_t)でなければならない)は、thisに含まれるアドレスの直前に配列の長さ5を含む追加のメモリにあります。

OK、仕様では、それが明らかに:

http://sourcery.mentor.com/public/cxx-abi/abi.html#array-cookies

2.7配列演算子の新しいクッキーが

は、new演算子は、新しい配列を作成するために使用される場合、クッキーは通常保存されている

覚えておく 割当てられた長さ(配列要素の数)は、正しく割り当てを解除することができます。

配列要素型Tは自明デストラクタ(12.4 [class.dtor])及び通常(アレイ)解放機能を有する場合にクッキーが必要とされない(3.7.3.2 [basic.stc:具体

.dynamic.deallocation])関数は2つの引数をとらない。

ので、デストラクタの仮想 -nessは、あなたが簡単にデストラクタの前にキーワードvirtualを削除することにより、確認することができ、どのような重要なのデストラクタが非自明であるということである、無関係であるとプログラムがクラッシュするのを観察してください。悪寒の答えに基づいて

+0

はい、私は本当にサイズを気にしません。私はこれがオブジェクトを配置するために与えられたポインタalloc()と一致しない理由を気にします。 – Martin

+0

@Martin 'alloc()'は配列の長さに追加のスペースを割り当てていて、配列要素が続くためです。仮想デストラクタがない場合に配列の長さが格納されない理由、C++ ABI仕様をチェックする必要があるのは今は分かりません。 – chill

+0

これはどのような仕様ですか?私のC++仕様にはクッキーに関する記述はありません。 –

0

、あなたはそれが 『安全』にしたい場合は:あなたは*行う

#include <type_traits> 

a->free(this - (std::has_trivial_destructor<Obj>::value ? 1 : 0)); 
+1

実際には、おそらく 'a-> free((std :: size_t *)this - (std :: has_trivial_destructor :: value?1:0));'ですが、厳密にはUDだから私は確信していますそれはいくつかのあいまいなコーナーケースや異なるコンパイラで壊れます;) – chill

関連する問題