C++ how to display multidimentional array in OO design - c++

Hi I'm writing a simple version of Pacman with OO design in C++. I have problem displaying the content of a 2D array. The array constains a bunch of symbols, which represent the wall of the map/maze. Here is the sample code that I wrote:
Game.h
class Game
{
private:
char map;
public:
Game();
void displayMap();
};
Game.cpp
char map[10][20] = {...};
void Game::displayMap()
{
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 20; j++)
{
cout << map[i][j] << " ";
}
cout << endl;
}
}
The compiler will pop out an error at [i] saying "expression must have pointer-to-object type".
But if I define the size of the array in the header file and assign its value when defining the constructor
Game.h
class Game
{
private:
char map[10][20];
};
Game.cpp
Game::Game()
{
char map[10][20] = {...};
}
It will compile but when I try to display the content of the array (using the same code of displayMap()), I found out it's filled with junk. I think it's because that assignment is not an assignment. It's actually an initialization of another array on the stack, so that array is destroyed after the constructor finishes, and the displayMap() at that time display the original unassigned array.
I could be wrong, so feel free to correct me. I need a recommendation on how should I structure my game to avoid this problem

Game::Game() {
char map[10][20] = {...};
}
Here you redeclare a local variable with the same name of the instance variable, hence you hide the outer one. In addition you are trying to assign to an array which has been declared before, that's not possible in C++. You can just initialise an array when you declare it, not afterwards.
You can do this (if you have C++11 support):
class Game {
char map[W][H] = { ... };
}
or you can do this:
class Game {
char map[W][H];
Game() {
char tmp[W][H] = { ... };
memcpy(map, tmp, sizeof(char)*W*H);
}
}
Even better, you can use std::copy. Or even even better just use an std::array and forget normal arrays.

I commented your question, but I think it would be good to make it an answer, so here it is.
The second option should work fine.
Garbage values are normal in C/C++.
What you have to do is to initialize your values inside your constructor ( Game::Game() ). Constructors are meant to be used in these cases.
The behavior of C/C++ is to not assign a default value, it just "takes what's in place in RAM". Other languages do initialize the RAM cells though. It all depends of what programming language you are using.
Including this code inside your constructor (before accessing map[][] for another thing) should work:
for (int ix = 0; ix < 10; ix++)
for (int jx = 0; jx < 20; jx++)
map[ix][jx] = "x"; // x will be your default value

Related

Class constructer from agregation link

