I am trying to write a C++ program so solve Rubik's cubes. I have defined four classes: Piece, Edge, Corner, and Cube where Corner and Edge are subclasses of Piece.
The Cube class is defined as such:
class Cube{
private:
Piece* pieces[3][3][3];
public:
Corner* WRG = new Corner(WHITE, RED, GREEN, WHITE);
Corner* WGO = new Corner(WHITE, GREEN, ORANGE, WHITE);
Corner* WOB = new Corner(WHITE, ORANGE, BLUE, WHITE);
Corner* WBR = new Corner(WHITE, BLUE, RED, WHITE);
Corner* YRB = new Corner(YELLOW, RED, BLUE, YELLOW);
Corner* YBO = new Corner(YELLOW, BLUE, ORANGE, YELLOW);
Corner* YOG = new Corner(YELLOW, ORANGE, GREEN, YELLOW);
Corner* YGR = new Corner(YELLOW, GREEN, RED, YELLOW);
Edge* WR = new Edge(WHITE, RED, WHITE);
Edge* WB = new Edge(WHITE, BLUE, WHITE);
Edge* WO = new Edge(WHITE, ORANGE, WHITE);
Edge* WG = new Edge(WHITE, GREEN, WHITE);
Edge* YR = new Edge(YELLOW, RED, YELLOW);
Edge* YB = new Edge(YELLOW, BLUE, YELLOW);
Edge* YO = new Edge(YELLOW, ORANGE, YELLOW);
Edge* YG = new Edge(YELLOW, GREEN, YELLOW);
Edge* GO = new Edge(GREEN, ORANGE, GREEN);
Edge* GR = new Edge(GREEN, RED, GREEN);
Edge* BO = new Edge(BLUE, ORANGE, BLUE);
Edge* BR = new Edge(BLUE, RED, BLUE);
Cube();
~Cube();
void rotateRedClock();
void rotateRedCounter();
void rotateOrangeClock();
void rotateOrangeCounter();
void rotateYellowClock();
void rotateYellowCounter();
void rotateGreenClock();
void rotateGreenCounter();
void rotateBlueClock();
void rotateBlueCounter();
void rotateWhiteClock();
void rotateWhiteCounter();
void doMove(int);
Piece getPieces();
Cube* getChildren();
};
Cube::Cube(){
Piece* pieces[3][3][3] = { { { WRG, WR, WBR }, { GR, NULL, BR }, { YGR, YR, YRB } }, //Red face
{ { WG, NULL, WB }, { NULL, NULL, NULL }, { YG, NULL, YB } }, //Middle section
{ { WGO, WO, WOB }, { GO, NULL, BO }, { YOG, YO, YBO } } }; //Orange face
}
This array is stored inside of a Cube object that can shuffle the pointers in the array and change an orientation parameter for each Piece to handle rotations. From what I can tell, this all should work fine.
The problem starts when I try to return an array of Cube objects that contains all the moves possible from the current state.
If I were programming this in Java, it would look like this:
public Cube[] getChildren(){
Cube children = new Cube[12];
for (int i = 0; i < 12; i++){
children[i] = new Cube(this.getPieces()); //Effectively clone this
children[i].doMove(i); //Does one of the 12 available moves on the cube
}
return children;
}
In C++, however, I can't seem to accomplish this goal. I tried the following:
Cube* Cube::getChildren(){
Cube* children = new Cube[12];
for (int i = 0; i < 12; i++){
children[i] = Cube();
children[i].pieces = pieces;
children[i].doMove(i);
}
return children;
}
But I get an error on this line:
children[i].pieces = pieces;
It says:
error C2106: '=' : left operand must be l-value
I am new to C++, and this error is probably a result of my lack of understanding of certain concepts. I would like to know what I am doing wrong so I can avoid this sort of problem in the future. Thanks in advance!
Don't use raw pointers and don't use new anywhere in your code. The C++ equivalent of Java's Cube[] is std::vector<Cube>. Your sample function could look like:
std::vector<Cube> Cube::getChildren() const
{
// 12 copies of current state
std::vector<Cube> children(12, *this);
for (int i = 0; i < children.size(); i++)
{
children[i].doMove(i);
}
return children;
}
There are other changes to be made before this will work though (as things stand, a huge amount of memory will be leaked, and the "copies" will affect each other).
I'm guessing that the last argument to your Corner and Edge constructor is meant to be some sort of orientation indicator, which you will change when the pieces are rotating. So the variables WRG, WGO are supposed to be mutable and encode the current orientation of that piece.
In C++, objects should have value semantics. In this case, it means that copying the object should do a "deep copy", aka. a clone. There should not ever be code for implementing the copy except for inside the object's constructor.
So the issue of attempting children[i].pieces = pieces inside the getChildren function would never even come up if your objects are designed to use value semantics.
If your object design includes 27 pointers to mutable members of the class, then the default-generated copy constructor will do the wrong thing because:
all "copies" actually have the same pointer to pieces -- there's only one actual set of pieces. (Reorienting the piece in a new cube will reorient the piece in the cube it was copied from)
Even if that is fixed, the "copy" will point to pieces of the original cube, instead of pieces of the copied cube.
So, this is not a good design in C++.
It would be better to just hold the pieces by value. An improvement on your original code (but still not workable yet) would be:
Corner WRG {WHITE, RED, GREEN, WHITE};
Edge WR {WHITE, RED, WHITE};
Pieces *pieces[3][3][3] =
{ {&WRG, &WR, &WBR}, {&GR, nullptr, &BR}, // etc...
With this version, at least there is no memory leak, however there is still the problem that the default-generated copy constructor will have the new cube's Piece pointers copying at the old cube's Pieces.
There are three ways to fix this:
Write a copy-constructor (and a copy-assignment operator) which detects which piece the old cube's pointers point to and make each corresponding pointer in the new cube point to the new piece
Make the pieces be static so there really is only one set of pieces. The orientation will be remembered using separate variables.
Hold the pieces by value instead of by pointer.
1 is what you are trying to do at the moment and actually harder than it looks; even if you fix the compilation error by using std::copy or equivalent, you still have the pointers pointing to the old cube's pieces.
2 is a good idea. If there is only one set of pieces then you can copy around pointers to pieces without causing any trouble. Of course, then you need each Cube to have a new array to represent the current orientation of each piece . That's still simpler than #1 though!
This option also has the big bonus of decimating the memory footprint of each state.
(see below for more comments on #3 and memory usage).
Here is how an implementation of strategy 2 might look like. In the definitions of Edge and Corner take out the field corresponding to the orientation.
class Cube
{
// "static" means only one copy of each for the whole program
// The constructor arguments for each one are placed in the .cpp file
static constexpr Corner WRG, WGO, WOB, /*....*/ ;
static constexpr Edge WR, GR, /*.....*/ ;
Pieces const *pieces[3][3][3] =
{ {&WRG, &WR, &WBR}, {&GR, nullptr, &BR}, // etc...
typedef unsigned char Orientation;
Orientation orientation[3][3][3] = { };
public:
// no default constructor needed if you got the above lists right
// no destructor needed either way
// Cube();
void rotateRedClock();
void rotateRedCounter();
// etc. - you'll probably find it easier to roll all of these into
// a single function that takes the face and the direction as parameter
void doMove(int); // suggest using an enum to describe possible moves
// not necessary getPieces();
vector<Cube> getChildren() const;
};
If you are planning some sort of solving algorithm, you'll want to reduce the memory footprint of each state. So 3 is also a good idea.
If you adopt #2, then doing #3 is not quite so pressing - you can make your code work via approach #2 and optimize it later.
To use #3, you will need to discard the idea of using Piece * polymorphically.
However you don't need this feature anyway because you can tell from the array indices whether the piece is supposed to be a Corner or an Edge. I would suggest just using Piece to be basically what Corner is now but without the orientation; and ignore the third field when you're expecting an edge.
My suggestion would be to replace the table of 27 pointers with a table of 27 bytes . Then, in your .cpp file you'd have a lookup table that you can use to get the piece corresponding to that index.
Check the edit history for my post to see a rough outline of how that might look. (I initially wrote that, but then decided that the current version would be a bit easier to digest!)
In C++, you cannot assign another array to an array of fixed length. You have to copy the values individually. (Not 100% sure, but pretty sure.)
In the definition of class Cube, did you mean a pointer to such an array, i.e. did you mean
typedef Piece PieceArray [3][3][3];
PieceArray * pieces;
In your code, you do not declare a pointer to an array of pieces, but an array of pointers to pieces.
Do yourself a favour and use std::vector.
You didn't provide how Cube::pieces look like pieces and have type int[3][3][3]. The problem that it is C-style array and C very weakly typed, so neither C nor C++ cannot distinguish int[3][3][3] from int[2][2] - type information is lost.
Consider using C++ array types - they define copy constructors and assignment operators and save their sizes internally, so they will do all work for you. Lets take a ride!
C style array
As we already know, this will not work, this is just an example.
int main() {
int i[2][2] = { {0, -1}, {1, 2} };
int b[2][2];
b = i; /* cube.cpp:5:9: error: invalid array assignment */
}
std::vector
For that you will need to define vector of vectors of vectors:
#include <vector>
int main() {
std::vector<std::vector<int> > i = { {0, -1}, {1, 2} };
std::vector<std::vector<int> > b;
b = i;
}
boost::multi_array
(This will require external library)
#include <boost/multi_array.hpp>
int main() {
boost::multi_array<int, 2> i(boost::extents[2][2]);
/* Note that for multi_array we need single-dimentional initializer */
auto _i = { /*{*/ 0, -1 /*}*/,
/*{*/ 1, 2 /*}*/ };
i.assign(_i.begin(), _i.end());
boost::multi_array<int, 2> b(boost::extents[2][2]);
b = i;
}
This may seem more complex than other solutions, but multi_array may be a bit more efficient than vector of vectors.
Related
So I am trying to make a game in c++ with SDL2 and ran into a little problem. In order to pick up a weapon from the ground, after I check if the player is next to the object and has clicked 'E' to pick it up in an inline function from a different namespace, I have to copy its address in a variable saved in the main class and delete the object from the ground so you can't re-pick it up. It seems that after I delete the object, the pointer from the main class gets the value from another object, not that I erased.
This is the main class .h file:
class GameScene : public Scene{
public:
GameScene();
void init(SDL_Renderer* &renderer, int sceneIdx); //initialize variables
void update(int &sceneIdx, double deltaTime, Vector2f screenSize); //update frame-by-frame
void graphics(SDL_Renderer* &renderer); //render objects
void clear(); //only called on exit
private:
std::vector<SDL_Texture*> textures = {}; //all the textures present in the game
std::vector<bool> movements{false, false, false, false}; //main character movements
std::vector<Weapon> droppedWeapons = {}; //dropped pickable weapons on the ground
std::vector<Weapon*> weapons = {nullptr, nullptr}; //slots for the primary and secondary weapon
std::vector<Bullet> bullets = {}; //ssaves all the fired bullets on the map until deletion
std::unordered_map<int, SDL_Rect> positionsAtlas = {}; //offsets in textures and render scales
Cube* cube = nullptr; //main character
int mode = 0; //0=nothing, 1=weapon --unused yet
bool currentWeapon = 0; //saves what weapon is being used(primary or secondary)
int mouseX, mouseY; //mouse position on screen
};
Here is the function call in the .cpp file:
WeaponActions::pickUpWeapons(cube, droppedWeapons, weapons, pickUp/*is E pressed*/, currentWeapon);
And the function in the WeaponActions namespace:
inline void pickUpWeapons(Cube* cube, std::vector<Weapon> &droppedWeapons, std::vector<Weapon*> &weapons, bool pickUp, bool ¤tWeapon)
{
for(unsigned int i=0;i<droppedWeapons.size();i++)
{
bool type = droppedWeapons[i].getType(); //type of weapon(primary or secondary)
if(weapons[type]==nullptr && pickUp) //there is empty space in inventory
{
if(Math::checkCollision(cube->getPos(), cube->getScale(), droppedWeapons[i].getPos(), droppedWeapons[i].getScale())) //check if cube is near weapon
{
weapons[type] = &droppedWeapons.at(i); //save address
droppedWeapons.erase(droppedWeapons.begin()+i); //delete element
currentWeapon = currentWeapon == type ? currentWeapon : type; //change current weapon if necessary
i--;
}
}
}
}
The type element in the Weapon object represent if it is a primary(rifle) or a secondary(pistol) weapon.
What should I do beside creating another object to store the object the pointer is headed to?
Looks like you've got some confusion with memory and what should be a pointer. I'm looking specifically at how you store your weapons here:
std::vector<Weapon> droppedWeapons = {}; //dropped pickable weapons on the ground
std::vector<Weapon*> weapons = {nullptr, nullptr}; //slots for the primary and secondary weapon
When you pickup a weapon you are currently assigning the pointer to the memory location of the Weapon object in your vector, but then immediately removing that weapon! That's why you observed that it appears you equipped the wrong weapon. The weapon you picked up has gone, the std::vector has been resized and the next one has shunted down to fill it's place (std::vectors are required to be hold their elements in contiguous memory). This operation could also leave a dangling pointer e.g. if the weapon you tried to pick up was the last in the droppedWeapons vector your weapons pointer would end up pointing past the end of the vector.
Without changing too much of the rest of your code, you could change droppedWeapons to a vector of Weapon* (weapon pointers), rather than Weapon. Then when you want to pickup the weapon, assign the pointer to your picked up weapons list like so:
weapons[type] = droppedWeapons.at(i); // pass weapon pointer
Followed by removing the element from droppedWeapons as you are already.
Now you are working with pointers you will have to fill droppedWeapons by allocating them with new, and cleanup all your Weapon* at some point with delete.
Before making this change, it may also be a good idea to learn more about stack vs heap memory allocation in c++ if you aren't familiar with it already :)
I have a simple ball class. It takes a color name for its initialization.
Ball::Ball(ColorName color_name) : m_color_name{color_name} {
}
I try to create a 2d-vector board to contain some balls. The width and the height are given outside.
class Board {
private:
int m_width;
int m_height;
std::vector<std::vector<Ball>> m_grids;
public:
Board(int width, int height);
~Board();
};
Board::Board(int width, int height) : m_width{width}, m_height{height} {
m_grids.resize(height);
for (int row_num = 0; row_num < height; row_num ++) {
m_grids[row_num].resize(width);
}
}
Then the compiler complains that the ball class needs default constructor for resizing.
Then I try to change it to pointer.
class Board {
private:
int m_width;
int m_height;
std::vector<std::vector<Ball> *> *m_grids;
public:
Board(int width, int height);
~Board();
};
Board::Board(int width, int height) : m_width{width}, m_height{height} {
m_grids->resize(height);
for (int row_num = 0; row_num < height; row_num ++) {
m_grids->at(row_num)->resize(width);
}
}
Or
Board::Board(int width, int height) : m_width{width}, m_height{height} {
m_grids->resize(height, new std::vector<Ball>[width]);
for (int row_num = 0; row_num < height; row_num ++) {
m_grids->at(row_num)->resize(width);
}
}
They still complain the same thing.
So far I can only create the default constructor for workaround.
Ball::Ball() {
m_color_name = ColorName::Default;
}
But I really don't like it because the balls should not be "default_color". Thy should be like "yellow", "pink" or some color I defined elsewhere.
Is there a way to avoid creating that constructor?
Well, if you don't know what color the balls on a new empty board should be, C++ cannot help you with it. You did not provide a default constructor for the type, so C++ has no way of knowing which default value to use. You have several options here.
One. Use board of std::optional<ColorName>. In this case, you will have an explicit "default" state which is std::nullopt. However, you would need to be careful and always make sure the color is emplaced if you need it.
Two. Do not create the board beforehand. Create it only when you know what color should be there. E.g. it is ok to do m_grids[row_num].push_back(ColorName::Whatever);, you don't need a default constructor for that.
As for pointers (be it raw or smart) - forget it. You don't need pointers in this task.
Instead of making the vectors to pointers (which doesn't make any sense), the entries themselves should be pointers. Since pointers can be nullptr, the vectors will be resizable, like this:
std::vector<std::vector<Ball *>> m_grids_bad;
// Or even better:
std::vector<std::vector<std::unique_ptr<Ball>>> m_grids;
// You can then assign things like this (for example):
m_grids[4][5] = std::make_unique<Ball>(ColorName::RED, 5);
I would strongly recommend you always use std::unique_ptr where possible, or std::shared_ptr, since nothing speaks against it. It helps you with freeing memory when exceptions are thrown, or allows you to share ownership of some memory address, etc. Just do it.
Resizing the vectors will now work like you want to, because std::unique_ptr has a default constructor which just initializes its internal pointer to nullptr.
PS: Maybe I should elaborate on why storing vectors on the heap doesn't make sense. Vectors already can store their content on the heap, and dynamically reallocate memory when required.
Another thing: Vectors to pointers are also really useful if your element type is unmovable. This could come in handy in multithreading situations, where threads sometimes are initialized with a pointer to something, and rely on that something to stay exactly where it is in memory.
I am converting some code between different systems, and I have a question regarding c++ vectors.
If I do something like this:
In header file:
struct Vertex
{
float x;
float y;
float z;
}
struct submesh
{
Vertex *meshdata;
}
std::vector<submesh> meshes;
In a routine in the c++ file:
{
Vertex *data = new Vertex[1024];
submesh g;
g.meshdata = data;
meshes.push_back(g);
delete [] data;
}
Will I be in trouble? My assumption is that the vector would hold a pointer to data that is no longer valid once I called delete on it. Do I need to write a copy constructor for Vertex so that the data is copied first?
Additional:
The question was more to do with how do I put a pointer to allocated memory into a std::vector<> and still cleanup the locally allocated data. Essentially, how do I copy the data into the vector so I can still clean up my copy.
The original code was in DirectX. I am porting it to the iPhone. The original code allocated a submesh locally in a routine using:
{
ID3DXMesh* subMesh = 0;
D3DXCreateMesh(SubGrid::NUM_TRIS, SubGrid::NUM_VERTS, D3DXMESH_MANAGED, elems, gd3dDevice, &subMesh));
//
// ... do some magical things to submesh
//
SubGrid g;
g.mesh = subMesh;
g.box = bndBox;
mSubGrids.push_back(g);
}
I am trying to duplicate how ID3DXMesh is able to be added to a vector, then lose it's scope in the routine.
As I don't have access to D3DXCreateMesh(), I figured I would simply allocate the vertices I needed, throw them into a vector, and clean up.
Sorry, I wanted to keep the nitty gritty details out of it, as the question is simply how do I allocate a chunk of data, put a pointer into a std::vector<>, then clean up the locally allocated memory. :)
I assumed a copy constructor had to be written somewhere. Just wasn't sure where or how.
A subgrid looks like this:
struct SubGrid
{
ID3DXMesh* mesh;
AABB box;
// For sorting.
bool operator<(const SubGrid& rhs)const;
const static int NUM_ROWS = 33;
const static int NUM_COLS = 33;
const static int NUM_TRIS = (NUM_ROWS-1)*(NUM_COLS-1)*2;
const static int NUM_VERTS = NUM_ROWS*NUM_COLS;
};
And the vector they get added to looks like:
std::vector<SubGrid> mSubGrids;
Don't directly dynamicly-allocate when you don't need to, and in this case you don't. Since you're filling your own submesh data rather than using ID3DXMesh, the container of that data should be RAII-compliant. If I were coding this I would remove the submesh class entirely and just use:
// vector containing list of vertices.
typedef std::vector<Vertex> SubMesh;
Your SubGrid class can then become a simple container that holds, as one of its properties, a submesh collection. I noticed you also have a class AABB for a box object. You would continue to keep that inside SubGrid. I don't have ton to work with here, so I'm making some of these up as I go along, but something like the following:
// a simple 3-value triplet of floats
struct Vertex
{
float x,y,z;
};
// a Submesh is an arbitrary collection of Vertex objects.
typedef std::vector<Vertex> SubMesh;
// I'm defining AABB to be an 8-vertex object. your definition
// is likely different, but I needed something to compile with =)
typedef Vertex AABB[8];
class SubGrid
{
public:
SubGrid() {};
// comparator for container ordering
bool operator <(const SubGrid&);
// submesh accessors
void setSubmesh(const SubMesh& mesh) { submesh = mesh;}
SubMesh& getSubmesh() { return submesh; }
const SubMesh& getSubmesh() const { return submesh; }
// box accessors
AABB& getBox() { return box; }
const AABB& getBox() const { return box;}
private:
SubMesh submesh;
AABB box;
};
// arbitrary collection of SubGrid objects
typedef std::vector<SubGrid> SubGrids;
When adding this to your global SubGrid collection g, you have several possibilities. You could just do this:
// declared globally
Subgrids g;
// in some function for adding a subgrid item
SubGrid subgrid;
AABB& box = subgrid.getBox();
SubBesh& submesh = subgrid.getSubmesh();
// ... initialize your box and submesh data ...
g.push_back(subgrid);
But you'd be copying a lot of data around. To tighten up the memory access you could always do this instead:
// push an empty SubGrid first, then set it up in-place
g.push_back(SubGrid());
Subgrid& subgrid = *(g.back());
AABB& box = subgrid.getBox();
SubMesh& submesh = subgrid.getSubmesh();
//... initialize your box and submesh data ...
This will establish a reference to the SubGrid just added to the global collection, then allow you to modify it in-place. This is but-one of a number of possible setup options. It should be noted that if you have C++11 in your toolchain (and if you're doing this on MacOS or iOS, you likely do, as Apple LLVM 4.2's clang is pretty good on C++11 compliance) this can get even more efficient with judicious usage of move-constructors and move-assignment-operators.
Most importantly, not a new or delete to be seen.
Anyway, I hope this gives you some ideas.
Your code looks fine in single threaded application. Your code only allocate data memory once and delete [] data once.
Do I need to write a copy constructor for Vertex so that the data is copied first?
Your code is clean as you shown, meshes points to only allocated data. If you meant to copy data when call meshes.push_back(g), then your code doesn't do what you meant to.
You might want to use std::vector instead:
struct submesh
{
std::vector<Vertex> meshdata;
}
vector<submesh> meshes;
void Func()
{
meshes.emplace_back(submesh());
meshes.at(0).meshdata.resize(100);
}
STL container uses RAII idiom, it manages memory deallocation for you automatically.
Yes of course, vector will have a pointer to deleted memory. What you need is either:
Create copy constructor for submesh (not Vertex).OR
Changesubmesh to have array of Vertex (not just a pointer).
Copy constructor can be done like this:
struct submesh
{
Vertex *meshdata;
unsigned meshsize;
submesh(Vertex* v = 0, unsigned s= 0) : meshdata(v), meshsize(s){}
submesh(const submesh& s)
{
if(meshdata) /*we have stored data, delete it.*/ delete(meshdata);
meshdata = new Vertex[s.meshsize];
meshsize = s.meshsize;
memcpy(meshdata, s.meshdata, sizeof(Vertex) * meshsize);
}
};
For sure it is much recommended to use unique_ptr (if you use c++11) or auto_ptr for old c++. To avoid the nightmare of memory management as much as you can.
Check How to avoid memory leaks when using a vector of pointers to dynamically allocated objects in C++?
I'm compiling using Code::Blocks on Windows 7 using the MinGW compiler (which I can only assume is the latest version; both Code::Blocks and MinGW were installed this past week). My issue crops up under a particular circumstance, and my attempts to write a simpler script that demonstrates the problem have failed (which implies that there is something wrong with my structure). Also, my apologies for how long this post is.
Currently, I'm rolling with one class, FXSDL, which will act as an SDL wrapper:
class FXSDL
{
public:
FXSDL();
virtual ~FXSDL();
int Initialize();
int Render();
FXID CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims);
int SetAnim(FXID hRefID, FXID hAnimID);
FXID hPlayer;
protected:
private:
list<FXSurface> m_lstFXObjects;
list<FXSurface>::iterator m_liFirst;
SDL_Surface* m_lpsfSDLScreen;
Uint32 m_tmOld;
Uint32 m_tmFrame;
};
The value type of my list is:
struct FXSurface
{
FXID hRefID;
int wpxTile;
int hpxTile;
int wpxTotal;
int hpxTotal;
int cntTiles;
map<int, vector<int> > htAnims; // All animations
map<int, vector<int> >::iterator vCurr; // Currently active animation
vector<int>::iterator fiCurr; // Currently active frame
SDL_Surface* lpsfSDL;
SDL_Rect* lprcTiles; // Predefined frame positions
string* fpImage;
};
I've implemented very simple initialize and render function. The CreateCharacter function takes a few parameters, the most important of which is htAnims, a map of integer vectors (idea being: I define numeric ids with easy-to-remember representations, such as FXA_IDLE or FXA_WALK, as the key, and the series of number values representing frames for the animation as a vector). This could be fairly easily implemented as a multidimensional integer array, but animations are variable in length and I want to be able to add new anims (or redefine existing ones) without having to recast an array.
The CreateCharacter function is simple. It creates a new FXSurface, populates it with the required data, and pushes the new FXSurface onto the list:
FXID FXSDL::CreateCharacter(FXID hRefID, string fpImage, int wpxTile, int hpxTile, map<int, vector<int> > htAnims)
{
//list<FXSurface>::iterator lpsfTemp;
FXSurface lpsfTemp;
list<FXSurface>::iterator lpsfPos;
SDL_Rect* lprcCurr = NULL;
int cntTileW = 0;
int cntTileH = 0;
int cntCurr = 0;
// Start off by initializing our container struct
//lpsfTemp = new FXSurface();
lpsfTemp.lpsfSDL = IMG_Load(fpImage.c_str()); // Try to load the requested image
if(lpsfTemp.lpsfSDL != NULL) // If we didn't fail to
{
// Assign some variables for tracking
lpsfTemp.hRefID = hRefID;
lpsfTemp.fpImage = &fpImage;
lpsfTemp.wpxTotal = lpsfTemp.lpsfSDL->w;
lpsfTemp.hpxTotal = lpsfTemp.lpsfSDL->h;
// If a tile width was specified, use it
if(wpxTile != 0)
{
lpsfTemp.wpxTile = wpxTile;
lpsfTemp.hpxTile = hpxTile;
} // Otherwise, assume one tile
else
{
lpsfTemp.wpxTile = lpsfTemp.wpxTotal;
lpsfTemp.hpxTile = lpsfTemp.hpxTotal;
}
// Determine the tiles per row and column for later
cntTileW = lpsfTemp.wpxTotal / lpsfTemp.wpxTile;
cntTileH = lpsfTemp.hpxTotal / lpsfTemp.hpxTile;
// And the total number of tiles
lpsfTemp.cntTiles = cntTileW * cntTileH;
lpsfTemp.lprcTiles = new SDL_Rect[cntTileW*cntTileH];
// So we don't calculate this every time, determine each frame's coordinates and store them
for(int h = 0; h < cntTileH; h++)
{
for(int w = 0; w < cntTileW; w++)
{
cntCurr = (h*cntTileW)+w;
lprcCurr = new SDL_Rect;
lprcCurr->w = lpsfTemp.wpxTile;
lprcCurr->h = lpsfTemp.hpxTile;
lprcCurr->x = w*lpsfTemp.wpxTile;
lprcCurr->y = h*lpsfTemp.hpxTile;
lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
lprcCurr = NULL;
}
}
// Now acquire our list of animations and set the default
//lpsfTemp.htAnims = new map<int, vector<int> >(*htAnims);
lpsfTemp.htAnims = htAnims;
lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();
this->m_lstFXObjects.push_back(lpsfTemp);
}
else
{
hRefID = 0;
}
return hRefID;
}
It is precisely as the object is pushed that the error occurs. I've stepped through the code numerous times. Initially, I was only able to tell that my iterators were unable to dereference to the FXSurface object. After using watches to identify the exact memory address that the iterator and list objects pointed to, and dereferencing the address, I noticed the reason for my segfaults: all the values which I put into the original FXSurface were pushed down two memory blocks when the list object copied it!
My process for doing this is simple. I set up a breakpoint at the return statement for CreateCharacter, which gives me a view of lpsfTemp (the FXSurface I later add to the list) and m_lstFXObjects (the list I add it to). I scroll through the members of m_lstFXObjects, which brings me to _M_node, which contains the memory address of the only object I have added so far. I add a watch to this address in the form of (FXSurface)-hex address here-
First, find the address:
(There should be a picture here showing the highlighted _M_node attribute containing the list item's address, but I can't post pictures, and I can only post one URL. The second one is by far more important. It's located at http://www.fauxsoup.net/so/address.jpg)
Next, we cast and deference the address. This image shows both lpsfTemp and the copy in m_lstFXObjects; notice the discrepancy?
http://www.fauxsoup.net/so/dereferenced.jpg - See? All the values are in the correct order, just offset by two listings
I had initially been storing fpImages as a char*, so I thought that may have been throwing things off, but now it's just a pointer and the problem persists. Perhaps this is due to the map<int, vector<int> > I store?
FXSDL has a destructor, but no copy constructor and no assignment operator. Yo you're using naked pointers, but violate the Rule of Three.
I'm not going to look any further.
Use smart pointers to manage resources. Do not put a naked resource into a type, except when that type's only intention is to manage this one resource. From another answer given yesterday:
As a rule of thumb: If you have to manually manage resources, wrap each into its own object.
At a glance, I'd say you're double-deleting lpsfSDL and/or lprcTiles. When you have raw pointers in your structure, you need to follow the rule-of-three (implement copy constructor, assignment operator, and destructor) to properly manage the memory.
These lines look wrong to me:
lprcCurr = new SDL_Rect;
lprcCurr->w = lpsfTemp.wpxTile;
lprcCurr->h = lpsfTemp.hpxTile;
lprcCurr->x = w*lpsfTemp.wpxTile;
lprcCurr->y = h*lpsfTemp.hpxTile;
lpsfTemp.lprcTiles[cntCurr] = *lprcCurr;
lprcCurr = NULL;
lpsfTemp.lprcTiles is a SDL_Rect*. lprcTemp.lprcTiles[cntCurr] is a SDL_Rect. You should be writing this, IMHO:
SDL_Rect tmpRect;
tmpRect.w = lpsfTemp.wpxTile;
tmpRect.h = lpsfTemp.hpxTile;
tmpRect.x = w*lpsfTemp.wpxTile;
tmpRect.y = h*lpsfTemp.hpxTile;
lpsfTemp.lprcTiles[cntCurr] = tmpRect;
Dump the lprcCurr entirely.
Now this code:
lpsfTemp.vCurr = lpsfTemp.htAnims.find(FXA_WALK_EAST);
lpsfTemp.fiCurr = lpsfTemp.vCurr->second.begin();
This is bad. These iterators are invalid as soon as the push_back completes. That push_back is making a copy of lpsfTemp. The map and vector members are going to copy themselves and those iterators will copy themselves but they will be pointing to lpsfTemp's members which are going to be destroyed as soon as CreateCharacter exits.
One way to fix that would be to push_back a FXSurface object at the beginning, use back() to get its reference and operate on that instead of lpsfTemp. Then the iterators would stay consistent and they should stay consistent since you are using a list which does not copy its objects around. If you were using a vector or deque or anything other than a list you would need to manage all those pointers and iterators in the copy constructor and assignment operator.
Another thing: Double and triple check your array bounds when you access that lprcTiles array. Any mistake there and you could be scribbling over who knows what.
I don't know if any of that will help you.
I have a structure called vertex and I created some pointers to them. What I want to do is add those pointers to a list. My code below, when it tries to insert the pointer into the list, creates a segmentation fault. Can someone please explain what is going on?
#include <iostream>
#include <list>
#define NUM_VERTICES 8
using namespace std;
enum { WHITE, GRAY, BLACK };
struct vertex
{
int color;
int distance;
char parent;
};
int main()
{
//create the vertices
vertex r = {WHITE, NULL, NULL};
//create pointer to the vertex structures
vertex *pr = &r;
//create a list to hold the vertices
list<vertex*> *r_list = new list<vertex*>;
list<vertex*>::iterator it;
r_list->insert(it, pr);
}
There are several things wrong here.
First off, you aren't initializing the iterator, like other's have said:
list<vertex*>::iterator it = r_list->begin();
Do this and your code will be fine. But your code is done in a bad manner.
Why are you allocating the list from the heap? Look at your code: you have a memory leak. You aren't calling delete r_list anywhere. This is why you should use smart pointers (std::unique_ptr, std::shared_ptr if you have C++11, boost equivalents otherwise : boost::scoped_ptr and boost::shared_ptr)
But better yet, just do it on the stack:
//create a list to hold the vertices
list<vertex*> r_list;
list<vertex*>::iterator it = r_list->begin();
r_list.insert(it, pr);
In addition, using the iterator to insert is going about things the long way. Just use push front() or push back():
//create a list to hold the vertices
list<vertex*> r_list;
r_list.push_back(pr);
Another thing: if your list outlives the vertex you've constructed, it will be pointing to something invalid.
For example:
// global
list<vertex*> r_list;
void some_function(void)
{
//create the vertices
vertex r = {WHITE, NULL, NULL};
//create pointer to the vertex structures
vertex *pr = &r;
r_list.push_back(pr);
} // right here, vertex r stops existing: the list now contains an
// invalid pointer.
One solution is to store pointers to heap-allocated vertices:
// global
list<vertex*> r_list;
void some_function(void)
{
//create the vertices
vertex *r = new vertex;
r->color = WHITE;
r->distance = 0;
r->parent = 0;
r_list.push_back(r);
}
Now even after the function the list is pointing to a valid heap-allocated vertex. This now has the problem that when you're done using the list, you need to go through the lsit and call delete on each element. This problem is assisted by using the Boost Pointer Container Library.
The best way, though, is to just store vertices themselves (rather than pointers to them):
//create a list to hold the vertices
list<vertex> r_list;
//create the vertices
vertex r = {WHITE, NULL, NULL};
r_list.push_back(r);
If you give vertex a constructor, you can even just construct them in-place:
struct vertex
{
int color;
int distance;
char parent;
vertex(int _color, int _distance, char _parent) :
color(_color),
distance(_distance),
parent(_parent)
{
}
};
//create a list to hold the vertices
list<vertex> r_list;
r_list.push_back(vertex(WHITE, NULL, NULL));
(these are now outside your problem)
Firstly, NULL is generally only used when dealing with pointers. Since distance and parent are not pointers, use 0 to initialize them, rather than NULL:
//create the vertices
vertex r = {WHITE, 0, 0};
Secondly, use constants rather than #define:
#define NUM_VERTICES 8 // <- bad
const int NumberVertices = 8; // <- good
Lastly, give your enum a name, or place it in a namespace:
enum Color { WHITE, GRAY, BLACK };
Hope these help!
You haven't initialised the iterator, so it's not valid to insert with. You could use r_list->push_back(pr) instead, for example.
Also, the pointers in your list aren't going to be valid once r goes out of scope. Obviously that's not a problem in this case since it's in main(), but I assume this isn't the exact example where you're going to use the code, so it may come back to bite you...
First of all, you aren't initializing it to anything. Do you mean:
list<vertex*>::iterator it = r_list->begin();
Also, why are you initializing an int and char to NULL? Usually people use NULL for pointers.
Also, how about naming your enum and benefiting from the type safety of enums, instead of using them as ints?
Also, no need to create a new variable to make a pointer to the vertex. When you call insert, you can pass in &r.
Also, as Peter points out, why not just use push_back()?
Your code should look more like this:
using namespace std;
enum Color {
WHITE,
GRAY,
BLACK
};
struct vertex
{
Color color;
int distance;
char parent;
};
int main(int argc, char** argv) {
//create the vertices
vertex r = {WHITE, 0, ''};
//create a list to hold the vertices
list* r_list = new list();
list::iterator it = r_list->begin();
r_list->insert(it, &r);
// Or even better, use push_back (or front)
r_list->push_back(&r);
}
You have not initialized it, so you're inserting at a random/uninitialized place/pointer.
Normal ways of adding items to a std::list include its methods push_back and push_front; you'd normally use insert only if you had previously otherwise determined the specific spot in which you want to insert one more item.