7

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

// 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から始まります

RUN FAILED(終了値1)

それが何をすることになっているのか尋ねないでください。私が言ったように、それはアリーナがさまざまなタイプのメモリのインターフェースであるというより複雑なケースから来ています。この例では、メモリはヒープから割り当てられ、割り当てが解除されています。

4

2 に答える 2

5

thisnewat行によって返されるポインタではありません。サイズが5インスタンスより大きいchar* p = new char[s];ことがわかります。違い(である必要があります)は、配列の長さ5を含む追加のメモリにあり、に含まれるアドレスの直前にあります。sObjsizeof (std::size_t)this

OK、仕様はそれを明確にしています:

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

2.7配列演算子の新しいCookie

演算子newを使用して新しい配列を作成する場合、Cookieは通常、割り当てられた長さ(配列要素の数)を記憶するために保存されるため、正しく割り当てを解除できます。

具体的には:

配列要素タイプTに自明なデストラクタ(12.4 [class.dtor])があり、通常の(配列)割り当て解除関数(3.7.3.2 [basic.stc.dynamic.deallocation])関数が2つの引数をとらない場合、Cookieは必要ありません。 。

したがって、デストラクタの仮想virtual性は関係ありません。重要なのは、デストラクタが自明ではないことです。これは、デストラクタの前にあるキーワードを削除してプログラムのクラッシュを観察することで簡単に確認できます。

于 2011-11-24T20:55:12.857 に答える
0

悪寒の答えに基づいて、それを「安全」にしたい場合:

#include <type_traits>

a->free(this - (std::has_trivial_destructor<Obj>::value ? 1 : 0));
于 2011-11-24T21:28:08.023 に答える