Given a struct like
struct Square
{
Square(Color p_color): color_(p_color) {}
Color color_;
};
how can i declare a two dimensional array and later initialize it. For example,
typedef Square (&Square8x8)[8][8];
Square8x8 initSquares()
{
Square board[ROWS][COLS]; //declare the array
for(int row=0;row<ROWS;row++)
for(int col=0;col<COLS;col++)
{
if(col%2 == 0)
board[row][col]= Square(WHITE); //actual initlization
else
board[row][col] = Square(BLACK);
}
return board;
}
You misunderstand the point of a constructor, which is to ensure an object is always initialized and valid during its entire lifecycle. Thus the invocation of a constructor cannot be in any way delayed or postponed.
It's a typical XY-problem though - you're asking for help with an intended solution, not a problem. The real problem is that you don't want to postpone the constructor, but initialize it later on. Construction and initialization are related subjects, but not identical.
The best solution is to give your class a setColor or initialize function, separate from the constructor. An alternative solution (which is closer to your question) is to declare the array not as objects, but as pointers, and then really instantiate the object later on with new Square(WHITE). I'd go for the first though, the second requires a lot more lifecycle control with explicit deletion.
You need a default constructor for Square which is able to call without parameters. For example:
struct Square
{
Square() {} // <------------------------- For example
Square(Color p_color): color_(p_color) {}
Color color_;
};
Otherwise, you should use pointer to Square and new them later. For example:
struct Square
{
Square(Color p_color): color_(p_color) {}
Color color_;
};
const int ROWS = 8;
const int COLS = 8;
In this case, you should use pointers:
std::unique_ptr<Square> board[ROWS][COLS];
for (int i=0; i<ROWS; i++)
for (int j=0; j<COLS; j++)
board[i][j] = std::unique_ptr<Square>(new Square(RED));
or (for bare pointers)
Square* board[ROWS][COLS];
for (int i=0; i<ROWS; i++)
for (int j=0; j<COLS; j++)
board[i][j] = new Square(RED);
....
// Be careful, you should delete them all
In case you don't want to use a default constructor you can use the constructor you already have.
This solution uses std::vector, which I also recommend you use.
std::vector<Square> myVector( ROWS*COLS, Square(WHITE) );
this will create an array of ROWS * COLS elements, each initialized to the value of Square(WHITE).
You could create a Board class, which uses such a vector inside and offers functionalities such as initializing a board of arbitrary size and indexing a Square in the linear vector based on Row and Column information.
You can also do:
Square board[2][2] = {Square(WHITE), Square(WHITE), Square(WHITE), Square(WHITE) };
but it doesn't really scale well.
Create an array of (smart) pointers instead of an array. One purpose of pointers is so objects can be initialized later.
Related
I only found solutions for 1d arrays, but couldn't apply them to 2d arrays.
The possible solutions included "vectors", "templates", and "pointers to arrays".
I know I can get vectors to work, but I would rather use either of the other 2. Preferably templates because I don't want to manually destruct, but pointers work too. (the pointer would be pointed to an array created in the constructor).
The class contains an empty 2d array called screen. The constructor is supposed to set its size. I tried too many things for me to list them all here, but I'll show what I currently have. (last thing i tried were pointers to arrays created in the constructor. in this case screen was a char pointer)
Screen::Screen(const int w, const int h) : screen(new char[h][w]) {} {
width = w;
height = h;
}
array size in new-expression must be constant
I failed implementing either of those strategies and received many kinds of errors while trying to make it work. How would I solve this problem? (primarily I want to know how to do this with templates. if not possible then with pointers to arrays created in the constructor)
The question was a little ambiguous, but it sounds like you want to dynamically allocate an array given some input.
Edit I changed the answer to match the code you provided. This creates a 2d array of chars given the height and width.
class Screen {
private:
char **data;
int rows;
int columns;
public:
Screen(int num_rows, int num_cols);
};
Screen::Screen(int num_rows, int num_cols) {
data = new char * [num_rows];
for (int i = 0; i < num_rows; ++i) {
data[i] = new char[num_cols];
}
rows = num_rows;
columns = num_cols;
}
This creates an empty 2D array of chars.
Explanation: All arrays in c are just pointers to the first block in memory of the type you have declared. By having the member variable as double pointer, you have an array of char pointers, which each point to the first value in each of their respective arrays.
BUT be careful, you WILL need to free the data variable to avoid memory leaks, by declaring a destructor.
Screen::~Screen() {
for (int i = 0; i < rows; ++i) {
delete[] data[i];
}
delete[] data;
}
I created a parameterizd constructor area. I used area to initialize the diagonal points. In the int main section, I am not able to call the constructor. Please correct my code and explain the mistakes:
int main()
{
int ab,p,q,r,s,l,m,n,o;
cout<<"Enter the number of rectangles: ";
cin>>ab;
for (int i=0; i<ab; i++)
{
cout<<"For rectangle "<<i+1<<endl;
cout<<"Enter the starting and ending values of the 1st diagonal: ";
cin>>p>>q>>r>>s;
cout<<"Enter the starting and ending values of the 2nd diagonal: ";
cin>>l>>m>>n>>o;
area obj[i](p,q,r,s,l,m,n,o);
obj[i].findArea();
obj[i].display();
}
return 0;
}
Just write :)
area obj(p,q,r,s,l,m,n,o);
obj.findArea();
obj.display();
As for the statement
area obj[i](p,q,r,s,l,m,n,o);
then you may not initialize arrays such a way. And it does not make sense to define an array within the loop.
Assuming the array obj should be used outside the loop I would suggest using a std::vector instead, declared before the loop. Then you have two alternatives:
Declare the vector and reserve enough memory (so the data doesn't have to be reallocated when adding new elements), then call emplace_back to add new elements.
std::vector<area> obj;
obj.reserve(ab);
for (int i=0; i<ab; i++)
{
...
obj.emplace_back(p,q,r,s,l,m,n,o);
}
Declare the vector with the correct size (ab) and have all elements default constructed, and then copy the actual object into place using simple assignment. This requires that area can be default constructed, and possible a copy-assignment or move-assignment operator.
std::vector<area> obj{ab};
for (int i=0; i<ab; i++)
{
...
obj[i] = area(p,q,r,s,l,m,n,o);
}
If you don't want an array (or a vector), and each object in the loop should just exist in the loop and nothing needs to be used after the loop, just declare the object with the correct arguments to the constructor: See the answer from Vlad from Moscow for how to do this.
I have created a dynamic matrix of class objects but i have made a big mess with handling the returned pointers.
My intention is to create a matrix of class Point( Int x,Int y) and later to use it in different ways in the program.
Everything is working but i can't figure out the returned pointers game between the functions.
class Point
{
private:
int x;
int y;
public:
Point(int x,int y);
void SetPoint(int x,int y);
};
In a second class I use a Point object as class member.
Init_Pallet() is used to Initialize the Matrix.
class Warehouse
{
private:
Robot r1,r2;
Point *Pallet_Matrix;
public:
Point* Init_Pallet();
};
This is the Init function
Point* Warehouse::Init_Pallet()
{
int rows =10,cols =10;
Point** Pallet_Matrix = new Point*[rows];
for (int i = 0; i < rows; i++)
Pallet_Matrix[i] = new Point[cols];
for (int i = 0; i < rows; ++i)
for (int j = 0; j < cols; j++) //Pallet matrix Init, x point for robots amount in position and y for box amount
Pallet_Matrix[i][j].SetPoint(0,0);
return *Pallet_Matrix;
}
The Init function is called by WareHouse C'Tor (ignore the other vars)
Warehouse::Warehouse(Robot p_r1,Robot p_r2): r1(p_r1),r2(p_r2)
{
this->r1=p_r1;
this->r2=p_r2;
Point *p =Init_Pallet();
this->Pallet_Matrix=p;
}
My question is: How do I return the address to the beginning of the matrix from the Init function to the C'Tor who called it?
And second question: how do i access the matrix different locations in the format of Matrix[i][j] after returning the matrix adress to the C'Tor.
Thank you in advance for all the help and your time.
You should just have Init_Pallet return a Point** and then do return Pallet_Matrix;. Currently you're copying one of the Point*s that you allocated out of the function, so the copy is no longer part of a contiguous array that you can index.
Don't forget to delete[] the dynamically arrays in your destructor.
However, you should much prefer to use the standard library containers like std::array or std::vector. Then you don't need to worry about the dynamic allocation yourself and no pointers to get in a mess with.
If I were doing it, I would just have:
class Warehouse
{
public:
Warehouse() : Pallet_Matrix() { }
private:
Robot r1,r2;
std::array<std::array<Point, 10>, 10> Pallet_Matrix;
};
And that's it. No init needed. No dynamic allocation. No assigning 0 to every element (if you give Point a default constructor that zero-initialises). Done.
How do I return the address to the beginning of the matrix from the Init function to the C'Tor?
In case you would really need just an address of first element, pretty straightforward would be:
return &Pallet_Matrix[0][0];
how do i access the matrix different locations in the format of Matrix[i][j] after returning the matrix address
Init_Pallet is a member function, which could simply work with the Pallet_Matrix member directly. Otherwise, the Init_Pallet function could actually return Point**, which should however make you feel that something's wrong with this code.
Better[1] solution would be:
Define the default constructor for Point:
class Point
{
public:
Point() : x(0), y(0){}
...
Use std::vectors instead of dynamically allocated arrays:
class Warehouse
{
private:
std::vector< std::vector<Point> > Pallet_Matrix;
and instead of:
Point *p =Init_Pallet();
this->Pallet_Matrix=p;
you would simply use std::vector's constructor:
int rows = 10, cols = 10;
Pallet_Matrix = std::vector< std::vector<Point> >(rows, cols);
[1] Better = You don't want to handle the memory management on your own.
The problem is that the returned type of Init_Pallet() is wrong — its a row, not a matrix. And in the last line of Warehouse::Init_Pallet() you dereference the proper pointer to matrix obtaining the pointer to the first row of the matrix.
You need to write Point **Pallet_Matrix; in Warehouse, use Point** Warehouse::Init_Pallet() definition of Init_pallet(), and return Pallet_Matrix in the last line of Init_Pallet().
The notation Point *row means the row is "the array of points" or "the pointer to the beginning of the array of points". The notation Point **matrix means the matrix is "the array of pointers to the beginnings of the arrays of points" or "the pointer to the beginning of such an array".
First: are the dimensions really constant, or is this just an
artifact of your having simplified the code for posting? If
they are really constant, there's no need for dynamic
allocation: you can just write:
Point palletMatrix[10][10];
and be done with it. (If you have C++11, it's even better; you
can use std::array, and palletMatrix will have full object
semantics.)
If you do need dynamic indexes, the only reasonable way of
doing this is to write a simple matrix class, and use it:
class Matrix
{
int m_rows;
int m_columns;
std::vector<Point> m_data;
public:
Matrix( int rows, int columns )
: m_rows( rows )
, m_columns( columns )
, m_data( rows * columns, Point( 0, 0 ) )
{
}
Point& operator()( int i, int j )
{
return m_data[ i * m_columns + j ];
}
// ...
};
Trying to maintain a table of pointers to tables is not a good
solution: it's overly complex, it requires special handling to
ensure that each row has the same number of columns, and it
generally has poor performance (on modern machines, at least,
where locality is important and multiplication is cheap).
Note too that the actual data is in an std::vector. There are
practically no cases where a new[] is a good solution; if you
didn't have std::vector (and there was such a time), you'd
start by implementing it, or something similar. (And
std::vector does not use new[] either.)
EDIT:
One other thing: if you're putting Point in a matrix, you might
want to give it a default constructor; this often makes the code
simpler.
Hello I am working on a tic-tac-toe project on my own. I am not taking a programming class so its not homework.
I have written pretty much the rest of the code and am now working on the AI.
For the AI i am going to have it take a copy of the (2 dimensional array) and it check if it can win in a move, then if the player can, then if it can in 2 moves, then the player and so on, there are some other things i will include. So in order to do this i think i need to pass a copy of the array, but i am not sure how to copy an array. So that is basically my question, how do you copy an array?
You can wrap the array into a class, in which case it'll be copied automatically by the default-generated copy constructor and assignment operator:
enum EField { empty, cross, oh };
typedef std::array<EField, 9> TTTBoard; // C++11, or use std::tr1::array
Or do it manually:
struct TTTBoard
{
EField data[9]; // or even EField data[3][3]
};
Now you can copy it at will:
TTTBoard b;
TTTBoard c = b;
A neat trick you could do without concerning yourself with array copying is to create a type for your 3x3 board:
struct TicTac{
int board[3][3];
}
And just pass that as the argument to your function.
What kind of array do you have?
I'm assuming you have the plain old school integer array.
when you have:
int Array[3][3];
then to copy it's contents to another array you will have to loop each element.
this can be done like this:
int MyArray[3][3];
int CopyHere[3][3];
for(int i = 0; i < sizeof(MyArray); ++i)
{
for(int j = 0; j < sizeof(MyArray[i]); ++j)
{
CopyHere[i][j] = MyArray[i][j];
}
}
I have somewhere an old TicTacToe script, lemme look for it.
Edit: found it:
http://pastebin.com/Xp5iT2b0
Here is the related topic:
http://forum.sa-mp.com/showthread.php?t=259549
I'm currently working on a card game, and I'm having trouble with some initialization code:
// in my class...
Card cards[20];
// in method...
for(int i = 0; i <= 20;i++)
cards++ = new Card(i, /*i as char +*/ "_Card.bmp");
The trouble is that my compiler's telling me that cards++ is not an l-value. I've read up on the whole pointer-array equivalence thing, and I thought I understood it, but alas, I can't get it to work. My understanding is that since cards degrades to a pointer, and the new operator gives me a pointer to the location of my new instance of Card, then the above code should compile. Right?
I've tried using a subscript as well, but isn't cards+i, cards++, and cards[i] just 3 ways of saying the same thing? I thought that each of those were l-values and are treated as pointers.
Card cards[20];
cards is already an array of objects. They are constructed with the default constructor(constructor with no arguments). There is no need to new again. Probably you need a member function equivalent to constructor arguments and assign through it.
for ( int i=0; i<20; ++i ) // array index shouldn't include 20
cards[i].memberFunction(/*....*/);
Even simpler is to use std::vector
std::vector<Card> cards;
for( int i=0; i<20; ++i )
cards.push_back(Card(i, /*i as char +*/ "_Card.bmp"); )
The code Card cards[20]; already creates an array of 20 Card objects and creates them with the default constructor. This may not be what you want given your code.
I would suggest using vector instead.
std::vector<Card> cards;
for(int i = 0; i < 20;i++)
{
cards.push_back(Card(i, /*i as char +*/ "_Card.bmp"));
}
Note that your for loop goes from 0 to 20 and thus one past the end of the array.
If you want to avoid unnecessary constructor calls and unnecessary resizing, then it's more complicated, because C++ normally initialises each objects one-by-one as it's allocated. One workaround is to do it the Java way -- use a loop and an array of pointers, like so:
Card *cards[20];
for (int i=0; i<20; i++) {
cards[i] = new Card(i);
}
Another option is to use malloc to get explicitly uninitialized memory:
Card *cards = malloc(20 * sizeof(Card));
for (int i=0; i<20; i++) {
new (&(cards[i])) Card(i);
}
Well, there is another possibility, when you are ok with your constructors being called automatically at initialization:
// in my class...
Card cards[20] = { Card(0, "0_Card.bmp"), Card(1, "1_Card.bmp"), /* ... */ };
The huge downside is that you cannot use a loop in this case.
An array name, cards in your code, contains the address of the first element of the array. Such addresses are allocated at run time and you cannot change them. Hence the compiler complaining about cards being not an l-value.
But you can definitely specify what those addresses can hold by using a pointer like below:
// in my class...
Card cards[20];
Card *cardsPointer = cards;// Pointer contains the address of the
//1st element of 'cards' array.
// in method...
for(int i = 0; i < 20; i++)
*(cardsPointer++) = Card(i, /*i as char +*/ "_Card.bmp");// Note that
// there is no 'new' operator as 'cardsPointer' has type 'Card *' and
// not 'Card **'. And 'cardsPointer' has type 'Card *' as the array is
// of type 'Card'.