I am pretty much new to OOP and C++ but have a project of "graph traversing" (sorry if there's a more formal term).
I am at the very beginning of the project where I have to initialize an 2D grid composed of cases.
I first thought of creating a class Case with 2D-position and a boolean state (occupied of free) as attributes, and a Grid class with a vector of vector of Cases representing the grid.
First I wondered if I'm going in the right direction on terms of Object-oriented programming, and if so I wondered how to initialize the grid in the constructor.
So far I have this for the Case class header :
class case_tab{
int x,y;
bool state;
public:
case_tab(int x_param, int y_param, bool state_param);
};
And the constructor :
case_tab::case_tab(int x_param, int y_param, bool state_param)
:x(x_param),y(y_param),state(state_param)
{}
But the problem comes for the grid constructor (named tableau, here you can see the header):
class tableau
{
int X, Y;
public:
std::vector<std::vector<case_tab>> tab;
tableau(int X_param, int Y_param);
};
Where I don't know how to initialize the grid (tableau) as a grid of free Cases disposed correctly.
I'm pretty sure this is of very basic difficulty but I can't find how to do so, and wonder if it doesn't come from my structure choice in the first place.
Hope someone can help me.
As you declared the attribute tab, it will be initialized by default. So you just have to do two loops so as to fill it. Here is a proposal:
tableau::tableau(int X_param, int Y_param)
: X(X_param), Y(Y_param) {
for(int i = 0; i < X; i++) {
std::vector<case_tab> row;
row.reserve(Y);
for(int j = 0; j < Y; j++) {
row.emplace_back(i, j, false);
}
tab.push_back(row);
}
}
emplace_back generates and adds case_tab objects by calling the constructor that you defined, and reserve allocates memory for the initialized objects.
You should also change tab visibility to private, so as to prevent adding unexpected items to it.

How do I add elements to a vector of objects and print them?

Our teacher told us to create a vector of objects and perform operations on it, but I couldn't understand how to properly do that: I tried to make a simple project with minimum data so that I could know what I was doing.
I have this class
class Obj {
private:
int num;
public:
Obj();
void setNum(int nuovo_num);
int getNum();
};
And then this one, with a vector of Obj
class VettObj{
private:
vector<Obj> vett;
public:
VettObj();
void setVett();
void stampaVett();
};
My initial thought was to use an iterator but I was just making a total mess and, with almost useless research, I decided to use a common integer counter.
I found that I shouldn't write anything in the VettObj costructor, as it automatically initialize stuff, so I left it blank.
The method that adds elements is this
void VettObj::setVett(){
Obj temp;
int i;
i = 0;
while(i < 5){
temp.setNum(10);
vett.push_back(temp);
i++;
}
}
And the one that prints elements
void VettObj::stampaVett(){
int i;
i = 0;
while(i < 5){
vett[i].getNum();
i++;
}
}
When I compile, everything goes well, but when I run the program I get nothing on the screen. I don't want to use mostly vector functions(if not necessary) as I saw that a lot of people can do it like this. I would really like to know how to do it with iterators too. Help pls ????
You are not actually printing anything in the stampaVett() method.
You could try with:
void VettObj::stampaVett(){
int i = 0;
while (i < 5){
std::cout << vett[i].getNum();
i++;
}
}
I'd also suggest using English for method or variable instead of Italian, since SO is an international community.

SFML c++ create multiply objects and call at the constructor?

Hello fellas when I'm trying to create multiply objects from a object and want to call for the constructor I'm getting som strange error.
the error is: a value of type const cha*r cannot be used to initialize an entity of type Obstacle[3]
Creating objects here referes to the Player object because it works, even if I try to for-loop to set my ("img....") I'm getting the same error
static const int CAP = 3;
//Works! Player player = ("img/playerSheet.png");
Obstacle obstacles[CAP] =("img/obstacle.png");
The Obstacle constructor looks like this
Obstacle::Obstacle(string spriteSearch) : Object(spriteSearch)
{
setPosition(750, 500);
}
//Filip
You are using the incorrect method of declaring an array. You create an array that holds three objects. You need to initialize each object with the default constructor. Here is one way to do it.
If you havent seen structs before, dont worry. All you need to know it works like a small class.
#include <iostream>
#include <string>
using namespace std;
struct Object
{
string name;
Object(string _name)
{
name = _name;
}
};
int main()
{
Object obs[3] = {"one", "two", "three"};
cout<<obs[0].name<<obs[1].name<<obs[2].name<<endl;
system("pause");
return 0;
}
Edit; If you have a large capacity, say 1000. I would suggest adding a seperate function. Where you dont initialize in the default constructor, but actually creates an Initialize function. That you call after. As an example;
Obstacle obstacles[CAP];
for(int i=0; i<CAP; i++)
{
obstacles[i].Initialize("img/playerSheet.png");
}
Comment: "Alright, i just edited the constructor above i wrote wrong before... so you mean to forloop and the how do i call the constructor, like obstaclei;? :O"
I might be in the wrong here. But I think in order to call the actual constructor, the only way is to do it like this:
Change:
Obstacle obstacles[CAP] =("img/obstacle.png");
to something like;
const int CAP = 5;
Object** obstacles = new Object*[CAP];
for(int i = 0; i<CAP; i++)
{
obstacles[i] = new Object(filename);
}
//Do other stuff. And when done, release memory.
for(int i=0; i<CAP; i++)
{
delete obstacles[i];
}
delete []obstacles;
It's fairly complicated solution if you are not used to using pointers.

Trying to pass a ** array but keep getting c2664 error

Okay, so I have a class called student and another called faculty that are both derived classes from a person class. I need to make two teams consisting of both student and faculty. I have an array of students and an array of faculty. I created two more arrays for the teams that are both person**. when I try to pass the array I get the compiler error c2664 with a message of cannot convert parameter3 from 'Person**[12] to Person*[]'
Student sArr[12];
Faculty fArr[12];
Person** t1Arr[12];
Person** t2Arr[12];
generateTeams(fArr,sArr,t1Arr,t2Arr);
}
void generateTeams(Faculty fArr[],Student sArr[],Person** t1Arr[], Person** t2Arr[]){....}
I was also getting erros while attempting to assign the student/faculty to the person array so I had to reinterpret_cast as follows:
for(int i=0;i<12;i++){
if(sArr[i].getTeam()==1){
t1Arr[t1Count]=reinterpret_cast<Person**> (&sArr[i]);
t1Count++;
}
else if(sArr[i].getTeam()==2){
t2Arr[t2Count]=reinterpret_cast<Person**> (&sArr[i]);
t2Count++;
}
else if(fArr[i].getTeam()==1){
t1Arr[t1Count]=reinterpret_cast<Person**> (&fArr[i]);
t1Count++;
}
else if(fArr[i].getTeam()==2){
t2Arr[t2Count]=reinterpret_cast<Person**> (&fArr[i]);
t2Count++;
}
}
I can provide mor of the code if needed, but I think what I have shown should suffice as that is where the problem lies.
Student sArr[12] is an array of Students.
If I understand your code, you're trying to assign your Students and Faculty to teams by keeping separate arrays of pointers to Person objects presumably Student and Faculty both inherit from Person).
If this is correct, then you want your teams to be made of of arrays of pointers.
Person* t1Arr[12]
What you have declared instead of are arrays of pointers-to-pointers, which has it's uses, but those are pretty rare.
Assuming that the error message quote
” cannot convert parameter3 from 'Person**[12] to Person*[]
is correct, then it does not match the alleged function declaration
void generateTeams(Faculty fArr[],Student sArr[],Person** t1Arr[], Person** t2Arr[]){....}
i.e. this is not the real code.
Alternatively it's not the real error message.
But basically, assuming that the error message is correctly quoted, it tells you what's wrong: you have declared a function that expects a Person*[] as third argument, but you're passing a Person**[].
At a guess, you have forward-declared the function, and missed a *.
Forward-declaration is a generally Bad Practice™: it's more work for no gain and does sometimes foul up things, as I suspect it has here.
A basic and probably the best solution is to ditch your current raw-array-and-pointers-and-casting code, and use std::vector.
Your problem appears to be in how you create an array of pointers to objects, and how you use those objects from the array. Here's a simplified example of how to do that:
#include <cstdio>
using namespace std;
class person {
const char * _name;
public:
person(const char * n) : _name(n) {}
const char * getname() const { return _name; }
};
person * pa1[12];
int main( int argc, char ** argv )
{
const char * names[12] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l" };
// construct objects
for( int i = 0; i < 12; ++i) {
pa1[i] = new person(names[i]);
}
// use objects
for( int i = 0; i < 12; ++i) {
printf("person %d is %s\n", i, pa1[i]->getname());
}
// destroy objects
for( int i = 0; i < 12; ++i) {
delete pa1[i];
}
return 0;
}
Here I've created a simple class, person, and I've constructed an array of pointers to person like this:
person * pa1[12];
I then used a simple for loop to construct objects with the new operator:
for( int i = 0; i < 12; ++i) {
pa1[i] = new person(names[i]);
}
The new operator allocates space for each object, and calls its constructor.
I can then use those objects by dereferencing the pointer with the -> operator:
for( int i = 0; i < 12; ++i) {
printf("person %d is %s\n", i, pa1[i]->getname());
}
Because I used new to construct the objects, I need to destroy them with delete:
for( int i = 0; i < 12; ++i) {
delete pa1[i];
}
Also note that it's always a good idea to reduce a problem to a short self-contained example in order to best understand it.

