In my program i have a very simple structure to represent a map in an RPG game. I have a Map class, with a 2d Array, "Grid", made out of Area objects, like so:
#pragma once
#include "Area.h"
class Map
{
public:
Map();
~Map();
Area Grid[10][10];
};
Then in the Map constructor:
Map::Map()
{
for (int y = 0; y < 10; y++) {
for (int x = 0; x < 10; x++) {
Grid[x][y] = Area();
}
}
}
I would like for the Area objects to be able to access certain values from the Map object, and I've read that i could include a reference to the map class when constructing the area object, so that it can refer back to its parent. But to do this, i would have to
#include "Map.h" in Area.h, which would create an include loop, and just not be very good in general. So how would i go about injecting a reference to the area's parent in each area object? Thanks for any help in advance.
// Area.h
#pragma once
struct Map;
struct Area {
Map* map = nullptr;
Area() {}
explicit Area( Map* m) : map(m) {}
};
Note that you may want to have some of the functions of Area defined in Area.cpp (that includes Map.h). I just left it out for simplicity of example code.
// Map.h
#pragma once
#include "Area.h"
struct Map
{
Map();
~Map();
Area Grid[10][10];
};
// Map.cpp
#include "Map.h"
Map::Map()
{
for (int y = 0; y < 10; y++) {
for (int x = 0; x < 10; x++) {
Grid[x][y] = Area(this);
}
}
}
Map::~Map() {}
// main.cpp
#include "Map.h"
int main()
{
Map m;
return 0;
}
Related
I want to pass my zero-filled nullMat multidimensional pointer array to a function called setGraphics in the constructor. In whatever way I try to do so I get "argument incompatible" or something in the lines of that.
The cpp file Test.cpp
#include "Test.h"
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGraphicsView>
Test::Test(QWidget *parent) : QMainWindow(parent)
{
ui.setupUi(this);
lifeMatrix(5);
setGraphics(nullMat(), 50);
}
void Test::setGraphics(bool **lifeMat, int scale)
{
QGraphicsScene* scena = new QGraphicsScene();
ui.view->setScene(scena);
int lDydis = 50; // nusistato langelio dydis, bus galima pakeist i variable
for (int row = 0; row < scale; row++)
{
for (int column = 0; column < scale; column++)
{
QGraphicsRectItem* rect = new QGraphicsRectItem();
rect->setRect(row * lDydis, column * lDydis, lDydis, lDydis);
if (lifeMat[row][column] == 1) rect->setBrush(Qt::white);
if (lifeMat[row][column] == 0) rect->setBrush(Qt::black);
rect->show();
scena->addItem(rect);
}
}
}
and the header file Test.h
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_Test.h"
class Test : public QMainWindow
{
Q_OBJECT
public:
Test(QWidget *parent = Q_NULLPTR);
void nullMat(bool **lifeMat) { for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) lifeMat[i][j] = 0; };
void setGraphics(bool** lifeMat, int);
int scale; // common, nustato matricos dydi rpagal column/row nariu skaiciu
void lifeMatrix(int scale)
{
// Matricos dydis x*y
bool** lifeMat = new bool* [scale];
for (unsigned int i = 0; i < scale; ++i) lifeMat[i] = new bool[scale];
}
private:
Ui::TestClass ui;
};
I don't know how to solve this one and I have been trying for quite some time.
Any help would be deeply appreciated.
Seems clear you don't understand when to return values from functions. lifeMatrix is creating the matrix so it need to return the matrix. nullMat is just modifying the matrix, so it doesn't need to return the matrix.
Rewrite your code like this
class Test : public QMainWindow
{
...
void nullMat(bool **lifeMat) { ... }
...
bool **lifeMatrix(int scale)
{
// Matricos dydis x*y
bool** lifeMat = new bool* [scale];
for (unsigned int i = 0; i < scale; ++i) lifeMat[i] = new bool[scale];
return lifeMat;
}
...
}
Test::Test(QWidget *parent) : QMainWindow(parent)
{
ui.setupUi(this);
bool** mat = lifeMatrix(5);
nullMat(mat);
setGraphics(mat, 50);
}
The code still seems to be leaking memory as the matrix is never deleted anywhere. Maybe you actually need to make the matrix a member variable. That would change the code around again.
Consider 2 variables (number of polygons and its coordinates) :
int numberPoly= 2;
float polycoord[18]= {0,50,0,0,0,0,50,0,0,0,50,0,50,50,0,50,0,0};
, a Model class (that is intended to store polygon classes to a list) :
class model{
public:
model();
void affect(int id, int address){
polyclasses[id]=address;
}
private:
string name;
vector<int> polyclasses;
};
, a Polygon class (that I have to sort in Model's polyclasses list) :
class polygone {
public:
polygone();
void affect(int id, int coord){
ABC[id]=coord;
}
private:
int id;
float ABC[9]={0.0};
};
I wanted to code a function (cf. "builder") that instanciate n Polygon classes and sort them (with their memory addresses like an id) in an array ("polyclasses" from Model class). So, I don't arrive. Here is a bit of my builder function not acomplished :
void builder(){
int from = 0; int subfrom = 0;
for(int i=0; i < numberPoly - 1; i++){
from = 0; subfrom = 0;
polygone poly();
!!! need to put that instance in Model's polygon list !!!
...
for(int j=from; j < (polycoord.size())-1; j++){
poly.affect(subfrom, polycoord[j]) ...
subfrom++;
}
from += 3;
}
}
This is for my first c++ project. I'm coding a light 2d engine.
You need to store pointer of instances in your vector and allocate your objects with new keyword. At destructor of your model yo uwill need to deletethe object to avoid a memory leak.
// Model.h
// Class name should begin with uppercase by convention
class Model{
public:
Model();
~Model();
void builder();
// Implementation should go in cpp file
void affect(int id, int address);
private:
// Having a m_ prefix on private variable is useful to make your code more readable so a reader can easily know if a variable is private or not
string m_name;
vector<Polygon*> m_polyclasses;
};
// Polygone.h
class Polygone {
public:
Polygone();
// Don't forget destructor
~Polygone();
// Implementation should go in cpp file
void affect(int id, int address);
private:
int m_id;
// Use std::array in C++ and give meaningful name to your variable
// float m_ABC[9]={0.0}; is replaced by :
std::array<float, 9> m_coordinates;
};
// Model.cpp
void Model::builder() {
int from = 0; int subfrom = 0;
for(int i=0; i < numberPoly - 1; i++){
from = 0; subfrom = 0;
Polygone * poly = new Polygone();
// A pointer of poly is now stored in Model
this->polyclasses.push_back(poly);
// Your polygone object should initialized in the constructor or in a member function of the class Polygone.
for(int j=from; j < (polycoord.size())-1; j++){
poly->affect(subfrom, polycoord[j]) ...
subfrom++;
}
from += 3;
}
}
Model::~Model() {
for(auto p: this->polyclasses) {
// Avoid memory leak
delete p;
}
this->polyclasses.clear();
}
You can also store a std::unique_ptr instead of a plain pointer. In that case you don't need to delete.
I'm trying to make some game engine (I know, should do an actual game instead).
Sadly have defeat at project structure. it looks like this (footage from my fifth attempt):
#include <iostream>
#include <vector>
#include <windows.h>
using namespace std;
class Player;
class Engine {
private:
CHAR_INFO *chiBuffer;
int width = 0;
int height = 0;
vector <Player> players;
public:
Engine(int x = 120, int y = 30) {
chiBuffer = new CHAR_INFO[x*y];
}
void addPlayer(Player player) {
players.push_back(player);
}
void render() {
for (int i = 0; i < players.size(); i++) {
Player *player = &players[i];
player->draw();
}
}
protected:
inline CHAR_INFO& getPixel(int x, int y) { return chiBuffer[x + width * y]; }
};
class Drawable : Engine {
protected:
inline CHAR_INFO& getPixel(int x, int y) { return Engine::getPixel(x, y); }
virtual void draw() = 0;
};
class Player : Drawable {
protected:
int x = 0;
int y = 0;
public:
void draw() {
getPixel(x, y).Char.UnicodeChar = L'*';
}
};
int main() {
Engine *eng = new Engine(120, 30);
Player p;
eng->addPlayer(p);
return 0;
}
I would like to make it easily extensible, so in my mind had been born an idea of creating master class (Engine) sub class Drawable which will have draw() method and later Tickable which would have onTick() etc... But I'm pretty sure I'm doing it so wrong. Could you tell me better idea of doing this? Or make this just working because this gives me too many errors to write it right here (VS 2017)
First, why use this ?
for (int i = 0; i < players.size(); i++) {
Player *player = &players[i];
player->draw();
}
It's the same as: for(...) { players[i].draw() ;}.
Don't use variables and pointers where you don't need to (like in your main, don't use the new keyword: it's pointless. Engine e = Engine(120,30) is ok.).
The for loop on a vector should use Iterators :
for (auto it = begin (vector); it != end (vector); ++it) {
it->draw;
}
In manners of good practice, you should write your code in different files.
Engine.h for Engine declaration, Engine.cpp for implementation.
Same for Player.
When I declare the "Level" object in the "LevelEditor" class definition like so, everything works fine:
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;
};
But when I declare the "Level" object in the "LevelEditor" constructor like so, I get a stack overflow:
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;
};
This makes me wonder what the difference is between declaring a variable in the class definition and in the constructor is besides the fact of the time of defining the variable. Any idea of what the cause could be? and how I could declare the "Level" object in the constructor without having to put anything on the heap?
EDIT:
"Level" class definition in case it is helpful:
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;
};
There are several issues with your code. I will try to address the stack overflow error. The other issue is that your Level class is not safely copyable -- that can be taken care of by utilizing smart pointers such as std::unique_ptr and std::shared_ptr.
First, your classes use 300 x 300 arrays of T, in one case, T is a WorldSprite* the other is int. Arrays this size declared as members will balloon the size of each of your objects that contain them to hundreds of kilobytes in size. This will at some point take a toll on the stack.
So you should remove these definitions, and instead use 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;
};
Here is the Level class with the same type of changes:
#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;
};
Note that the vector replaces the array, and it will use heap memory to initialize. In the Level class, we initialize the vector and set all the entries to -1 in one single call of the vector's constructor.
The reason why this will not hike the size of your objects to very high amounts is that vector will create its data on the heap (unless you have some sort of custom allocator that gets the memory from another source). Thus the size of your classes will be reasonable (probably less than 100 bytes).
The other issue is that your Level class is not safely copyable (neither is the LevelEditor, but I will leave it alone, as the same set of changes can be done).
The problem will be this line:
level = Level(width, height);
The problem with this line is that the assignment operator will be called and the copy constructor may be called. If you look at your Level class, it has a destructor that removes all the pointers from the vectors that contain pointers. This will be disastrous if you copy Level objects, since you will be destroying all of your data due to temporaries being destroyed.
If there is no sense of which Level actually owns the pointers, and it comes down to "whoever is the last man standing is the owner", and you will actually be sharing pointers between Level instances (that's why it's called shared_ptr) then you can use this solution:
#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;
};
Note how there is no destructor code -- there need not be any. The deletion is all done by the shared_ptr, so there is no work for you to do -- everything is managed. What will happen is that the last Level that gets destroyed that you shared the pointers with will do the actual deletion. So when this line is done
level = Level(width, height);
the copying of the Level objects bumps up and down the internal shared_ptr's reference count, leaving you with a reference count of 1 (that is the final level on the left-hand side of the = sign).
See here for usage of std::shared_ptr: http://en.cppreference.com/w/cpp/memory/shared_ptr
Please note that you may want to use std::unique_ptr if ownership is an issue. I suggest you search SO for usages of std::unique_ptr. I showed you std::shared_ptr since it is the most straightforward at this point (but again, may not suit all your needs - YMMV).
I have a TileMap class that has a std::vector<Tile>. While just generating the vector, i notice that the Tiles are getting deleted shortly after creation, thus not letting the TileMap class do anything with them.
TileMap is a kind of information class that will be used by a Stage class for various things, so it will need to access TileMap.tiles() (which returns the mTiles_ TileMap.
TileMap constructor:
TileMap::TileMap(std::vector<int> pTiles, int pWidth):mWidth_(pWidth)
{
for(int i = 0; i < pTiles.size(); i++)
{
int x = (i % mWidth_);
int y = floorf(i / mWidth_);
Tile tile((Tile::TileType)pTiles[i]);
tile.x = x;
tile.y = y;
tile.position(sf::Vector2f(x * Tile::TILE_WIDTH, y * Tile::TILE_HEIGHT));
mTiles_.push_back(tile);
}
}
Previously it was a std::vector<std::shared_ptr<Tile>> but i was seeing if i could get around using pointers. Is there a way to do this?
EDIT: Tile definition added -
class Tile : public SquareCollidableObject
{
public:
enum TileType {
TILE_GRASS,
TILE_OUTSIDE_WALL_TOP_LEFT_OUTER,
TILE_OUTSIDE_WALL_TOP,
TILE_OUTSIDE_WALL_TOP_RIGHT_OUTER,
TILE_OUTSIDE_WALL_LEFT,
TILE_OUTSIDE_WALL_RIGHT,
TILE_OUTSIDE_WALL_BOTTOM_RIGHT_INNER,
TILE_OUTSIDE_WALL_BOTTOM_LEFT_INNER,
TILE_OUTSIDE_WALL_BOTTOM_LEFT_OUTER,
TILE_OUTSIDE_WALL_BOTTOM,
TILE_OUTSIDE_WALL_TOP_RIGHT_INNER,
TILE_OUTSIDE_WALL_TOP_LEFT_INNER,
TILE_OUTSIDE_WALL_BOTTOM_RIGHT_OUTER,
TILE_WALL,
TILE_INSIDE_WALL_TOP_LEFT_INNER,
TILE_INSIDE_WALL_TOP,
TILE_INSIDE_WALL_TOP_RIGHT_INNER,
TILE_INSIDE_WALL_LEFT,
TILE_INSIDE_WALL_RIGHT,
TILE_INSIDE_WALL_BOTTOM_RIGHT_OUTER,
TILE_INSIDE_WALL_BOTTOM_LEFT_OUTER,
TILE_INSIDE_WALL_BOTTOM_LEFT_INNER,
TILE_INSIDE_WALL_BOTTOM,
TILE_INSIDE_WALL_TOP_RIGHT_OUTER,
TILE_INSIDE_WALL_TOP_LEFT_OUTER,
TILE_INSIDE_WALL_BOTTOM_RIGHT_INNER,
TILE_FLOOR
};
Tile(TileType);
virtual ~Tile();
virtual void update(float);
virtual void draw(sf::RenderWindow&, sf::Vector2f);
TileType tileType;
static int TILE_WIDTH;
static int TILE_HEIGHT;
int x;
int y;
// pathfinding
std::shared_ptr<Tile> previousTile;
float g; // cost to tile (total cost from previous tiles + cost to this tile)
float h; // cost to next tile
float f; // g + h
bool walkable;
};
Tile needs to have a copy (or move) constructor and assignment operator for use with std::vector.
nTiles_.push_back(tile) copy-constructs a new Tile object from the local tile.
In that for loop, at each iteration, the local object tile gets constructed, then a copy gets pushed into the vector, and then the local tile gets destructed. This is why destructors get called during the for loop.
One way to avoid this and instead only construct the Tile object that will be in the vector, you could write
TileMap::TileMap(std::vector<int> pTiles, int pWidth):mWidth_(pWidth)
{
for(int i = 0; i < pTiles.size(); i++)
{
int x = (i % mWidth_);
int y = floorf(i / mWidth_);
mTiles_.emplace_back( (Tile::TileType)pTiles[i] );
Tile& tile = mTiles_.back();
tile.x = x;
tile.y = y;
tile.position(sf::Vector2f(x * Tile::TILE_WIDTH, y * Tile::TILE_HEIGHT));
}
}
emplace_back takes the arguments of the Tile constructor, and constructs an object in-place at the end of the vector. back returns a reference to the last item.
If Tile objects are heavy-weight (i.e. copying them is expensive), it may be better to use pointers instead as before, or implement move-constructor and move-assignment operator. std::vector will copy (or move) its items if new items get inserted/erased, or when the vector gets resized.
Also the tiles() function needs to return the vector by reference.
There is 2 reasons of Tile destruction in your code:
The local variable that you copy inside vector, and the internal copy when vector resizes internal memory.
To avoid the former, you have to emplace back the new element; for the later, you have to reserve place in vector. It results in something like:
TileMap::TileMap(const std::vector<int>& pTiles, int pWidth) : mWidth_(pWidth)
{
mTiles_.reserve(pTiles.size());
for(int i = 0; i != pTiles.size(); ++i)
{
const int x = i % mWidth_;
const int y = i / mWidth_;
mTiles_.emplace_back(static_cast<Tile::TileType>(pTiles[i]));
Tile& tile = mTiles_.back();
tile.x = x;
tile.y = y;
tile.position(sf::Vector2f(x * Tile::TILE_WIDTH, y * Tile::TILE_HEIGHT));
}
}
First of all, your TileMap constructor calls .position which isn't a member of the Tile class.
Secondly, #tmlen's answer looks like it works as expected to me. If I run this code:
#include <stdlib.h>
#include <memory>
#include <vector>
#include <iostream>
using namespace std;
class Tile
{
public:
enum TileType {
TILE_GRASS,
TILE_OUTSIDE_WALL_TOP_LEFT_OUTER,
TILE_OUTSIDE_WALL_TOP,
TILE_OUTSIDE_WALL_TOP_RIGHT_OUTER,
TILE_OUTSIDE_WALL_LEFT,
TILE_OUTSIDE_WALL_RIGHT,
TILE_OUTSIDE_WALL_BOTTOM_RIGHT_INNER,
TILE_OUTSIDE_WALL_BOTTOM_LEFT_INNER,
TILE_OUTSIDE_WALL_BOTTOM_LEFT_OUTER,
TILE_OUTSIDE_WALL_BOTTOM,
TILE_OUTSIDE_WALL_TOP_RIGHT_INNER,
TILE_OUTSIDE_WALL_TOP_LEFT_INNER,
TILE_OUTSIDE_WALL_BOTTOM_RIGHT_OUTER,
TILE_WALL,
TILE_INSIDE_WALL_TOP_LEFT_INNER,
TILE_INSIDE_WALL_TOP,
TILE_INSIDE_WALL_TOP_RIGHT_INNER,
TILE_INSIDE_WALL_LEFT,
TILE_INSIDE_WALL_RIGHT,
TILE_INSIDE_WALL_BOTTOM_RIGHT_OUTER,
TILE_INSIDE_WALL_BOTTOM_LEFT_OUTER,
TILE_INSIDE_WALL_BOTTOM_LEFT_INNER,
TILE_INSIDE_WALL_BOTTOM,
TILE_INSIDE_WALL_TOP_RIGHT_OUTER,
TILE_INSIDE_WALL_TOP_LEFT_OUTER,
TILE_INSIDE_WALL_BOTTOM_RIGHT_INNER,
TILE_FLOOR
};
Tile(TileType t):
tileType(t)
{
cout << "Constructing tile\n";
}
virtual ~Tile()
{
cout << "Destructing tile\n";
}
TileType tileType;
static int TILE_WIDTH;
static int TILE_HEIGHT;
int x;
int y;
// pathfinding
std::shared_ptr<Tile> previousTile;
float g; // cost to tile (total cost from previous tiles + cost to this tile)
float h; // cost to next tile
float f; // g + h
bool walkable;
};
class TileMap
{
int mWidth_;
std::vector<Tile> mTiles_;
public:
TileMap(const std::vector<int>& pTiles, int pWidth) : mWidth_(pWidth)
{
mTiles_.reserve(pTiles.size());
for (int i = 0; i != pTiles.size(); ++i)
{
const int x = i % mWidth_;
const int y = i / mWidth_;
mTiles_.emplace_back(static_cast<Tile::TileType>(pTiles[i]));
Tile& tile = mTiles_.back();
tile.x = x;
tile.y = y;
//tile.position(sf::Vector2f(x * Tile::TILE_WIDTH, y * Tile::TILE_HEIGHT));
}
}
};
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<int> tiles;
tiles.push_back(Tile::TileType::TILE_GRASS);
cout << "Creating tilemap\n";
TileMap t(tiles, tiles.size());
cout << "Tilemap created\n";
cout << "Exiting\n";
return 0;
}
I get the following result:
Creating tilemap
Constructing tile
Tilemap created
Exiting
Destructing tile