How to build a dynamic 2d array inside a constructor? - c++

Building a Memory game. Users can input the number of cards they would like to deal on their 'board'. I am taking that input number(int size) and building a 2d array of objects as the game board, which is stored in a constructor. My next step after getting this to work will be to replace the array elements with 'dealt' card objects to display. **Receiving initialization for the arrays and am curious why. Do you have any suggestions as to why or better ways to write this? **
.cpp file relvant code
#include <iostream>
#include "GameBoard.h"
#include <math.h>
using namespace std;
GameBoard::GameBoard(int size)
{
if (size % 2 != 0)
{
size++;
}
int row;
int col;
row = 4;
col = ceil(size/4);
Card gameBoard = new Card*[row];
int i;
for(i=0; i<row; i++){
Card gameBoard[i] = new Card[col];
}
.h file
#ifndef GameBoard_h
#define GameBoard_h
#include "Deck.h"
#include <iostream>
using namespace std;
class GameBoard{
private:
Card **gameBoard;
Deck deck;
Card card;
int CardsToDeal;
public:
GameBoard(int size);
GameBoard();
void initializeBoard();
};
#endif

If you know the size at compile-time, use std::array. When you don't use std::vector. These can of course be nested, like:
std::array<std::vector<Card>, 4> gameBoard;
If the number or "rows" are small you can initialize directly in the constructor initializer list:
GameBoard::GameBoard(int size)
: gameBoard {
std::vector<Card>(std::ceil(size / 4.0)),
std::vector<Card>(std::ceil(size / 4.0)),
std::vector<Card>(std::ceil(size / 4.0)),
std::vector<Card>(std::ceil(size / 4.0))
}
{
// Rest of constructor...
}
Otherwise let the array default-construct and set up the vectors inside the constructor body in a loop:
GameBoard::GameBoard(int size)
{
int columns = std::ceil(size / 4.0);
for (auto& card_vector : gameBoard)
{
card_vector.resize(columns);
}
// Rest of constructor...
}

Related

unresolved overloaded function type [int] when implementing graph

I try to implement graph bfs but get compiler error
error:
invalid types '<unresolved overloaded function type>[int]' for array subscript|
My questions:
Is my approach making array of vector of struct is right approach? And how can I solve the compiler error?
How to initialize array value to infinity?
When making undirected graph, should I push back 2 times?
My code:
#include <iostream>
#include <deque>
#include <vector>
#define INT_MAX 21422
using namespace std;
int distancee[10]={4,4,4,4,4,4,4,4,4,4}; //want to intialize all to infinity
struct bfss{
int firstt;
int secondd;
};
vector<bfss>bfs[10];
void bfsfunc(int start){
deque<int> q;
q.push_back(start);
distancee[start]=0;
while(!q.empty()){
int v=q.front();
q.pop_front();
for(int i=0;i<bfs[v].size();i++){
if(distance[bfs[v][i].firstt]>(distance[v]+bfs[v][i].secondd)){ // got error in this line
distance[bfs[v][i].firstt]=distance[v]+bfs[v][i].secondd;
if(bfs[v][i].second==0)
{
q.push_front(bfs[v][i].first);
} else {
q.push_back(bfs[v][i].second);
}
}
}
}
}
int main()
{
int edges,nodes,x,y,z;
cin>>edges>>nodes;
for(int i=0;i<edges;i++){
cin>>x>>y>>z; //x is array subscript , y is node(x-y is edge) , z is weight
bfss newbfs;
newbfs.firstt=y;
newbfs.secondd=z;
bfs[x].push_back(newbfs);
bfss newbfs;
newbfs.firstt=x;
newbfs.secondd=z;
bfs[y].push_back(newbfs); // when making undirected graph, should i push back 2 times?
}
bfsfunc(0);
return 0;
}
As mentioned, you had a few typos on distancee, firstt and secondd. Fix those and the errors goes away. For an int the closest you'll come to infinity is it's max value. With that and a few other minor changes (comments in the code), this is what I came up with:
#include <iostream>
#include <deque>
#include <vector>
#include <array>
#include <limits> // std::numeric_limits
// removed using namespace std;
// max distance: not infinity, but hopefully large enough
constexpr int md = std::numeric_limits<int>::max();
// replaced the C array with a standard C++ array
std::array<int, 10> distancee={md,md,md,md,md,md,md,md,md,md};
struct bfss { // shouldn't these be unsigned?
int firstt;
int secondd;
};
std::vector<std::vector<bfss>> bfs(10); // replaced C array with a standard C++ vector
void bfsfunc(int start) {
std::deque<int> q;
q.push_back(start);
distancee[start]=0;
while(!q.empty()) {
int v=q.front();
q.pop_front();
// using size_t intstead of int for array subscript
for(size_t i=0;i<bfs[v].size();i++) {
if(distancee[bfs[v][i].firstt]>(distancee[v]+bfs[v][i].secondd)) {
distancee[bfs[v][i].firstt]=distancee[v]+bfs[v][i].secondd;
if(bfs[v][i].secondd==0) {
q.push_front(bfs[v][i].firstt);
} else {
q.push_back(bfs[v][i].secondd);
}
}
}
}
}
int main()
{
int edges,nodes,x,y,z;
std::cin>>edges>>nodes;
for(int i=0;i<edges;i++) {
std::cin>>x>>y>>z; //x is array subscript , y is node(x-y is edge) , z is weight
// using emplace_back to let the vector create the bfss in place
bfs[x].emplace_back(bfss{y, z});
bfs[y].emplace_back(bfss{x, z});
}
bfsfunc(0);
return 0;
}
I don't know the answer to the question about pushing twice since I don't know the algorithm.

C++ Declaring arrays in class and declaring 2d arrays in class

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".

c++ segmentation fault for dynamic arrays

I want to add a theater object into a boxoffice object in a C++ code. When I try to add it in main code, first one is added successfully. But a segmentation fault occurs for second and obvioulsy other theater objects. Here is the add function;
#include <iostream>
#include <string>
#include "BoxOffice.h"
using namespace std;
BoxOffice::BoxOffice()
{
sizeReserv = 0;
sizeTheater = 0;
theaters = new Theater[sizeTheater];
reserv = new Reservation[sizeReserv];
}
BoxOffice::~BoxOffice(){}
void BoxOffice::addTheater(int theaterId, string movieName, int numRows, int numSeatsPerRow){
bool theaterExist = false;
for(int i=0; i<sizeTheater; i++)
{
if(theaters[i].id == theaterId)
{
theaterExist=true;
}
}
if(theaterExist)
cout<<"Theater "<<theaterId<<"("<<movieName<<") already exists"<< endl;
else
{
++sizeTheater;
Theater *tempTheater = new Theater[sizeTheater];
if((sizeTheater > 1)){
tempTheater = theaters;
}
tempTheater[sizeTheater-1] = Theater(theaterId,movieName,numRows,numSeatsPerRow);
delete[] theaters;
theaters = tempTheater;
cout<<"Theater "<<theaterId<<"("<<movieName<<") has been added"<< endl;
cout<<endl;
delete[] tempTheater;
}
}
And I get segmentation fault on this line;
tempTheater[sizeTheater-1] = Theater(theaterId,movieName,numRows,numSeatsPerRow);
This is Theater cpp;
#include "Theater.h"
using namespace std;
Theater::Theater(){
id=0;
movieName="";
numRows=0;
numSeatsPerRow=0;
}
Theater::Theater(int TheaterId, string TheaterMovieName, int TheaterNumOfRows, int TheaterNumSeatsPerRow)
{
id = TheaterId;
movieName = TheaterMovieName;
numRows = TheaterNumOfRows;
numSeatsPerRow = TheaterNumSeatsPerRow;
theaterArray = new int*[TheaterNumOfRows];
for(int i=0;i<TheaterNumOfRows;i++)
theaterArray[i]= new int[TheaterNumSeatsPerRow];
for(int i=0; i<TheaterNumOfRows;i++){
for(int j=0;j<TheaterNumSeatsPerRow;j++){
theaterArray[i][j]=0;
}
}
}
This is header file of Theater;
#include <iostream>
#include <string>
using namespace std;
class Theater{
public:
int id;
string movieName;
int numRows;
int numSeatsPerRow;
int **theaterArray;
Theater();
Theater(int TheaterId, string TheaterMovieName, int TheaterNumOfRows, int TheaterNumSeatsPerRow);
};
And this is how i call add functions;
BoxOffice R;
R.addTheater(10425, "Ted", 4, 3);
R.addTheater(8234, "Cloud Atlas", 8, 3);
R.addTheater(9176, "Hope Springs",6,2);
The problematic lines are these:
if((sizeTheater > 1)){
tempTheater = theaters;
}
First you allocate memory and assign it to tempTheater, but here you overwrite that pointer so it will point to the old memory. It does not copy the memory. Since the code is for a homework assignment, I'll leave it up to you how to copy the data, but I do hope you follow the rule of three for the Theater class (as for the BoxOffice class) which will make it very simple.
Also, there's no need to allocate a zero-size "array", just make the pointers be nullptr (or 0).

C++ ~ call to function in client gives error: "identifier ____ is undefined"

I'm coming to you with a problem that has several different files involved. I'm not sure why I'm getting the error specified in the title. Let me put the files below and go from there.
DummyClient.cpp
#include "Gameboard.h" //for Gameboard
#include "Location.h" //for function prototypes
#include "zList.h" //for Zombies
#include <iostream> //for input/output stream
using namespace std;
void main()
{
srand(123456789);
Gameboard myGB;
myGB = Gameboard();
ZombieListClass();
ZombieRec zombieList[MAX_ZOMBIES];
PopulateZombies(zombieList[MAX_ZOMBIES]); // this throws the error here of "Error: identifier "PopulateZombies" is undefined"
}
zList.h
#ifndef ZLIST_H
#define ZLIST_H
#include "Location.h" // for list record
#include "ZombieRec.h"
#include "Gameboard.h"
class ZombieListClass
{
public:
ZombieListClass(); //default constructor
void PopulateZombies(ZombieRec zombieList[]);
bool IsInBounds(int row, int col);
private:
ZombieRec list[MAX_ZOMBIES]; //stores the items in the list
int length; //# of values currently in the list
int currPos; //position of current element
int strength; // health and attack units of a zombie
};
#endif
zList.cpp
#include "zList.h"
ZombieListClass::ZombieListClass() //default constructor
{
length = 0;
currPos = 0;
strength = 5;
LocationRec zombieLoc;
}
void ZombieListClass::PopulateZombies(ZombieRec zombieList[])
{
int row, col;
for (int i = 0; i < MAX_ZOMBIES; i++)
{
row = rand() % MAX_ROW + 1;
col = rand() % MAX_COL + 1;
while (!IsInBounds(row, col))
{
row = rand() % MAX_ROW + 1;
col = rand() % MAX_COL + 1;
}
zombieList[i].currLoc.row = row;
zombieList[i].currLoc.col = col;
}
}
bool ZombieListClass::IsInBounds(int row, int col)
{
if (row == 0 || row == MAX_ROW + 1 || col == 0 || col == MAX_COL + 1)
{
return false;
}
else
{
return true;
}
}
Gameboard.h
#ifndef GAMEBOARD_H
#define GAMEBOARD_H
#include "Location.h"
#include "ZombieRec.h"
#include "zList.h"
const int MAX_ROW = 3; // total number of rows in the board
const int MAX_COL = 3; // total number of cols in the board
class Gameboard
{
public:
Gameboard();
private:
int boardSizeArr[MAX_ROW + 2][MAX_COL + 2];
}; // end Gameboard
#endif
and finally, Gameboard.cpp
#include "Gameboard.h"
Gameboard::Gameboard()
{
// Declares a board with a boundary along the outside
boardSizeArr[MAX_ROW + 2][MAX_COL + 2];
}
I'm not looking to be spoonfed and for somebody to solve my problem for me, I'm trying to figure out what I'm doing wrong so that the remainder of my project isn't as bumpy as it has been this whole time.
Looking back on my error, "identifer "PopulateZombies" is undefined", I can't imagine why it is. Could this have something to do with the scope of how I'm doing things? If I've left any code out (I didn't put everything in there but I think I have everything relevant) just let me know, I'm able to converse back and forth as long as this takes.
Thank you to everybody in advance that tries to help :)
-Anthony
In general, you call the function using a variable, instead of calling it directly if defined in a class:
ZombieListClass zombieList=new ZombieListClass(); // add a variable here
ZombieRec zombieList[MAX_ZOMBIES];
zombieList.PopulateZombies(zombieList[MAX_ZOMBIES]); // See the difference?
I am not sure whether the error you posted is the only error. Here is what I see in your main.cpp
#include "Gameboard.h" //for Gameboard
#include "Location.h" //for function prototypes
#include "zList.h" //for Zombies
#include <iostream> //for input/output stream
using namespace std;
void main()
{
srand(123456789);
Gameboard myGB;
myGB = Gameboard();//The constructor"Gameboard()" is automatically called when you defined
//myGB in the previous line,
ZombieListClass();//see Hai Bi's great answer on this one
ZombieRec zombieList[MAX_ZOMBIES];//ZombieRec is a member of ZombieListClass, use . to access it
PopulateZombies(zombieList[MAX_ZOMBIES]); //Also see Hai Bi's answer
}
My advice is to revisit the concept of constructor and class definition before put your hands on s a problem like this.

