在类定义和构造函数中将对象声明到堆栈上

时间:2015-03-20 02:37:07

标签: c++ stack-overflow variable-declaration

当我宣布" Level" " LevelEditor"中的对象像这样的类定义,一切正常:

class LevelEditor
{
public:
    LevelEditor(int w, int h, Shader* shader)
    {
        width = w;
        height = h;
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                tile[x][y] = new WorldSprite(tileWidth * x, tileHeight * y, tileWidth, tileHeight, shader);
            }
        }
    }

    //...
private:

    //...
    Level level = Level(50, 50);

    WorldSprite* tile[300][300];

    //tile characteristics
    int tileWidth = 50;
    int tileHeight = 50;

    //flags
    bool editing = true;
};

但是当我宣布&#34; Level&#34; &#34; LevelEditor&#34;中的对象像这样的构造函数,我得到一个堆栈溢出:

class LevelEditor
{
public:
    LevelEditor(int w, int h, Shader* shader)
    {
        width = w;
        height = h;
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                tile[x][y] = new WorldSprite(tileWidth * x, tileHeight * y, tileWidth, tileHeight, shader);
            }
        }
        //NOTE: width and height both equal 50
        level = Level(width, height);
    }

    //...
private:

    //...
    Level level;

    WorldSprite* tile[300][300];

    //tile characteristics
    int tileWidth = 50;
    int tileHeight = 50;

    //flags
    bool editing = true;
};

这让我想知道在类定义和构造函数中声明变量之间的区别是除了定义变量的时间之外。知道原因可能是什么吗?以及如何宣布&#34; Level&#34;构造函数中的对象,而不必在堆上放任何东西?

编辑: &#34;级&#34;类定义以防有用:

class Level
{
public:
    Level(int w, int h)
    {
        Worldwidth = w;
        Worldheight = h;

        for (unsigned int y = 0; y < Worldheight; y++)
        {
            for (unsigned int x = 0; x < Worldwidth; x++)
            {
                grid[x][y] = -1;
            }
        }
    }
    Level(){}
    ~Level()
    {
        for (auto it = tiles.begin(); it != tiles.end(); ++it)
        {
            delete *it;
        }
        tiles.clear();

        for (auto it = entities.begin(); it != entities.end(); ++it)
        {
            delete *it;
        }
        entities.clear();
    }

    void draw()
    {

    }
private:

    int Worldwidth;
    int Worldheight;

    int grid[300][300];

    std::vector<Tile*> tiles;
    std::vector<Entity*> entities;
};

1 个答案:

答案 0 :(得分:1)

您的代码存在一些问题。我将尝试解决stack overflow错误。另一个问题是您的Level类不能安全地复制 - 可以通过使用std::unique_ptrstd::shared_ptr等智能指针来处理。

首先,您的课程使用300 x 300 T数组,在一种情况下,TWorldSprite*,另一个是int。声明为成员的此大小的数组将使包含它们的每个对象的大小增加到数百KB的大小。这在某种程度上会对筹码造成影响。

因此,您应该删除这些定义,而是使用std::vector

#include <vector>

class LevelEditor
{
public:
    LevelEditor(int w, int h, Shader* shader) : 
                tile(w,std::vector<WorldSprite*>(h))
                editing(true), width(w), height(h)
    {
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
                tile[x][y] = new WorldSprite(tileWidth * x, tileHeight * y, 
                                             tileWidth, tileHeight, shader);
        }
        level = Level(width, height);
    }
private:
    Level level;
    int width, height;
    std::vector<std::vector<WorldSprite*>> tile;
    bool editing;
};

以下是具有相同更改类型的Level类:

#include <vector>
//...
class Level
{
public:
    Level(int w, int h) : Worldwidth(w), Worldheight(h), 
                          grid(300, std::vector<int>(300, -1))
    {}

    Level(){}

    ~Level()
    {
        for (auto it = tiles.begin(); it != tiles.end(); ++it)
        {
            delete *it;
        }
        tiles.clear();

        for (auto it = entities.begin(); it != entities.end(); ++it)
        {
            delete *it;
        }
        entities.clear();
    }

    void draw()
    {
    }
private:
    int Worldwidth;
    int Worldheight;
    std::vector<std::vector<int> >grid;
    std::vector<Tile*> tiles;
    std::vector<Entity*> entities;
};

请注意,向量替换了数组,它将使用堆内存进行初始化。在Level类中,我们初始化向量并在向量的构造函数的单个调用中将所有条目设置为-1

这不会将对象的大小增加到非常高的数量的原因是向量将在堆上创建其数据(除非您有某种自定义分配器从另一个源获取内存)。因此,类的大小将是合理的(可能少于100个字节)。


另一个问题是你的Level类不能安全地复制(LevelEditor也不是,但我会不管它,因为可以进行同样的更改)。

问题在于这一行:

level = Level(width, height);

这一行的问题是将调用赋值运算符并调用复制构造函数。如果你查看你的Level类,它有一个析构函数,它从包含指针的向量中删除所有指针。如果您复制Level个对象,这将是灾难性的,因为您将销毁由于临时销毁而导致的所有数据。

如果没有Level实际上拥有指针的感觉,那么归结为“谁是最后一个站立的人是所有者”,你实际上将分享 Level实例之间的指针(这就是为什么它被称为 shared_ptr )然后你可以使用这个解决方案:

#include <vector>
#include <memory>
//...
class Level
{
public:
    Level(int w, int h) : Worldwidth(w), Worldheight(h), 
                          grid(300, std::vector<int>(300, -1))
    {}

    Level(){}
    void draw()
    {
    }
private:
    int Worldwidth;
    int Worldheight;
    std::vector<std::vector<int>> grid;
    std::vector<std::shared_ptr<Tile>> tiles;
    std::vector<std::shared_ptr<Entity>> entities;
};

注意没有析构函数代码 - 不需要任何代码。删除全部由shared_ptr完成,因此您无需做任何事情 - 一切都得到了管理。会发生的事情是,与共享指针的最后一个Level将被删除。所以当这一行完成时

level = Level(width, height);

Level个对象的复制会在内部shared_ptr的引用计数中上下颠簸,让您的引用计数为1(即左侧的最终level - =符号的手边。

请参阅此处了解std::shared_ptr的使用情况:http://en.cppreference.com/w/cpp/memory/shared_ptr

请注意,如果所有权有问题,您可能需要使用std::unique_ptr。我建议你搜索SO std::unique_ptr的用法。我告诉你std::shared_ptr,因为它在这一点上是最直接的(但同样,可能不适合你所有的需求 - YMMV)。