OK so I am working on some game logic, I have done a fair bit of research (as much as the internet will allow) and still don't have a solid understanding of class and struct so please go gentle!
Basically, I want to be able to create an object with the properties all on one line ie.
object a{1, 1, 50, 15, 5}; // create object a
and I want some extra stuff to be made up aswell like:
class object
{
public:
int x;
int y;
int h;
int w;
int s;
int x1;
int y1;
int ps;
int ns;
int x1 = x + w;
int y1 = y + h;
int ps = 0 + s;
int ns = 0 - s;
};
I don't know which language you're working with, but it looks a bit like C++, so here's an example:
class Rect
{
public:
int x, y;
int w, h;
int right, bottom;
// This method is called a constructor.
// It allows you to perform tasks on
// the instantiation of an object.
Rect(int x_, int y_, int w_, int h_)
{
// store geometry
this->x = x_;
this->y = y_;
this->w = w_;
this->h = h_;
// calculate sides
this->right = x_ + w_;
this->bottom = y_ + h_;
}
};
// You use the constructor in your main() function like so:
Rect myObject(1, 1, 50, 15);
// And you can access the members like so:
myObject.x = 10;
myObject.right = myObject.x + myObject.w;
You cannot use operators inside the definition of a class as you proposed in your question. Operations on variables must take place inside a constructor (or other method).
Related
When I create a function such as:
int addThree(int x=1, int y=1, int z=1)
I want to call the function such that it uses the default arguments for x and z, but not y.
Some attempts have been addThree(5,,5) and addThree(5,NULL,5), but neither work effectively.
The default arguments must be at the last of your list, so do as follows
int addThree(int y , int x = 1, int z = 1)
{
//some stuff
return someInt;
}
, hence you can call it as
int ans = addThree(4);
Default arguments in C++, need to be specified in immediate succession, and cannot be succeeded by a non-default parameter.
So, something like
int sum(int x = 0, int y, int z = 0) {
return (x + y + z);
}
is forbidden in C++
The function needs to be as follows:
#include <iostream>
int sum(int x, int y = 0, int z = 0) {
return (x + y + z);
}
int main() {
std::cout << sum(1) << "\n";//calls sum(1,0,0)
std::cout << sum(1,2) << "\n";//calls sum(1,2,0)
return 0;
}
However, while specifying default arguments, you always need to take care in function overloading. The overloaded functions cannot be called ambiguously..
So a code like:
#include <iostream>
int sum(int x, int y = 0, int z = 0) {
return (x + y + z);
}
float sum(int x, float y = 0.0, float z = 0.0) {
return (float(x) + y + z);
}
int main() {
std::cout << sum(1) << "\n";
return 0;
}
does not compile and righty produces ambiguity error, as the compiler does not understand
Whether it should call the first sum, or the second sum.
If you're consistently passing a value for one parameter and using the defaults for the others, you can rearrange the parameters to the one you need to pass is first, and the ones for which you use defaults come later. But that only works if it's essentially always the same ones for which you supply a value vs. use the defaults.
Otherwise, if you need something similar to the basic capability badly enough, you can pass an instance of a class, and have that class implement the named parameter idiom.
class triplet {
int x_ {1};
int y_ {1};
int z_ {1};
public:
triplet &x(int val) { x_ = val; return *this; }
triplet &y(int val) { y_ = val; return *this; }
triplet &z(int val) { z_ = val; return *this; }
int x() const { return x_; }
int y() const { return y_; }
int z() const { return z_; }
};
int addThree(triplet const &t) {
return oldAddThree(t.x(), t.y(), t.z());
}
int ans = addThree(triplet().x(4));
This lets you use the defaults for as many or few of the values you need as you want, and override only those that you actually want to. On the other hand, it does add a fair amount of syntactic overhead, so you have to want the capability pretty badly to bother.
#include <iostream>
using namespace std;
double Default = 0;
class Vector
{
public:
Vector(double x, double y, double z);
double X, Y, Z;
double fVctr[3];
};
Vector::Vector(double x,double y,double z)
{
this->X = x;
this->Y = y;
this->Z = z;
this->fVctr[0] = X;
this->fVctr[1] = Y;
this->fVctr[2] = Z;
}
this part throws me the error no default constructor esists
class Edge
{
public:
Vector vectA;
Vector vectB;
Edge(Vector _vectA , Vector _vectB){
this->vectA = _vectA;
this->vectB = _vectB;
}
};
int main()
{
Vector Vertex(0,2,5);
// Debug
cout << Vertex.X <<"\n"<< endl;
cout << Vertex.Y << "\n" << endl;
cout << Vertex.Z << "\n" << endl;
}
Given a constructor
Constructor( ... )
{ // A
...
};
at Point A, all members are getting default initialized, since they cannot be uninitialized, when we start into the code of the constructor. To initialize members without default constructor or const members, you have to use the so-called member initializer list.
In your example this would look like this:
Edge(Vector _vectA, Vector _vectB) : vectA(_vectA), vectB(_vectB)
{ // This can be empty, since everything is done, but it has to exist nonetheless.
}
It is good practice to use the member initializer list for as much as possible, since you can save yourself some default initializations and every member is in the state it should be before you do anything with it.
PS: You don't have to reference members by this->member, just write member. Also, please do not use using namespace std;.
the reason is in your Edge class Constructor.
Edge(Vector _vectA , Vector _vectB){
this->vectA = _vectA;
this->vectB = _vectB;
}
the Arguments need a default value. for this, you can Overload your constructor in Vector class.
like this :
Vector::Vector(double x=0,double y=0,double z=0)
{
this->X = x;
this->Y = y;
this->Z = z;
this->fVctr[0] = X;
this->fVctr[1] = Y;
this->fVctr[2] = Z;
}
or like this :
Vector::Vector()
{
this->X = 0;
this->Y = 0;
this->Z = 0;
this->fVctr[0] = X;
this->fVctr[1] = Y;
this->fVctr[2] = Z;
}
As a matter, of course, you didn't use the Edge class in Main Procedure. so you can safely delete or comment that class and prevent the compile error of your code.
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
I'm a bit confused with classes was hoping some one could explain.
I have a class I'm making to create buttons for a game menu. There are four variables:
int m_x
int m_y
int m_width
int m_height
I then want to use a render function in the class but Im not understanding how i use the 4 int variables in the class and pass it to the function in the class?
My class is like this:
class Button
{
private:
int m_x, m_y; // coordinates of upper left corner of control
int m_width, m_height; // size of control
public:
Button(int x, int y, int width, int height)
{
m_x = x;
m_y = y;
m_width = width;
m_height = height;
}
void Render(SDL_Surface *source,SDL_Surface *destination,int x, int y)
{
SDL_Rect offset;
offset.x = x;
offset.y = y;
SDL_BlitSurface( source, NULL, destination, &offset );
}
} //end class
Where i am confused is how the values created in public:Button is passed to void render I'm not fully sure I've got this right, if i have its pure luck so far because I'm still a little bit confused.
Maybe an example will help:
#include <iostream>
class Button
{
private:
int m_x, m_y; // coordinates of upper left corner of control
int m_width, m_height; // size of control
public:
Button(int x, int y, int width, int height) :
//This is initialization list syntax. The other way works,
//but is almost always inferior.
m_x(x), m_y(y), m_width(width), m_height(height)
{
}
void MemberFunction()
{
std::cout << m_x << '\n';
std::cout << m_y << '\n';
//etc... use all the members.
}
};
int main() {
//Construct a Button called `button`,
//passing 10,30,100,500 to the constructor
Button button(10,30,100,500);
//Call MemberFunction() on `button`.
//MemberFunction() implicitly has access
//to the m_x, m_y, m_width and m_height
//members of `button`.
button.MemberFunction();
}
You might want to spend some time learning C++ before getting too deep into a complex programming project.
To answer your question, The variables initialized in the constructor (Button) are part of the class instance. So they're available within any class method, including Render.