1

我正在用 C++ 设计一个类似于 Minecraft 的游戏,它在内存中保存了大量的地形数据。一般来说,我想将一个数组存储在 [5][4][5][50][50][50] 的内存中。这还不错,因为它相当于大约 100mb 的虚拟内存,因为我的结构只有大约 8 个字节。

但是,我无法找出处理此问题的最佳方法。我确实希望它在虚拟内存中,但显然不在堆栈中。而且我一直在犯一些如何在堆栈上创建这个数组并导致堆栈溢出的错误。我想做的是下面。这只是我拼凑在一起的代码,为您提供了我正在做的事情的示例,我的机器上有语法正确的代码,我只是不想弄乱帖子。

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];
}

但是,每次我这样做时,都会导致堆栈溢出(适当的网站选择对吗?)。我玩过使用基于指针的数组,但是数据在其范围之外丢失时遇到了很多麻烦。

如果有人能给我一些关于如何让我的数据存储在堆而不是堆栈上的见解,或者如果我应该使用其他方式来创建我的数组,我将非常感谢您的帮助。由于开销,我想避免使用向量,但我不确定它有多大。

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++ 中。默认情况下,结构只是一个所有成员都是公共的类;就像一个类,你可以不使用struct标签来引用结构类型的名字。也无需指定void不带参数的函数。

boost::multi_array (在 PigBen 的答案中链接)是原始数组的不错选择。

于 2010-12-16T19:51:26.953 回答
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 回答
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

如果要在堆上创建类,请使用 new 创建它:

Child * c = new Child;

然后当然删除它,或者最好还是使用智能指针。

于 2010-12-16T19:42:38.827 回答
1

为了准确地做你想做的事情,你必须将所有东西声明为指针(以及指向指针的指针指向指针的指针),然后单独分配每一个。

太棒了!

一个更好的选择是简单地在一个块中分配巨大的块,并使用多个变量以及指针算法来到达正确的位置。

编辑:没有注意,也没有注意到你的构造函数。这不仅不是让您的孩子在免费商店中分配的方法,而且是创建引发未定义行为的情况的好方法。当构造函数完成时,您的 Child 将消失,指向它的指针将无效。我想知道您是否不应该在尝试编写游戏之前浏览一些基本教程。

于 2010-12-16T19:43:56.617 回答
0

以下是我如何理解您在示例中展示的内容。我试图保持直截了当。[50][50][50] 的每个 Array 都分配在堆上的一个内存块中,并且仅在使用时分配。还有一个访问代码的例子。没有花哨的提升或任何特别的东西,只是基本的 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;
}

这仍然可以更简单直接,只需在堆上的一个内存块中使用一个 [50][50][50][5][4][5] 块的错误数组,但我会让你弄清楚如果这是你想要的怎么办。

另外,在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 回答