I'm using a C++ engine called libtcod to print out ASCII characters in a rogue-like game.
In the game I want to represent the 'map' as a 2D array of type Map, which contains 'layer1' for tiles, e.g Grass, Sand, Water, and a second layer to represent objects on top of layer2, e.g Trees and Boulders.
I have created the Map class, which contains two attributes; one of type Tile, and the other of type Object.
However I seem to be having difficulties in the creation of the 2D array itself.
I am getting the error: "an array may not have elements of this type"
#include "libtcod.hpp"
#include <ctime>
#include <stdio.h>
#include <ciso646>
#include <cstring>
static const TCODColor colour_darkWall(128, 128, 128);
static const TCODColor colour_grass(0, 255, 0);
static const TCODColor colour_black(0, 0, 0);
static const TCODColor colour_white(255, 255, 255);
static const TCODColor colour_blue(0, 0, 255);
static const TCODColor colour_brown(139, 69, 19);
static const TCODColor colour_red(255, 0, 0);
int i, j;
class object {
public:
char name[20];
TCODColor colour;
char symbol;
bool passable;
object(int layer_id) {
if (layer_id == 0) { //EMPTY
strcpy(name, "NA");
symbol = ' ';
passable = true;
colour = colour_white;
}
else if (layer_id == 1) { //TREE
strcpy(name, "Tree");
symbol = '^';
passable = false;
colour = colour_grass;
}
else if (layer_id == 2) { //ROCK
strcpy(name, "Rock");
symbol = 'R';
passable = false;
colour = colour_black;
}
}
};
class tile {
public:
char name[20];
TCODColor colour;
bool passable;
tile(int layer_id) {
if (layer_id == 0) { //WATER
strcpy(name, "Water");
colour = colour_blue;
passable = false;
}
else if (layer_id == 1) { //GRASS
strcpy(name, "Grass");
colour = colour_grass;
passable = true;
}
else if (layer_id == 2) { //SAND
strcpy(name, "Sand");
colour = colour_brown;
passable = true;
}
}
};
class Map {
public:
tile layer1;
object layer2;
Map(int layer1_id, int layer2_id) {
layer1 = tile(layer1_id);
layer2 = object(layer2_id);
}
};
int main() {
const int window_x = 150;
const int window_y = 50;
Map[][] map = new Map[5][5];
TCODConsole::initRoot(window_x, window_y, "CivSim v0.1", false);
while (!TCODConsole::isWindowClosed()) {
TCODConsole::root->clear();
TCODConsole::root->putChar(0, 0, map.layer2.symbol);
TCODConsole::flush();
}
return 0;
}
Not sure how to create the 2D array in question.
Thanks in advance for any help!
Map[][] map = new Map[5][5]; is not legal. Only the first dimension can be omitted. You need Map[][5] map = new Map[5][5]; or better still Map (*map)[5] = new Map[5][5];.
The reason is that these notations using [] in a type are just another (and confusing) way of describing a pointer type. So int[] x is just a different way of writing int* x and Map[][5] m is just another way of writing Map (*m)[5] (which is to say a pointer to an array of five Maps).
I think you should always use the pointer version of these notations, it's more correctly describes what you are really doing. In the long term writing code that looks like you have an array, when all you actually have is a pointer is confusing.
So why isn't Map[][] another way of writing Map**? It's because the similarity between arrays and pointer in C and C++ only works for one level. A two dimensional array is a fundamentally different structure from a pointer to an array of pointers. So int[5][5] isn't compatible with int** so there is no reason to make int[][] mean the same as int**.
Related
I am having trouble passing an array of object pointers from main() to a function from different class.
I created an array of object pointers listPin main() and I want to modify the array with a function editProduct in class Manager such as adding new or edit object.
Furthermore, I want to pass the whole listP array instead of listP[index]. How to achieve this or is there any better way? Sorry, I am very new to c++.
#include <iostream>
using namespace std;
class Product
{
protected:
string id, name;
float price;
public:
Product()
{
id = "";
name = "";
price = 0;
}
Product(string _id, string _name, float _price)
{
id = _id;
name = _name;
price = _price;
}
};
class Manager
{
protected:
string id, pass;
public:
Manager(string _id, string _pass)
{
id = _id;
pass = _pass;
}
string getId() const { return id; }
string getPass() const { return pass; }
void editProduct(/*array of listP*/ )
{
//i can edit array of listP here without copying
}
};
int main()
{
int numProduct = 5;
int numManager = 2;
Product* listP[numProduct];
Manager* listM[numManager] = { new Manager("1","alex"), new Manager("2", "Felix") };
bool exist = false;
int index = 0;
for (int i = 0; i < numProduct; i++) { //initialize to default value
listP[i] = new Product();
}
string ID, PASS;
cin >> ID;
cin >> PASS;
for (int i = 0; i < numManager; i++)
{
if (listM[i]->getId() == ID && listM[i]->getPass() == PASS) {
exist = true;
index = i;
}
}
if (exist == true)
listM[index]->editProduct(/*array of listP */);
return 0;
}
Since the listP is a pointer to an array of Product, you have the following two option to pass it to the function.
The editProduct can be changed to accept the pointer to an array of size N, where N is the size of the passed pointer to the array, which is known at compile time:
template<std::size_t N>
void editProduct(Product* (&listP)[N])
{
// Now the listP can be edited, here without copying
}
or it must accept a pointer to an object, so that it can refer the array
void editProduct(Product** listP)
{
// find the array size for iterating through the elements
}
In above both cases, you will call the function as
listM[index]->editProduct(listP);
That been said, your code has a few issues.
First, the array sizes numProduct and numManager must be compiled time constants, so that you don't end up creating a non-standard variable length array.
Memory leak at the end of main as you have not deleted what you have newed.
Also be aware Why is "using namespace std;" considered bad practice?
You could have simply used std::array, or std::vector depending on where the object should be allocated in memory. By which, you would have avoided all these issues of memory leak as well as pointer syntaxes.
For example, using std::vector, you could do simply
#include <vector>
// in Manager class
void editProduct(std::vector<Product>& listP)
{
// listP.size() for size of the array.
// pass by reference and edit the listP!
}
in main()
// 5 Product objects, and initialize to default value
std::vector<Product> listP(5);
std::vector<Manager> listM{ {"1","alex"}, {"2", "Felix"} };
// ... other codes
for (const Manager& mgr : listM)
{
if (mgr.getId() == ID && mgr.getPass() == PASS)
{
// ... code
}
}
if (exist == true) {
listM[index]->editProduct(listP);
}
You cannot have arrays as parameters in C++, you can only have pointers. Since your array is an array of pointers you can use a double pointer to access the array.
void editProduct(Product** listP){
and
listM[index]->editProduct(listP);
Of course none of these arrays of pointers are necessary. You could simplify your code a lot if you just used regular arrays.
Product listP[numProduct];
Manager listM[numManager] = { Manager("1","alex"), Manager("2", "Felix")};
...
for(int i = 0; i < numManager; i++ ){
if(listM[i].getId() == ID && listM[i].getPass() == PASS) {
exist = true;
index = i;
}
}
if(exist == true){
listM[index].editProduct(listP);
}
I am currently trying to refresh my c++ skills with implementing a snake-game. I have created following class - relevant code snippet:
snake_class.h
#include <string>
#include <vector>
#include <windows.h>
typedef struct coordinates {
int x;
int y;
};
class Snake {
public:
std::vector<coordinates> body;
Snake(const int MAX_HEIGHT, const int MAX_WIDTH, const int initLengthSnake);
void updateSnakeBody(coordinates newDirection, int startingPoint);
};
... and with the corresponding code snippet of the .cpp-file:
snake_class.cpp
#include <vector>
#include "snake_class.h"
Snake::Snake(const int MAX_HEIGHT, const int MAX_WIDTH, const int initLengthSnake) {
for (int snakeLength = 0; snakeLength < initLengthSnake; snakeLength++) {
coordinates currentBodyPoint = { (MAX_WIDTH + initLengthSnake) / 2 - snakeLength, (MAX_HEIGHT) / 2 };
body.push_back(currentBodyPoint);
}
}
void Snake::updateSnakeBody(coordinates newDirection, int startingPoint) {
coordinates currentBodyPoint = body[startingPoint];
body[startingPoint].x += newDirection.x;
body[startingPoint].y += newDirection.y;
if (startingPoint + 1 < body.size()) {
coordinates nextDirection = { currentBodyPoint.x - body[startingPoint + 1].x,
currentBodyPoint.y - body[startingPoint + 1].y };
updateSnakeBody(nextDirection, startingPoint + 1);
}
}
My main-method looks like the following:
bool crashed = false;
int main()
{
//init-part for windows and snake length
const int windowHeight = 20;
const int windowWidth = 25;
const int initSnakeLength = 4;
//init part for snake game to move and some stock variables
coordinates direction = { 1, 0 };
bool initNeeded = false;
//snake init
Snake* snake = new Snake(windowWidth, windowHeight, initSnakeLength);
while (true) {
if (initNeeded) {
crashed = false;
Snake* snake = new Snake(windowWidth, windowHeight, initSnakeLength);
initNeeded = false;
}
if (!crashed) {
(*snake).updateSnakeBody(direction, 0);
crashed = true;
}
else {
delete snake;
initNeeded = true;
}
}
return 0;
}
Build is successfull and the first round of the game works as expected. When I feedback to the game, that I want to play another round, then the new snake class is constructed once again inside the if (initNeeded) {...}-condition. The vector also got the size of 4 after the construction.
But as soon as the program enters the line
(*snake).updateSnakeBody(direction, 0);
I retrieve the error-message vector subsrictp out of range and somehow the vector got the size 0.
I know, that I do not need to dynamically allocate a new class for getting the game to run as intended, but I wanted to try it out in this way.
I cannot really figure out why the new class behaves like that and hope some of you could help me resolving that issue!
Thanks in advance!
You construct two different snakes, one in the main scope, on in the scope of the if statement. Then you create two pointers to these, with the same name. You need to sort this out!
while (true) {
if (initNeeded) {
crashed = false;
Snake* snake = new Snake(windowWidth, windowHeight, initSnakeLength);
initNeeded = false;
}
This snake pointer cannot be accessed from anywhere else. Whatever you are trying to do, this must be wrong.
Perhaps this is what you meant to do
//declare snake pointer
Snake* snake;
while (true) {
if (initNeeded) {
crashed = false;
// set pointer to a new snake
snake = new Snake(windowWidth, windowHeight, initSnakeLength);
initNeeded = false;
}
I want to make a Class Color, and declare 2 Class Color objects to store a list of warm colors and cold colors respectively.
How do i initialize an array of char pointer and define it in the constructor in C++?
.h
Const int MAX = 5;
Class Color {
Char* list[MAX];
Int numList;
}
.cpp
//default constructor
Color() {
list[MAX] = nullptr;
numList = 0;
}
//custom constructor
Color(char* list_, int numList_) {
for (int i = 0; i < num_List_; i++) {
list[i] = list_[i];
}
numList = numList_;
}
main.cpp
//declaring 2 objects of Class Color
Color warm_color;
Char warmList = {red, pink, orange, yellow, brown};
Color cold_color;
Char coldList = {“green”, “blue”, “navy”};
warm_color = Color(warmList, 5);
cold_color = Color(coldList, 3);
You're trying to store a variable length list of strings of variable length. C++ offers very useful objects for this in the standard library.
std::vector: a variable length list, properly managed.
std::string: a variable length list of characters, all properly managed.
That way your code can be reduced to a simple:
#include <vector>
#include <string>
class Colors {
private:
std::vector<std::string> colorList;
public:
Colors(std::vector<std::string> const& colorList) : colorList(colorList) {}
};
int main() {
Colors warm_colors({ "red", "pink", "orange", "yellow", "brown" });
Colors cold_colors({ "green", "blue", "navy" });
}
Actually, in this case the constructor can be replaced by a move constructor:
Colors(std::vector<std::string>&& colorList) : colorList(std::move(colorList)) {}
... but that maybe too advanced for you yet.
So looking at your code you have some errors. Take the Menu constructor
Menu::Menu(const char* menuTitle, const char* items[], int numItems) {
m_menuTitle = new char[strlen(menuTitle) + 1];
strcpy_s(m_menuTitle, strlen(menuTitle) + 1, menuTitle);
for (int i = 0; i < numItems; i++) {
m_items[i] = &items[i];
}
m_numItems = numItems;
}
You have correctly handled the menu title, now you should use the same ideas on the menu items. That is you allocate each menu item with new, and then you copy each menu item with strcpy_s. Like this
Menu::Menu(const char* menuTitle, const char* items[], int numItems) {
m_menuTitle = new char[strlen(menuTitle) + 1];
strcpy_s(m_menuTitle, strlen(menuTitle) + 1, menuTitle);
for (int i = 0; i < numItems; i++) {
m_items[i] = new char[strlen(items[i]) + 1];
strcpy_s(m_items[i], strlen(items[i]) + 1, menuTitle);
}
m_numItems = numItems;
}
See? It's really no different to what you had already, except that m_items[i] replaces m_menuTitle.
I'm new with using classes and I encountered a problem while delcaring an array into a class. I want to initialize a char array for text limited to 50 characters and then replace the text with a function.
#ifndef MAP_H
#define MAP_H
#include "Sprite.h"
#include <SFML/Graphics.hpp>
#include <iostream>
class Map : public sprite
{
private:
char mapname[50];
int columnnumber;
int linenumber;
char casestatematricia[];
public:
void setmapname(char newmapname[50]);
void battlespace(int column, int line);
void setcasevalue(int col, int line, char value);
void printcasematricia();
};
#endif
By the way I could initialize my 2d array like that
char casestatematricia[][];
I want later to make this 2d array dynamic where I enter a column number and a line number like that
casestatematricia[linenumber][columnnumber]
to create a battlefield.
this is the cpp code so that you have an idea of what I want to do.
#include "Map.h"
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace sf;
void Map::setmapname(char newmapname[50])
{
this->mapname = newmapname;
}
void Map::battlespace(int column, int line)
{
}
void Map::setcasevalue(int col, int line, char value)
{
}
void Map::printcasematricia()
{
}
thank you in advance.
Consider following common practice on this one.
Most (e.g. numerical) libraries don't use 2D arrays inside classes.
They use dynamically allocated 1D arrays and overload the () or [] operator to access the right elements in a 2D-like fashion.
So on the outside you never can tell that you're actually dealing with consecutive storage, it looks like a 2D array.
In this way arrays are easier to resize, more efficient to store, transpose and reshape.
Just a proposition for your problem:
class Map : public sprite
{
private:
std::string mapname;
int columnnumber;
int linenumber;
std::vector<char> casestatematricia;
static constexpr std::size_t maxRow = 50;
static constexpr std::size_t maxCol = 50;
public:
Map():
casestatematricia(maxRow * maxCol, 0)
{}
void setmapname(std::string newmapname)
{
if (newmapname.size() > 50)
{
// Manage error if you really need no more 50 characters..
// Or just troncate when you serialize!
}
mapname = newmapname;
}
void battlespace(int col, int row);
void setcasevalue(int col, int row, char value)
{
// check that col and line are between 0 and max{Row|Column} - 1
casestatematricia[row * maxRow + col] = value;
}
void printcasematricia()
{
for (std::size_t row = 0; row < maxRow; ++row)
{
for (std::size_t col = 0; col < maxCol; ++col)
{
char currentCell = casestatematricia[row * maxRow + col];
}
}
}
};
For access to 1D array like a 2D array, take a look at Access a 1D array as a 2D array in C++.
When you think about serialization, I guess you want to save it to a file. Just a advice: don't store raw memory to a file just to "save" time when your relaunch your soft. You just have a non portable solution! And seriously, with power of your computer, you don't have to be worry about time to load from file!
I propose you to add 2 methods in your class to save Map into file
void dump(std::ostream &os)
{
os << mapname << "\n";
std::size_t currentRow = 0;
for(auto c: casestatematricia)
{
os << static_cast<int>(c) << " ";
++currentRow;
if (currentRow >= maxRow)
{
currentRow = 0;
os << "\n";
}
}
}
void load(std::istream &is)
{
std::string line;
std::getline(is, line);
mapname = line;
std::size_t current_cell = 0;
while(std::getline(is, line))
{
std::istringstream is(line);
while(!is.eof())
{
char c;
is >> c;
casestatematricia[current_cell] = c;
++current_cell;
}
}
}
This solution is only given for example. They doesn't manage error and I have choose to store it in ASCII in file. You can change to store in binary, but, don't use direct write of raw memory. You can take a look at C - serialization techniques (just have to translate to C++). But please, don't use memcpy or similar technique to serialize
I hope I get this right. You have two questions. You want know how to assign the value of char mapname[50]; via void setmapname(char newmapname[50]);. And you want to know how to create a dynamic size 2D array.
I hope you are comfortable with pointers because in both cases, you need it.
For the first question, I would like to first correct your understanding of void setmapname(char newmapname[50]);. C++ functions do not take in array. It take in the pointer to the array. So it is as good as writing void setmapname(char *newmapname);. For better understanding, go to Passing Arrays to Function in C++
With that, I am going to change the function to read in the length of the new map name. And to assign mapname, just use a loop to copy each of the char.
void setmapname(char *newmapname, int length) {
// ensure that the string passing in is not
// more that what mapname can hold.
length = length < 50 ? length : 50;
// loop each value and assign one by one.
for(int i = 0; i < length; ++i) {
mapname[i] = newmapname[i];
}
}
For the second question, you can use vector like what was proposed by Garf365 need to use but I prefer to just use pointer and I will use 1D array to represent 2d battlefield. (You can read the link Garf365 provide).
// Declare like this
char *casestatematricia; // remember to initialize this to 0.
// Create the battlefield
void Map::battlespace(int column, int line) {
columnnumber = column;
linenumber = line;
// Clear the previous battlefield.
clearspace();
// Creating the battlefield
casestatematricia = new char[column * line];
// initialise casestatematricia...
}
// Call this after you done using the battlefield
void Map::clearspace() {
if (!casestatematricia) return;
delete [] casestatematricia;
casestatematricia = 0;
}
Just remember to call clearspace() when you are no longer using it.
Just for your benefit, this is how you create a dynamic size 2D array
// Declare like this
char **casestatematricia; // remember to initialize this to 0.
// Create the battlefield
void Map::battlespace(int column, int line) {
columnnumber = column;
linenumber = line;
// Clear the previous battlefield.
clearspace();
// Creating the battlefield
casestatematricia = new char*[column];
for (int i = 0; i < column; ++i) {
casestatematricia[i] = new char[line];
}
// initialise casestatematricia...
}
// Call this after you done using the battlefield
void Map::clearspace() {
if (!casestatematricia) return;
for(int i = 0; i < columnnumber; ++i) {
delete [] casestatematricia[i];
}
delete [][] casestatematricia;
casestatematricia = 0;
}
Hope this help.
PS: If you need to serialize the string, you can to use pascal string format so that you can support string with variable length. e.g. "11hello world", or "3foo".
In the image below (Character.cpp), may I know how to create only one Initialize method that can be called to stored many sprites? Do I need to change the Texture1,Sprite,PosX,PosY, etc to array?
The initialize method will be called in my MAIN.cpp. Sorry if the explaination is not good enough. That is just my idea of doing it but will there be a better ones instead of having so many arrays?
void Character::Initialize1(string image1, float PosX1, float PosY1, float CenterX1, float CenterY1)
{
D3DXCreateTextureFromFile(Pull->GETd3ddev(), image.c_str(), &Texture1);
D3DXCreateSprite(Pull->GETd3ddev(), &sprite1);
RECT SpriteRect1;
SpriteRect1.top = 0;
SpriteRect1.bottom = 127;
SpriteRect1.left = 0;
SpriteRect1.right = 128;
SpritePos1 = D3DXVECTOR2(PosX1, PosY1);
SpriteCenter1 = D3DXVECTOR2(CenterX1, CenterY1);
}
void Character::Initialize2(string image2, float PosX2, float PosY2, float CenterX2, float CenterY2)
{
D3DXCreateTextureFromFile(Pull->GETd3ddev(), image.c_str(), &Texture2);
D3DXCreateSprite(Pull->GETd3ddev(), &sprite2);
RECT SpriteRect2;
SpriteRect2.top = 0;
SpriteRect2.bottom = 14;
SpriteRect2.left = 0;
SpriteRect2.right = 14;
SpritePos2 = D3DXVECTOR2(PosX2, PosY2);
SpriteCenter2 = D3DXVECTOR2(CenterX2, CenterY2);
}
Create the necessary initialization method in your sprite class. Then in main() create your sprites and call the appropriate initialization methods. If using lots of Sprites, it will be probably handy to put the created sprites inside a vector in order to have less code for cleanup and probably other things.
A quick example for a Sprite class called SpriteConfig, which only contains position parameters.
#include <iostream>
#include <vector>
using namespace std;
class SpriteConfig {
public:
SpriteConfig() {
top = 0;
bottom = 0;
left = 0;
right = 0;
}
void setPos(float aTop, float aBottom, float aLeft, float aRight) {
mTop = aTop;
mBottom = aBottom;
mLeft = aLeft;
mRight = aRight;
}
private:
// position parameters
float mTop;
float mBottom;
float mLeft;
float mRight;
};
int main()
{
vector<SpriteConfig*> spriteConfigs;
SpriteConfig *sprCfg;
sprCfg = new SpriteConfig();
sprCfg->setPos(1,1,1,1);
spriteConfigs.push_back(sprCfg);
sprCfg = new SpriteConfig();
sprCfg->setPos(2,2,2,2);
spriteConfigs.push_back(sprCfg);
// We now have a number of Sprites
// Add code here to do something with it.
// ...
for(vector<SpriteConfig*>::iterator it = spriteConfigs.begin();
it != spriteConfigs.end();
++it) {
// cleanup
delete (*it);
(*it) = null;
}
return 0;
}