c++ - 在类定义中与在构造函数中将对象声明到堆栈上

标签 c++ stack-overflow variable-declaration

当我像这样在“LevelEditor”类定义中声明“Level”对象时,一切正常:

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

但是当我像这样在“LevelEditor”构造函数中声明“Level”对象时,出现堆栈溢出:

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

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

编辑: “级别”类定义,以防有帮助:

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

最佳答案

您的代码存在多个问题。我将尝试解决 stack overflow错误。另一个问题是你的 Level类不可安全复制——这可以通过使用 std::unique_ptr 等智能指针来解决。和 std::shared_ptr .

首先,您的类(class)使用 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;
};

注意vector代替了array,会使用堆内存来初始化。在Level类,我们初始化 vector 并将所有条目设置为 -1在 vector 构造函数的一次调用中。

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


另一个问题是您的 Level类不可安全复制(LevelEditor 也不是,但我会保留它,因为可以完成相同的一组更改)。

问题出在这一行:

level = Level(width, height);

这一行的问题是会调用赋值运算符,可能会调用拷贝构造函数。如果你看看你的 Level类,它有一个析构函数,可以从包含指针的 vector 中删除所有指针。如果您复制 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)。

关于c++ - 在类定义中与在构造函数中将对象声明到堆栈上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29158466/

相关文章:

java - 具有太多 Observable 的 RxJava1 StackOverflow 异常

debugging - 堆栈溢出后,如何在 OCaml 中获得完整的、未截断的堆栈跟踪?

variables - 在 Go var 声明中显式提供类型失败,而隐式工作

c++ - 为什么 boost::thread 构造函数不接受 boost::thread::attributes 参数?

C++ Win32 API 控制消息

c++ - 素数生成器中的 sigabrt 和 sigsegv 错误

c - 如何知道一个c程序的堆栈溢出?

syntax - 如何在 Go 中声明由返回多个值的函数调用实例化的多个变量?

c++ - `List x;` 和 `List x()` 有区别吗

c++ - 强制调用常量版本成员函数