1

膨大な量の地形データをメモリに保持する Minecraft に似た C++ でゲームを設計しています。一般に、[5][4][5][50][50][50] の配列をメモリに格納したいと考えています。私の構造は約 8 バイトしかないため、仮想メモリは約 100 MB になるので、これは悪くありません。

しかし、これを処理する最善の方法を見つけるのに苦労しています。これを仮想メモリに入れたいのですが、明らかにスタックには入れません。そして、スタック上にこの配列を作成する方法を間違えて、スタックオーバーフローを引き起こし続けています。やりたいことは以下です。これは、私が行っていることの例を示すためにまとめた単なるコードです。私のマシンには正しい構文のコードがあります。投稿を混乱させたくありませんでした。

typedef struct modelBlock
{
    // Information about the blocks
} BLOCK;

typedef struct modelGrid
{
    bool empty;

    BLOCK blocksArray[50][50][50];

} GRID;


class Parent
{
     Child* child;
     Parent(void);
}

Parent::Parent()
{
    Child c;
    child = &c;
}

class Child
{
     GRID grids[5][4][5];
}

ただし、これを行うたびに、スタック オーバーフローが発生します (適切な Web サイトの選択ですよね?)。ポインターベースの配列を使用して遊んでいましたが、その範囲外でデータが失われるという多くの問題がありました。

データをスタックではなくヒープに保存する方法について誰かが私に洞察を与えることができれば、または配列を作成する他の方法を使用する必要がある場合は、本当に助けていただければ幸いです。オーバーヘッドのためにベクトルの使用を避けたいのですが、それがどれほど重要かはわかりません。

4

8 に答える 8

4

boost::multi_arrayを使用する

于 2010-12-16T19:47:43.117 に答える
2

ヒープに何かを割り当てたい場合は、を使用しますnew

#include <memory>

class Parent
{
    std::auto_ptr<Child> child; // use auto_ptr for dynamically-allocated members
    Parent(const Parent&); // You probably don't want to copy this giant thing
public:
    Parent();
};

Parent::Parent()
  : child(new Child) // initialize members with an initializer list
{
}

また、C と C++ のスタイルを混在させないようにしてください。やる理由がない

typedef struct blah{ ... } BLAH;

C++で。構造体は、デフォルトですべてのメンバーが public である単なるクラスです。structクラスと同じように、タグを使用せずに構造体型の名前を参照できます。voidパラメータをとらない関数についても指定する必要はありません。

boost::multi_array (PigBen の回答にリンクされています) は、生の配列よりも適切な選択です。

于 2010-12-16T19:51:26.953 に答える
1

クラスをヒープ上に作成する場合は、new で作成します。

Child * c = new Child;

もちろん、それを削除するか、スマートポインターを使用することをお勧めします。

于 2010-12-16T19:42:38.827 に答える
1

やろうとしていることを正確に行うには、すべてをポインター (およびポインターへのポインターへのポインターへのポインター) として宣言し、それぞれを個別に割り当てる必要があります。

サックス!

より良いオプションは、単純に巨大なブロックを 1 つのチャンクに割り当て、複数の変数とポインター演算を使用して正しい場所に到達することです。

編集:注意を払っておらず、コンストラクターに気づきませんでした。これは、Child をフリー ストアに割り当てる方法であるだけでなく、未定義の動作を引き出す状況を作成する優れた方法でもあります。コンストラクターが完了するとあなたの子はなくなり、それへのポインターは無効になります。ゲームを作成する前に、いくつかの基本的なチュートリアルを実行するべきではないのでしょうか。

于 2010-12-16T19:43:56.617 に答える
1

これは、ブースト依存関係なしで機能し、構築できるものです。欠点の 1 つは、参照要素の [][][] スタイルの使用を削除することですが、それはわずかなコストであり、追加することができます。

