2

これらの構造体があるとします:

struct Base{
 ...
}

struct Derived:public Base{
 //everything Base contains and some more
}

これらの配列を複製してから変更したい関数があります。

void doStuff(Base *data, unsigned int numItems){
 Base *newdata = new Base[numItems];
 memcpy(newdata, data, numItems*sizeof(Base));
 ...
 delete [] newdata;
}

しかし、この関数を次のように使用した場合:

Base *data = new Derived[100];
doStuff(data, 100);

それはうまくいきませんよね?Derived1 は Base よりも大きいため、Base に割り当てるとメモリが不足するのでしょうか?

4

6 に答える 6

4

丁度。これはスライス問題のバリエーションです。

于 2010-02-18T22:19:24.117 に答える
2

ポインターを使用し、コピー コンストラクターを使用する必要があります。structまた、基本的なデータ構造以外にキーワードを使用しないでください。技術的には機能しますが、作成しているのはクラス階層なので、classキーワードを使用してください。

Derived はより大きく、意図と目的のために、Base主にインターフェイスで互換性のある完全に異なるオブジェクトであるため、これは機能しません。代わりに、コピー コンストラクターを設定し、 < algorithm > などのライブラリを使用してテンプレート化されたアクションを実行する必要があります。

さらに、正当な構文 (つまり ) であるにもかかわらず、それが機能しない理由Base * = Derived *は、 a がインデックスを作成するよりも大きなオブジェクトを割り当てているためBase *です。これにより、メモリが間違った場所に書き込まれてメモリが破損する可能性があります。

たとえば、Baseオブジェクトが 4 バイトの場合、C++ は 4 バイトごとに配列のインデックスを作成しますが、実際に割り当てられたDerivedオブジェクトが 8 バイトの場合、オブジェクト境界の途中でインデックスを作成しているため、メンバー変数は正しい場所を指していません。記憶に。

配列でのクラス階層の使用:

Base *objects[100];
for (int i = 0; i < 100; i++)
    objects[i] = new Derived();

さらに、物事を管理しやすくするために、生のポインターの代わりにスマート ポインター メカニズムとテンプレート リストを使用することもできます。

于 2010-02-18T22:30:40.537 に答える
1

はい!あなたが正しい。それはうまくいきません。Derived1 は Base よりも大きいため、Base に割り当てるだけでは十分なメモリがありません。

于 2010-02-18T22:19:06.977 に答える
0

テンプレートを使用すると、これを簡単に行うことができます。

template< class T >void doStuff(T *data, unsigned int numItems)
{
    T *newdata = new T[numItems];
    memcpy( newdata, data, sizeof( T ) * numItems );
    ...
    delete [] newdata;
}

コメントに従って編集します。混合コレクションに対してこれを実行したい場合、事態はすぐに複雑になります... 考えられる解決策の1つは次のとおりです。

struct Base{
    virtual Base* CopyTo()      { return new Base( *this ); }
};

struct Derived:public Base{
    virtual Derived* CopyTo()   { return new Derived( *this ); }

};

void doStuff( Base** ppArray, int numItems )
{
    Base** ppNewArray   = new Base*[numItems];
    int count = 0;
    while( count < numItems )
    {
        ppNewArray[count] = ppArray[count]->CopyTo();
        count++;
    }

    // do stuff

    count = 0;
    while( count < numItems )
    {
        delete ppNewArray[count];
        count++;
    }
    delete[] ppNewArray;
}
于 2010-02-18T22:21:24.600 に答える
0

はい。Derived のメモリ フットプリントは Base のメモリ フットプリントよりも大きいため、コピーは意図したとおりに機能しません。

于 2010-02-18T22:35:30.680 に答える
0

Derivedの配列は Base の配列ではありません

a を aにアップキャストする必要がある場合は、ポインタの配列を Base に割り当てるか、できればDerived*a に割り当てる必要があります。Base*vector<Base*>

vector<Base*> data(100);
// Initialize the elements
for (vector<Base*>::iterator it = data.begin(); it != data.end(); ++it)
{
    *it = new Derived;
}

doStuff(data);

// Destroy the elements
for (vector<Base*>::iterator it = data.begin(); it != data.end(); ++it)
{
    delete *it;
}

そして、あなたのdoStuff関数は次のようになります:

void doStuff(const vector<Base*>& data)
{
    // Copy the objects, not the pointers
    vector<Base*> newdata;
    for (vector<Base*>::const_iterator it = data.begin();
         it != data.end(); ++it)
    {
        newdata.push_back((*it)->clone());
    }

    // Do stuff

    // Destroy the copies
    for (vector<Base*>::iterator it = newdata.begin();
         it != newdata.end(); ++it)
    {
        delete *it;
    }
}

Baseオブジェクトがまたはであるかどうかを知らずにオブジェクトをコピーするには、仮想コンストラクタイディオムDerivedを使用する必要があることに注意してください。次のように変更する必要があります。BaseDerived

struct Base{
    ...
    virtual Base* clone() const { return new Base(*this); }
    virtual ~Base() {}
};

struct Derived : public Base {
    ...
    Derived* clone() const { return new Derived(*this); }
};
于 2010-02-18T22:36:52.457 に答える