How can I access a class's member function via an array of pointers?

I have a pretty standard class with some public member functions and private variables.
My problem originally stems from not being able to dynamically name object instances of my class so I created an array of pointers of the class type:
static CShape* shapeDB[dbSize];
I have some prompts to get info for the fields to be passed to the constructor (this seems to work):
shapeDB[CShape::openSlot] = new CShape(iParam1,sParam1,sParam2);
openSlot increments properly so if I were to create another CShape object, it would have the next pointer pointing to it. This next bit of code doesn't work and crashes consistently:
cout << shapeDB[2]->getName() << " has a surface area of: " << shapeDB[2]->getSA() << shapeDB[2]->getUnits() << endl;
The array of pointers is declared globally outside of main and the get() functions are public within the class returning strings or integers. I'm not sure what I'm doing wrong but something relating to the pointer set up I'm sure. I'm writing this code to try and learn more about classes/pointers and have gotten seriously stumped as I can't find anyone else trying to do this.
I'm also curious as to what the CShape new instances get named..? if there is any other way to dynamically create object instances and track the names so as to be able to access them for member functions, I'm all ears.
I've tried all sorts of permutations of pointer referencing/de-referencing but most are unable to compile. I can post larger chunks or all of the code if anyone thinks that will help.
class CShape {
int dim[maxFaces];
int faces;
string units;
string type;
string name;
bool initialized;
int slot;
public:
static int openSlot;
CShape();
CShape(int, string, string); // faces, units, name
~CShape();
void initialize(void);
// external assist functions
int getA(void) {
return 0;
}
int getSA(void) {
int tempSA = 0;
// initialize if not
if(initialized == false) {
initialize();
}
// if initialized, calculate SA
if(initialized == true) {
for(int i = 0; i < faces; i++)
{
tempSA += dim[i];
}
return(tempSA);
}
return 0;
}
string getUnits(void) {
return(units);
}
string getName(void) {
return(name);
}
// friend functions
friend int printDetails(string);
};
// constructor with values
CShape::CShape(int f, string u, string n) {
initialized = false;
faces = f;
units = u;
name = n;
slot = openSlot;
openSlot++;
}
My guess is you use the CShape constructor to increment CShape::openSlot?
You're probably changing the value before it's read, thus the pointer is stored in a different location.
Try replacing openSlot with a fixed value to rule out this CShape::option.
-- code was added --
I'm pretty sure this is the problem, the constructor is executed before the asignment, which means the lhs. will be evaluated after CShape::openSlot is incremented.