2015-03-20 4 views
0

Когда я объявляю «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; 
}; 

Но когда я объявляю «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); 
      } 
     } 
     //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; 
}; 

Это заставляет меня задаться вопросом, в чем разница между объявлением переменной в определении класса и в конструкторе кроме того, из время определения переменной. Любая идея о том, что может быть причиной? и как я могу объявить объект «Уровень» в конструкторе, не помещая ничего в кучу?

EDIT: «Уровень» Определение класса в случае, если это полезно:

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

Имеет ли класс Level правильный конструктор копирования? Покажите нам свое определение класса уровня. –

+0

http://ideone.com/inKGMR Это, по крайней мере, количество байтов, которое занимает один из объектов 'LevelEditor'. Пересмотрите свой дизайн. Вы объявляете отдельные объекты размером более 300 Кбайт. – PaulMcKenzie

+0

@RonTang не является конструктором копирования, то C++ автоматически дает классы, не требуя определения вручную? –

ответ

1

Есть несколько проблем с вашим кодом. Я попытаюсь обратиться к ошибке stack overflow. Другая проблема заключается в том, что ваш класс Level не безопасно копируется - об этом можно позаботиться, используя интеллектуальные указатели, такие как std::unique_ptr и std::shared_ptr.

Во-первых, ваши классы используют 300 х 300 массивов из T, в одном случае, T является WorldSprite* другой int. Массивы такого размера, объявленные как члены, будут заполнять размер каждого из ваших объектов, которые содержат их до сотен килобайт. В какой-то момент это скажется на стеке.

Поэтому вы должны удалить эти определения и вместо этого использовать 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 «S счетчик ссылок, оставив вас с счетчиком ссылок 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).

Смежные вопросы