1

これは私のコードの例です:

template <typename T> struct MyStruct {
    T object;
}

template <typename T> class MyClass {
    MyStruct<T>* structPool;
    size_t structCount;

    MyClass(size_t count) {
        this->structCount = count;
        this->structPool  = new MyStruct<T>[count];
        for( size_t i=0 ; i<count ; i++ ) {
            //placement new to call constructor
            new (&this->structPool[i].object) T(); 
        }
    }

    ~MyClass() {
        for( size_t i=0 ; i<this->structCount ; i++ ) {
            //explicit destructor call
            this->structPool[i].object.~T(); 
        }
        delete[] this->structPool;
    }
}

私の質問は、これは安全な方法ですか?ある条件で隠れた間違いをしますか?すべてのタイプのオブジェクト(PODおよび非POD)で機能しますか?

4

4 に答える 4

7

いいえ、コンストラクタとデストラクタの両方が2回呼び出されるためです。あなたがこれを持っているので:

template <typename T> struct MyStruct {
    T object;
}

MyStruct<T>コンパイルを作成すると内部が作成され、Tオブジェクトを削除すると内部Tにデストラクタが自動的に呼び出されます。

この例では、新規配置または明示的なデストラクタ呼び出しは必要ありません。

生のメモリを割り当てる場合は、新しい配置が役立ちます。たとえば、新しいものを次のように変更した場合:

this->structPool  = new char[sizeof(T) * count];

次に、新しく明示的なデストラクタ呼び出しを配置し​​ます。

于 2010-06-07T23:41:22.977 に答える
1

いいえ、それは確かにそれを行うためのリモートで安全な方法でさえありません。new MyStruct<T>[count]非PODの場合、配列内のTMyStruct<T>オブジェクトはすでにデフォルトで構築されています。つまり、objectメンバーのコンストラクターが自動的に呼び出されます。次に、その上で(値の初期化によって)インプレース構築を実行しようとします。結果として生じる動作は未定義です。

同じ問題が削除にも存在します。

あなたは何を達成しようとしていますか?実行するだけでnew MyStruct<T>[count]()(余分な空に注意してください())、配列の各要素に対してすでに値の初期化が実行されます(後で「手動で」実行しようとしていることとまったく同じです)。なぜあなたはインプレース建設によってそれをしなければならないと感じますか?

同様に、あなたがするとき

delete[] this->structPool;

MyStruct<T>::object配列内の各メンバーのデストラクタを自動的に呼び出します。手動で行う必要はありません。

于 2010-06-07T23:44:03.137 に答える
0
  1. newを覚えていると、配置であるかどうかに関係なく、常にコンストラクターが呼び出されます。

-つまり、コードはnewを2回使用しました。これにより、コンストラクターが2回呼び出されます。これを避けたい場合は、次のいずれかを行います。

最初の新規をmalloc(または任意の種類のalloc)に変更します

新しい2番目の配置を削除します

  1. 配列内のオブジェクトを削除するには、次の方法が最適です。すべてのオブジェクトのデストラクタを呼び出します。メモリを解放します。

-したがって、次のいずれかを実行できます。

new []を使用している場合は、delete[]を使用してオブジェクトを削除します

mallocと配置を使用している場合は、すべてのデストラクタに電話してCスタイルを無料で実行してください。

于 2010-06-07T23:48:37.830 に答える
0
template <typename T> class MyClass {
    void* structPool;
    size_t structCount;

    MyClass(size_t count)
      : structPool(new char[sizeof(T)*count], structCount(count)
    {
        //placement new to call constructor
        for( size_t i=0 ; i<count ; i++ )
            new (structPool+i*sizeof(T)) T(); 
    }

    ~MyClass() {
        //explicit destructor call
        for( size_t i=0 ; i<structCount ; i++ )
            reinterpret_cast<T*>(structPool+i*sizeof(T))->~T(); 
        delete[] structPool;
    }
}

これは例外セーフではないことに注意してください。コンストラクタの1つが例外を引き起こした場合、すでに構築されているオブジェクトのデストラクタを呼び出さず、メモリをリークします。デストラクタの1つがスローすると、これも失敗します。

std::vectorこれを正しく行う方法を確認するには、お気に入りのstdlib実装を見てください。しかし、それは質問につながります:なぜあなたはそもそもこれをしたいのですか?
Astd::vectorはすでにこれをすべて行っており、正しく行っています。箱から出してすぐに使用でき、コードを見ている人なら誰でもすぐに理解できます。

template <typename T> class MyClass {
    std::vector<T> data_;
    MyClass(size_t count) : data() {data.resize(count);}
    //~MyClass() not needed
}
于 2010-06-08T07:05:25.050 に答える