2

よくわからない問題について説明が必要です。以下の 2 つのシナリオを使用すると、割り当てられるメモリの量はほぼ同じになると考えられます。ただし、シナリオ 2 ではbad_allocしばらくすると例外が発生し、狂ったようにメモリを食いつぶしているように見えます (プロセスに割り当てられたメモリ量のプロキシとして Windows のタスク マネージャを使用します)。以下は、MSVC10 を使用して Windows 32 ビットでコンパイルされます。

次の基本クラスがあるとします。

template<class T>
class Base
{
protected:
    T x;
public:
    Base() {}
    Base(T _x) : x(_x){}
    virtual bool func() = 0;
};

さて、派生クラスについては:

template<class T>
class Derived : public Base<T>
{
public:
    Derived() {}
    Derived(T _x) :  Base(_x){}
    bool func() { return true; };
};

ここで、2 つのケースを考えてみましょう。まず、Derived クラスの動的配列を割り当て、Derived オブジェクトで埋めます。つまり、次のようになります。

int main()
{
    int size = SOME_LARGE_NUMBER;
    Derived<int>* d = new Derived<int>[size];
    for (int i = 0; i < size; i++)
    {
        d[i] = Derived<int>(i);
    }

    // delete here
}

次に、Base クラスのポインターの動的配列を割り当て、Derived クラスの実際のインスタンスを指すようにします。つまり、次のようになります。

int main()
{
    int size = SOME_LARGE_NUMBER;
    Base<int>** d = new Base<int>*[size];
    for (int i = 0; i < size; i++)
    {
        d[i] = new Derived<int>(i);
    }

    // delete here
}

どちらのシナリオでも SOME_LARGE_NUMBER を 40,000,000 に設定しました。最初のシナリオでは、プログラムは問題なく完了します。2 番目のシナリオでは、bad_alloc 例外が発生します。これは予想される動作なのか、それともここで何かを見落としているのか疑問に思っています。もしそうなら、これを行うより良い方法は何ですか? vector<Base<int>*>と を使用しても同じ問題が発生することに注意してくださいboost::ptr_vector<Base<int>>

4

5 に答える 5

6

そうですね、1 つのブロックに割り当てると、単一の割り当てsizeの の倍のサイズDerivedとわずかなオーバーヘッドが必要になります。ただし、個別に割り当てる場合はsize、ポインターのサイズ (4 バイト) のsize倍、サイズの倍、Derivedおよびsize(プラス 1) 倍の割り当てのオーバーヘッドが必要になります。これは少なくとも 8 バイトです。限界に近い場合、12 倍のsize余分なバイトが、メモリに収まるかどうかの違いになる可能性があります。

アロケーターのオーバーヘッドが正確に 8 バイトであると断言することはできませんが、ポインターを 2 つ未満にすることは非常に複雑であり、Microsoft 標準のアロケーターがそれほど洗練されている可能性はほとんどありません。

std::vectorと は手動で行うのとboost::ptr_vectorまったく同じように考えるので、同じように失敗することに注意してください。これらは、delete が適切な場所で確実に呼び出されるようにする単なるラッパーであり、それ以上のものではありません。

64 ビット ターゲットでは、すべてのオーバーヘッドが 2 倍になります。ポインターは 8 バイトで、アロケーターのオーバーヘッドは通常 2 ポインターです。

于 2011-04-18T12:05:19.420 に答える
4

2 番目の例では、オブジェクトへのポインターとオブジェクト自体の両方を格納するためにメモリを割り当てています。

(sizeof(Base<int>*)+sizeof(Derived<int>))*size

プラットフォームのポインター サイズによっては、最初の例に比べてかなりのオーバーヘッドが発生する可能性があります。

2 番目の例のように各オブジェクトを個別に動的に割り当てることも、ランタイムsizeof(Derived<int>) に完全に一致しない特定のサイズのメモリ ブロックのみを提供する可能性があるため、無駄になる可能性があります。

于 2011-04-18T12:05:20.660 に答える
0

EDIT:私はコードをよく読んでいませんでした.2番目の例のオーバーヘッドは、ポインタ用とオブジェクト自体用の二重のメモリ割り当てにあるようです:)

于 2011-04-18T11:47:51.450 に答える
0

最初のケースでは、1 つのブロックに十分なメモリがないと思います。2 番目のケースでは、ポインターの配列に十分なメモリがあり、メモリ内のさまざまな場所/ブロックをオブジェクトに割り当てるのに十分なメモリがあります。

于 2011-04-18T11:42:04.843 に答える
0

どちらの実装にも明らかな問題はありません (おそらく、今朝十分なコーヒーを飲んでいなかったのでしょう)。デバッガーで割り当てられたメモリを見て、何が起こっているかを確認することをお勧めします。たとえば、例 2 の同じ割り当て項目でクラッシュした場合は、メモリが不足している可能性があります。(これは、最初の実装が機能し、2 番目の実装が機能しない理由を説明していませんが、少なくとも正しい道に進むことができます。)

また、デバッガーで割り当てられたメモリのブロック サイズを比較して、割り当てられたときに実際に同じサイズであることを確認することもできます。

申し訳ありませんが、これ以上提供することはできません。

于 2011-04-18T11:59:05.277 に答える