template<class T>
class Matrix {
    unsigned char* _data;
    const size_t _depth;
    const size_t _cols;
    const size_t _rows;
public:
    Matrix(const size_t& depth, const size_t& rows, const size_t& cols):
        _depth(depth),
        _rows(rows), 
        _cols(cols) {
        _data = new unsigned char [depth * rows * cols * sizeof(T)];
    }
    ~Matrix() {
        delete[] _data;
    }
    T& at(const size_t& depthIndex, const size_t& rowIndex, const size_t& colIndex) const {
        return *reinterpret_cast<T*>(_data + ((((depthIndex * _cols + colIndex) * _rows) + rowIndex) * sizeof(T)));
    }
    const size_t& getDepth() const {
        return _depth;
    }
    const size_t& getRows() const {
        return _rows;
    }
    const size_t& getCols() const {
        return _cols;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Matrix<int> block(50, 50, 50);
    size_t d, r, c;
    for (d = 0; d < block.getDepth(); d++) {
        for (r = 0; r < block.getRows(); r++) {
            for (c = 0; c < block.getCols(); c++) {
                block.at(d, r, c) = d * 10000000 + r * 10000 + c;
            }
        }
    }
    for (d = 0; d < block.getDepth(); d++) {
        for (r = 0; r < block.getRows(); r++) {
            for (c = 0; c < block.getCols(); c++) {
                assert(block.at(d, r, c) == d * 10000000 + r * 10000 + c);
            }
        }
    }
return 0;
}
于 2010-12-16T20:23:31.163 に答える
1

より小さな例 (一般的な原則をより明確にするために、すべての構造体の名前が変更されています)。「Bloe」構造体は、ヒープに割り当てたい構造体であり、これは「new」を使用して実現されます。

   struct Bla {
        int arr[4][4];
   };

   struct Bloe {
        Bla bla[2][2];
   };

   int main()
   {
        Bloe* bloe = new Bloe();
        bloe->bla[1][1].arr[1][1] = 1;
        return 0;
   }
于 2010-12-16T21:54:12.353 に答える
0

以下は、あなたがあなたの例でやろうとしていることをあなたが示したことを私がどのように理解したかです。私はそれを単純に保つように努めました。[50] [50] [50]の各配列は、ヒープ上の1つのメモリチャンクに割り当てられ、使用された場合にのみ割り当てられます。アクセスコードの例もあります。派手なブーストや特別なものはなく、基本的なC++だけです。

#include <iostream>

class Block
{
    public:
    // Information about the blocks
    int data;
};


class Grid
{
    public:
    bool empty;
    Block (*blocks)[50][50];

    Grid() : empty(true) {
    }

    void makeRoom(){
        this->blocks = new Block[50][50][50];
        this->empty = false;
    }

    ~Grid(){
        if (!this->empty){
            delete [] this->blocks;
        }
    }
};


class Parent
{
    public:
    Grid (* child)[4][5];

    Parent()
    {
        this->child = new Grid[5][4][5];
    }

    ~Parent()
    {
        delete [] this->child;
    }
};


main(){
    Parent p;
    p.child[0][0][0].makeRoom();
    if (!p.child[0][0][0].empty){
        Block (* grid)[50][50] = p.child[0][0][0].blocks;
        grid[49][49][49].data = 17;
    }

    std::cout << "item = " 
              << p.child[0][0][0].blocks[49][49][49].data 
              << std::endl;
}

これはさらに単純でわかりやすく、ヒープ上の1つのメモリチャンクで[50] [50] [50] [5] [4] [5]ブロックのバグ配列を1つ使用するだけですが、理解させていただきますこれがあなたが望むものである場合はどうでしょうか。

また、クラスParentでの動的割り当ての使用は、スタックにインストールされたヒープを使用することのみを目的としていますが、このような小さな配列(5 * 4 * 5ポインター)の場合、スタックへの割り当ては問題にならないため、次のように記述できます。 。

class Parent
{
    public:
    Grid child[5][4][5];
};

使用方法を変更することなく。

于 2010-12-16T22:32:29.693 に答える
0

これを行うには、すべてのデータをバイナリ ファイルに入れました。データのオフセットを計算し、必要に応じて seek() と read() を使用してデータを取得しました。open() 呼び出しは非常に遅いため、プログラムの存続期間中はファイルを開いたままにしておく必要があります。

于 2010-12-16T19:43:17.153 に答える