Software: Visual Studio 2017 Community
Hi, everybody,
I am making a simple 2d console game in C++ (perhaps very simplified Dwarf Fortress if you know it).
And I want a map to be displayed in console with ASCII.
Something like this:
I have a WorldMap class declared in the header file(simplified version).
And I declare a 2d array inside it.
#pragma once
#include <iostream>
class WorldMap
{
public:
WorldMap();
virtual ~WorldMap();
private:
int worldWidth;
int worldHeight;
char worldMap; // Declare a variable that will hold all the characters for the map
};
And then define it in the .cpp file:
#include "WorldMap.h"
#include <algorithm>
WorldMap::WorldMap()
{
worldWidth = 50;
worldHeight = 50;
worldMap[50][50]; // Define the map array
// And then here I will also somehow need to be able to fill the whole map with '.' symbols, and so on
}
So that is the basic idea of what I am trying to achieve. The reason why I can't define the array size immediately is because I want to be able to choose the size of the map when the map is created.
I have already tried:
The code above.
Error:
error C2109: subscript requires array or pointer type
Declaring 2d array as char worldMap[][]; and then defining it as worldMap[50][50]; .
Error:
error C2087: 'worldMap': missing subscript
warning C4200: nonstandard extension used: zero-sized array in struct/union
message : This member will be ignored by a defaulted constructor or copy/move assignment operator
Declaring 2d array as char worldMap[worldWidth][worldHeight];, expecting that when the object is created, the width and height variables will be defined first, and then they will define the array.
Error:
error C2327: 'WorldMap::worldWidth': is not a type name, static, or enumerator
error C2065: 'worldWidth': undeclared identifier
error C2327: 'WorldMap::worldHeight': is not a type name, static, or enumerator
error C2065: 'worldHeight': undeclared identifier
Using char* worldMap; and char** worldMap, but so far I can't even understand how double pointer works, yet char* worldMap actually works with a 1D array without errors, until I start accessing values of the elements in the array.
I suppose a workaround would be to use a string or 1D char array and when displaying it just use mapWidth to end line each 50 characters for example, which will give the same result. But I feel like that's not a good way to achieve this since I will need to access x and y coords of this map and so on.
I guess what I am asking is:
What's the best way of declaring a 2d array for a class and then defining it in the object?
What's the best way to store a map for such a console game? (Not necessarily using arrays)
Thank you for reading. I will really appreciate any help, even just ideas and tips might push me in the right direction :)
What's the best way of declaring a 2d array for a class and then defining it in the object?
What's the best way to store a map for such a console game? (Not necessarily using arrays)
This is not "the best way" but it's one way of doing it.
Create a class wrapping a 1D std::vector<char>.
Add operator()s to access the individual elements.
Add misc. other support functions to the class, like save() and restore().
I've used your class as a base and tried to document what it's doing in the code: If some of the functions I've used are unfamiliar, I recommend looking them up at https://en.cppreference.com/ which is an excellent wiki that often has examples of how to use the particular function you read about.
#include <algorithm> // std::copy, std::copy_n
#include <filesystem> // std::filesystem::path
#include <fstream> // std::ifstream, std::ofstream
#include <iostream> // std::cin, std::cout
#include <iterator> // std::ostreambuf_iterator, std::istreambuf_iterator
#include <vector> // std::vector
class WorldMap {
public:
WorldMap(unsigned h = 5, unsigned w = 5) : // colon starts the initializer list
worldHeight(h), // initialize worldHeight with the value in h
worldWidth(w), // initialize worldWidth with the value in w
worldMap(h * w, '.') // initialize the vector, size h*w and filled with dots.
{}
// Don't make the destructor virtual unless you use polymorphism
// In fact, you should probably not create a user-defined destructor at all for this.
//virtual ~WorldMap(); // removed
unsigned getHeight() const { return worldHeight; }
unsigned getWidth() const { return worldWidth; }
// Define operators to give both const and non-const access to the
// positions in the map.
char operator()(unsigned y, unsigned x) const { return worldMap[y*worldWidth + x]; }
char& operator()(unsigned y, unsigned x) { return worldMap[y*worldWidth + x]; }
// A function to print the map on screen - or to some other ostream if that's needed
void print(std::ostream& os = std::cout) const {
for(unsigned y = 0; y < getHeight(); ++y) {
for(unsigned x = 0; x < getWidth(); ++x)
os << (*this)(y, x); // dereference "this" to call the const operator()
os << '\n';
}
os << '\n';
}
// functions to save and restore the map
std::ostream& save(std::ostream& os) const {
os << worldHeight << '\n' << worldWidth << '\n'; // save the dimensions
// copy the map out to the stream
std::copy(worldMap.begin(), worldMap.end(),
std::ostreambuf_iterator<char>(os));
return os;
}
std::istream& restore(std::istream& is) {
is >> worldHeight >> worldWidth; // read the dimensions
is.ignore(2, '\n'); // ignore the newline
worldMap.clear(); // empty the map
worldMap.reserve(worldHeight * worldWidth); // reserve space for the new map
// copy the map from the stream
std::copy_n(std::istreambuf_iterator<char>(is),
worldHeight * worldWidth, std::back_inserter(worldMap));
return is;
}
// functions to save/restore using a filename
bool save(const std::filesystem::path& filename) const {
if(std::ofstream ofs(filename); ofs) {
return static_cast<bool>(save(ofs)); // true if it suceeded
}
return false;
}
bool restore(const std::filesystem::path& filename) {
if(std::ifstream ifs(filename); ifs) {
return static_cast<bool>(restore(ifs)); // true if it succeeded
}
return false;
}
private:
unsigned worldHeight;
unsigned worldWidth;
// Declare a variable that will hold all the characters for the map
std::vector<char> worldMap;
};
Demo
There is no best way to do anything*. It's what works best for you.
From what I understand you want to make a dynamic 2D arrays to hold your char of world map. You have a lot of options to do this. You can have a worldMap class nothing wrong with that. If you want dynamic 2D arrays just make functions out of this kind of logic.
#include <iostream>
#include <vector>
int main() {
int H = 10, W = 20;
char** map = NULL; //This would go in your class.H
//Make a function to allocate 2D array
map = new char* [H];
for (int i = 0; i < H; i++) {
map[i] = new char[W];
}
//FILL WITH WHATEVER
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
map[i][j] = 'A';
}
}
//do what ever you want like normal 2d array
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
std::cout << map[i][j] << " ";
}
std::cout << std::endl;
}
//Should always delete when or if you want to make a new one run time
for (int i = 0; i < H; i++)
delete[] map[i];
delete[] map;
map = NULL;
//Also you can use vectors
std::cout << "\n\n With vector " << std::endl;
std::vector<std::vector<char>> mapV;
//FILL WITH WHATEVER
for (int i = 0; i < H; i++) {
std::vector<char> inner;
for (int j = 0; j < W; j++) {
inner.push_back('V');
}
mapV.push_back(inner);
}
//do what ever you want kind of like a normal array
//but you should look up how they really work
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
std::cout << mapV[i][j] << " ";
}
std::cout << std::endl;
}
mapV.clear();
return 0;
}
Related
class A{
char info[256];
public:
char* getInfo();
A(char i[256]);
//A.cpp
#include "A.h"
char * A::getInfo(){
return(&info[256]);
}
A::A(char i[256]){
info[256]=i[256];
}
I'm struggling with the accessor. When I try to use getInfo(), I get a char*, and thus with
char test[256] = "test";
FractionException d(test);
for (int i = 0; i < 256; i++) {
cout << d.getInfo()[i] ;
}
I get
╠╠╠╠╠╠╠╠test
I guess I'm doing things the wrong way, but I cant figure it out..
By the way, VScode also warn me on
info[256]=i[256]
by telling me that 257 octets bytes might be written (C6386) but I dont get it ...
Could you help me please ? Thanks !
The problem is, your constructor is not initializing the contents of the info array correctly, and your accessor is returning a bad pointer.
In the constructor, info[256]=i[256] does not do what you think it does. You are trying to copy the 257th element of i into the 257th element of info, which is Undefined Behavior since neither array has 257 elements. That is why the compiler is warning you about it.
Try this instead:
A::A(char i[256]){
for(int x = 0; x < 256; ++x){
info[x] = i[x];
}
}
Alternatively:
#include <algorithm>
A::A(char i[256]){
std::copy_n(i, 256, info);
}
As for the accessor, it is returning a pointer to the non-existent 257th element. You need to return a pointer to the 1st element instead:
char * A::getInfo(){
return(&info[0]);
}
Or simply:
char * A::getInfo(){
return info;
}
This declaration of the constructor
A(char i[256]);
does not make a great sense because the compiler will adjust the parameter declaration like
A(char *i);
Taking into account this code snippet
char test[256] = "test";
FractionException d(test);
for (int i = 0; i < 256; i++) {
cout << d.getInfo()[i] ;
}
It seems you want that the constructor would accept a string. If so then it should be declared like
A( const char * );
and it can be defined like
#include <cstring>
//...
A::A( const char *i ){
strncpy( info, i, sizeof( info ) );
info[sizeof( info ) - 1] = '\0';
}
The member function getInfo should return the array instead of the address of the non-existent element info[256]
char * A::getInfo(){
return info;
}
This method should be also overloaded
const char * getInfo() const;
And this loop
for (int i = 0; i < 256; i++) {
cout << d.getInfo()[i] ;
}
should be substituted for this statement
std::cout << d.getInfo();
I am struggling to find the correct format for initializing a (private) array within a class and getting/setting the values from outside the class.
My code is semi-functional, but feels awkward in incorrectly formatted.
It is returning only the first element of the array, I want it to return all the contents. Read code comments for additional details.
Note: This is (a very small part of) a project I am working on for school -- an array must be used, not a vector or list.
student.h
class Student {
public:
// Upon researching my issue, I read suggestions on passing pointers for arrays:
void SetDaysToCompleteCourse(int* daysToCompleteCourse[3]);
int* GetDaysToCompleteCourse(); // Ditto # above comment.
private:
int daysToCompleteCourse[3];
student.cpp
#include "student.h"
void Student::SetDaysToCompleteCourse(int* daysToCompleteCourse) {
// this->daysToCompleteCourse = daysToCompleteCourse; returns error (expression must be a modifiable lvalue)
// Feels wrong, probably is wrong:
this->daysToCompleteCourse[0] = daysToCompleteCourse[0];
this->daysToCompleteCourse[1] = daysToCompleteCourse[1];
this->daysToCompleteCourse[2] = daysToCompleteCourse[2];
}
int* Student::GetDaysToCompleteCourse() {
return daysToCompleteCourse;
}
ConsoleApplication1.cpp
#include "pch.h"
#include <iostream>
#include "student.h"
int main()
{
Student student;
int daysToCompleteCourse[3] = { 1, 2, 3 };
int* ptr = daysToCompleteCourse;
student.SetDaysToCompleteCourse(ptr);
std::cout << *student.GetDaysToCompleteCourse(); // returns first element of the array (1).
}
I gave this my best shot, but I think I need a nudge in the right direction.
Any tips here would be greatly appreciated.
I would say:
// student.h
class Student
{
public:
// If you can, don't use numbers:
// you have a 3 on the variable,
// a 3 on the function, etc.
// Use a #define on C or a static const on C++
static const int SIZE= 3;
// You can also use it outside the class as Student::SIZE
public:
void SetDaysToCompleteCourse(int* daysToCompleteCourse);
// The consts are for "correctness"
// const int* means "don't modify this data" (you have a setter for that)
// the second const means: this function doesn't modify the student
// whithout the const, student.GetDaysToCompleteCourse()[100]= 1 is
// "legal" C++ to the eyes of the compiler
const int* GetDaysToCompleteCourse() const; // Ditto # above comment.
Student()
{
// Always initialize variables
for (int i= 0; i < SIZE; i++) {
daysToCompleteCourse[i]= 0;
}
}
private:
int daysToCompleteCourse[SIZE];
// On GCC, you can do
//int daysToCompleteCourse[SIZE]{};
// Which will allow you not to specify it on the constructor
};
// student.cpp
void Student::SetDaysToCompleteCourse(int* newDaysToCompleteCourse)
{
// It's not wrong, just that
// this->daysToCompleteCourse[0] = daysToCompleteCourse[0];
// use another name like newDaysToCompleteCourse and then you can suppress this->
// And use a for loop
for (int i= 0; i < SIZE; i++) {
daysToCompleteCourse[i]= newDaysToCompleteCourse[i];
}
}
const int* Student::GetDaysToCompleteCourse() const
{
return daysToCompleteCourse;
}
// main.cpp
#include <iostream>
std::ostream& operator<<(std::ostream& stream, const Student& student)
{
const int* toShow= student.GetDaysToCompleteCourse();
for (int i= 0; i < Student::SIZE; i++) {
stream << toShow[i] << ' ';
}
return stream;
}
int main()
{
Student student;
int daysToCompleteCourse[3] = { 1, 2, 3 };
// You don't need this
//int* ptr = daysToCompleteCourse;
//student.SetDaysToCompleteCourse(ptr);
//You can just do:
student.SetDaysToCompleteCourse(daysToCompleteCourse);
// On C++ int* is "a pointer to an int"
// It doesn't specify how many of them
// Arrays are represented just by the pointer to the first element
// It's the FASTEST and CHEAPEST way... but you need the SIZE
const int* toShow= student.GetDaysToCompleteCourse();
for (int i= 0; i < Student::SIZE; i++) {
std::cout << toShow[i] << ' ';
// Also works:
//std::cout << student.GetDaysToCompleteCourse()[i] << ' ';
}
std::cout << std::endl;
// Or you can do: (because we defined operator<< for a ostream and a Student)
std::cout << student << std::endl;
}
You can check out it live here: https://ideone.com/DeJ2Nt
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".
I'm pretty new in c++ althought a bit experienced in java, and my problem it's the next one:
I'm doing a sudoku project where I'm creating an abstract data type of the box and the board. In the board one, I'm creating a bidimesional array of boxes, but when I want to create it as a public data so I can use it in the whole class and not only in the board constructor.
I'm creating it in the board constructor because If I don't create it there, I have no way of knowing the value of each dimension, and if I create the variable Box box[int][int] where I can use it in the class, I've got no way of knowing the dimensions. It'll be better understandable with some code.
This code allows me to create the Box array with the dimensions I want, because it's in the board constructor than when it's created it has as a parameters the number of boxes, but it don't let me use the "casilla" variable in the other part of the class nor other classes:
class tablero{
int filas;
int columnas;
public:
tablero (int filas, int columnas){
this->filas = filas;
this->columnas =columnas;
Casilla casilla[filas][columnas];
}
Casilla getCasilla(int n, int m){
return casilla[n][m]; <- Here shows an error because casilla cannot be resolved.
}
And this other code lets me use the casilla variable, but I have to give it the parameters to the dimensions before I know them:
class tablero{
int filas;
int columnas;
public:
Casilla casilla[0][0];
tablero (int filas, int columnas){
this->filas = filas;
this->columnas =columnas;
}
Casilla getCasilla(int n, int m){
return casilla[n][m];
}
No error, but the dimensions of the casilla array have to be given before I know them, and so, they may be the wrong ones (because the board may have different dimensions.
It's the first time I'm programming in c++, and I'm really frustated with this problem, can anyone help me to find a way to make it so it works both ways? (I already tried to leave both dimensions empty and then in the constructor put casilla[][] = Casilla cas[filas] [columnas] but it gives me an error..)
Thanks for the help everyone. Also, If you think the title is not clear enough, you can suggest one and I'll change it.
The Casilla code is this one:
class Casilla{
int fila;
int columna;
int numero;
public:
// constructor
Casilla(int fila, int columna,int numero)
{
this->fila = fila;
this->columna = columna;
this->numero = numero;
}
};
Thanks everyone for your answers, I've already found the answer I needed from 3 different people. I can't upvote all of you because I still don't have 15 reputation, but when I have it i'll upvote you all. Thanks for all your answers, really. I just need to know what I commented on the checked answer and it'll be all answered.
A solution with an array
//----------------------------------------------------------------------------
#include <iostream>
#include <iomanip>
//----------------------------------------------------------------------------
class Casilla
{
int fila;
int columna;
int numero;
public:
// default constructor
Casilla()
{
this->fila = -1;
this->columna = -1;
this->numero = 0;
}
int GetNumero() {return numero;}
void SetCasilla (int _fila, int _columna) //set a cell position
{
fila = _fila;
columna = _columna;
}
void SetCasilla (int _numero) //set a cell value
{
numero = _numero;
}
void SetCasilla (int _fila, int _columna, int _numero) //set a cell position and value
{
fila = _fila;
columna = _columna;
numero = _numero;
}
};
class Tablero
{
int filas;
int columnas;
Casilla **casilla;
public:
Tablero (int filas, int columnas)
{
this->filas = filas;
this->columnas =columnas;
casilla = new Casilla* [filas];
for (int i = 0; i<filas; i++)
casilla[i] = new Casilla [columnas];
for (int i = 0; i<filas; i++)
for (int j = 0; j<columnas; j++)
casilla[i][j].SetCasilla(i,j); //set the right position for each cell
//the values = 0 by default
}
//destructor
~Tablero()
{
for (int i = 0; i<filas; i++)
delete [] casilla[i];
delete [] casilla;
}
//set a cell value in the table
void ChangeCasillaValue (int _fila, int _columna, int _numero)
{
casilla[_fila][_columna].SetCasilla (_numero);
}
Casilla getCasilla(int n, int m)
{
return casilla[n][m];
}
void PrintTablero ()
{
for (int i = 0; i<filas; i++)
{
for (int j = 0; j<columnas; j++)
std::cout << std::setw(5)<<casilla[i][j].GetNumero();
std::cout << "\n";
}
std::cout << "\n";
}
};
//----------------------------------------------------------------------------
int main()
{
int N = 5, M = 6;
Tablero table(N, M);
table.PrintTablero();
table.ChangeCasillaValue(1,1,-5); //change value in cell(1,1)
table.PrintTablero();
std::cin.get();
return 0;
}
//-----------------------------------------------------------------------------
You have to add a bunch of setters and getters of your own.
But, as a draft, it works.
C-style array dimensions must be known at compile-time in C++. So there is no variant of Casilla casilla[filas][columnas]; that will work.
Instead you should use a container which can hold the data you want to put in it. Use of C-style arrays in C++ is discouraged because they have some strange behaviour and rules, they are mainly there for backwards compatibility.
The simplest option is a 1-dimensional array with runtime size, that is called vector:
class tablero{
int filas;
int columnas;
std::vector<Casilla> casilla;
public:
tablero (int filas, int columnas)
: filas(filas), columnas(columnas), casilla(filas * columnas)
{ }
Casilla getCasilla(int f, int c) const
{
return casilla[f * columnas + c];
}
};
Note the use of the constructor initializer list. You should provide initial values for class members this way, instead of using assignment statements inside the constructor.
In your first example, in your constructor, the line
Casilla casilla[filas][columnas];
declares casilla an array of arrays of Casilla objects local to your constructor. Once your constructor returns, casilla goes out of scope. There is no casilla member variable or local variable in your getCasilla function, so of course it cannot be resolved.
In your second example, your class has a public member casilla declared as 0 by 0 array of Casilla objects. Your getCasilla function would return the item in the nth row and mth column of the 0 by 0 array. In C++, there is no bound checking on array dereferences, so this is returning some out of bounds memory location and is very bad.
You can create dynamic C arrays yourself by using malloc and free, but since you are using C++ it will be easier to just use std::vector.
For example, you could use:
#include <iostream>
#include <vector>
class Casilla
{};
class tablero
{
int filas_;
int columnas_;
std::vector<std::vector<Casilla> > casilla_; // a vector of vectors of Casillas
public:
tablero(int filas, int columnas) : filas_(filas), columnas_(columnas),
casilla_(filas, std::vector<Casilla>(columnas))
// The above is an initialization list
// We initialize casilla_ as a vector of filas vectors of columnas Casillas
{}
std::vector<std::vector<Casilla> > getCasilla() const
{
return casilla_;
}
};
int main(int argc, const char* argv[])
{
tablero t(3, 3);
std::cout << "casilla rows: " << t.getCasilla().size() << std::endl;
std::cout << "casilla cols: " << t.getCasilla()[0].size() << std::endl;
return 0;
}
First Casilla casilla[filas][columnas]; needs to be a class variable so it's accessible to all methods.
Second the sizes of rows and columns must be a fixed number e.g Casilla casilla[9][9];
If it need to be dynamically allocated you could you Vectors or Vectors of Vectors.
If it's 2d array, you can still create it as 1d array, but depends which is best for your purposes.
This code works fine in VS2010 but now I am trying to port it to my mac with xcode 4.6 and it's giving me some bad access errors at run time. Basically I have a board class which contains a 2d array of tiles, when I create the board I can access the tiles functions but when I later run my draw function it gives me bad access. Here is a sample of my board class.
Board.h
#include "Tile.h"
class Board
{
private:
//This is the GameBoard of a 2D array of Tiles
Tile *** GameBoard;
void CreateBoard(const int size);
void FillValues();
...
public:
Board(int size);
void DrawBoard();
...
}
Board.cpp
Board::Board(const int size)
{
won=false;
lost=false;
BoardSize =size;
GameBoard = new Tile**[size];
CreateBoard(size);
}
void Board::CreateBoard(const int size)
{
...
FillValues()
}
void Board::FillValues()
{
for(int x=1;x<BoardSize+1;x++)
{
for(int y =1;y<BoardSize+1;y++)
{
if (GameBoard[x][y]->Type()=="NumberTile")
{
int neighbors = CountNeighbours(x,y);
GameBoard[x][y]->SetValue(neighbors);
//This works
}
}
}
}
void Board::DrawBoard()
{
for(int i=0;i<=BoardSize+1;i++)
{
for (int j=0;j<=BoardSize+1;j++)
{
if (GameBoard[i][j]->Type() != "BorderTile") {
GameBoard[i][j]->Draw();
//This does not work, i get the error when it tries to use ->Type()
}
}
}
}
...
I call the functions like this
GI = new Board(SCREEN_SIZE);
GI->DrawBoard();
GameBoard = new Tile**[size];
This just creates an array of Tile**. You don't yet have any actual Tiles or even Tile*s and later, when you're trying to access elements of the array with GameBoard[x][y]->, you're hitting undefined behaviour.
As you have it, you would need to do this:
GameBoard = new Tile**[size]; // Allocate an array of Tile**
for (int i = 0; i < size; i++) {
GameBoard[i] = new Tile*[size]; // Allocate an array of Tile*
for (int j = 0; i < size; j++) {
GameBoard[i][j] = new Tile(); // Allocate an array of Tile
}
}
However, this is awful. It's three lots of dynamic allocation that you have to remember to tidy up at the end (and tidy up correctly).
A simpler approach would be to just have an 2D array of tiles:
Tile GameBoard[CONSTEXPR_SIZE][CONSTEXPR_SIZE];
Or better yet, use the std::array container:
std::array<std::array<Tile, CONSTEXPR_SIZE>, CONSTEXPR_SIZE> GameBoard;
Here, the size given has to be a constant expression. If you need it to be dynamically sized, use a std::vector instead.
In the comments below, you say the size of your array is actually BoardSize+1. Still, you are iterating over too many elements in both your outer and inner for loops:
for(int i=0;i<=BoardSize+1;i++)
This should be:
for(int i=0; i<BoardSize+1; i++)
Also in the comments below, you say that Type returns a char*. That means you can't do your string comparison like this:
GameBoard[i][j]->Type() != "BorderTile"
This simply performs pointer comparison, since the left operand is a char* and the right operand is convertible to const char*. It doesn't compare the strings themselves. Instead, you want:
GameBoard[i][j]->Type() != std::string("BorderTile")
This will force std::string comparison to be used.