Pointer declaration in .h, instantiation in .cpp but unresolved in other project files

I am trying to declare an array of pointers to my Node class/object in a header file and then in the constructor for the class I want to instantiate the size of the array. I then wish to initialize the array with Node object.
The problems that I am having have to do with correct syntax when first declaring the Node array in the SOM.h file. I have tried just having it be a Node* nodeArray and Node* nodeArray[][some constant number]. Not sure if either or both of these are a correct way to do this.
Then in the SOM.cpp constructor I am initializing this this way nodeArray = Node[Config::NODE_GRID_HEIGHT][Config::NODE_GRID_WIDTH]
I then run an initializing function for the array of node pointers
void SOM::RandInitilizeNodeArray(){
srand (time(NULL));
for(int i=0; i<10; i++){
for(int j=0; j<10; j++){
nodeArray[i][j] = new Node();
nodeArray[i][j]->modelVec[0] = (rand() % 256)/255;//there is a uniform real distribution that gives better results
nodeArray[i][j]->modelVec[1] = (rand() % 256)/255;//THE 256 HERE MIGHT NEED TO BE 255
nodeArray[i][j]->modelVec[2] = (rand() % 256)/255;
}
}
}
For each of the 3 times I try to access the modelVec I get "Field "modelVec" could not be resolved" from eclipse. Why? Am I not declaring the array of pointers correctly, not initializing correctly, not accessing correctly. Maybe eclipse just hates me.
Here is more code to look at.
SOM.h
#ifndef SOM_H
#define SOM_H
#include "Node.h"
#include "LoadFile.h"
//#include "LearningFunc.h"
#include "Config.h"
#include <cstdlib>
#include <string>
#include "opencv2/core/core.hpp"
#include <vector>
#include <iostream>
#include <stdio.h>
#include <opencv2/highgui/highgui.hpp>//FOR TESTING
//#include <highgui.h>
class SOM{
public:
// Variables
Node* nodeArray;//[][Config::NODE_GRID_WIDTH];
//Node* nodeArray;
cv::Mat inputImg;
cv::Mat normalizedInputImg;
std::string filename;
cv::Mat unnormalizedInputImg;//FOR TESTING
cv::Mat outputImg;
//LearningFunc thislearner();
// Functions
//Node* FindWinner(std::vector<uchar>);//send in a pixel vector get a winning node in return
void BatchFindWinner();
Node* SingleFindWinner(uchar*);
void NormilizeInput();
void InitilizeNodeArray();
void RandInitilizeNodeArray();
float GetSimilarity(uchar*, uchar*);
void AllocateNodeArray();
void OutputNodeArray();
void UnnormalizeInputImage();
void DisplayWins();
cv::Mat OutputSom();
void MyPause(std::string);//FOR TESTING
void WriteSomToDisk(std::string);
// Constructors
SOM(){};
SOM(std::string);
~SOM(){};
private:
};
#endif // SOM_H
SOM.cpp
SOM::SOM(std::string file){
filename = file;
inputImg = LoadFile(filename);
nodeArray = Node[Config::NODE_GRID_HEIGHT][Config::NODE_GRID_WIDTH];
//nodeArray = new Node[NODE_GRID_HEIGHT][NODE_GRID_WIDTH];
AllocateNodeArray();
InitilizeNodeArray();
//OutputNodeArray();//FOR TESTING
}
void SOM::RandInitilizeNodeArray(){
srand (time(NULL));
for(int i=0; i<10; i++){
for(int j=0; j<10; j++){
nodeArray[i][j] = new Node();
nodeArray[i][j]->modelVec[0] = (rand() % 256)/255;//there is a uniform real distribution that gives better results
nodeArray[i][j]->modelVec[1] = (rand() % 256)/255;//THE 256 HERE MIGHT NEED TO BE 255
nodeArray[i][j]->modelVec[2] = (rand() % 256)/255;
}
}
}
Node.h
#ifndef NODE_H
#define NODE_H
#include <vector>
#include "Config.h"
class Node{
public:
//variables
//unsigned int location[2];//location of node in 2d grid
std::vector<unsigned int> winnerDataPixels;
uchar* modelVec;
//functions
//constructors
Node();
~Node(){};
private:
};
#endif //node.h end define
Node.cpp
#include "Node.h"
#include "Config.h"
Node::Node(){
modelVec = uchar[Config::vectorLength];
}