Reputation: 2409
I know this has been asked before (or some similar variations) however I cannot get it to work.
I am trying to create a board game that is composed of a board filled with squares. I am trying to model my board as a 2d array of Square objects. The board can be created with any width or height, and those parameters are passed in through the constructor. Here is the code I am working with:
Board.h
#ifndef BOARD_H
#define BOARD_H
#include "Square.h"
class Board{
public:
Board(int w, int h);
private:
Square **squares;
const int width;
const int height;
};
#endif
Board.cpp
#include "Board.h"
Board::Board(int w, int h):width(w), height(h) {
squares = new Square*[width];
for (int i = 0; i < width; i++) {
squares[i] = new Square[height];
}
}
However, when I try to compile this I get an error which seems to indicate the squares[i] = new Square[height]
is trying to call the default constructor for the Square object (which I do not want to exist nor call in this case).
Board.cpp: In constructor ‘Board::Board(int, int)’:
Board.cpp:7:33: error: no matching function for call to ‘Square::Square()’
squares[i] = new Square[height];
Any ideas? Is this possible within C++?
Upvotes: 2
Views: 1425
Reputation: 629
As another answer mentioned, you could create a flat vector and do some simple arithmetic to calculate the index.
int main() {
// dimensions of the board
int width = 100;
int height = 102;
// some coordinates on the board
int x = 10;
int y = 32;
// allocate memory for every element on the board (10200 elements)
int * board = new int[width * height];
// access element (x, y) of the board
int val = board[y*width + x]
// don't forget to delete dynamic memory!
delete [] board;
}
No two distinct coordinates will have the same index.
Upvotes: 1
Reputation: 56557
Your code is equivalent to this:
struct Foo
{
Foo(int){} // no default constructor
// Foo() = default; /* uncomment this and it will work */
};
int main()
{
Foo* pFoo = new Foo[10]; // need a default ctor
delete[] pFoo;
}
The problem is that on the rhs of Foo* pFoo = new Foo[10];
, you are allocating the memory as well as creating 10 Foo
objects. The compiler doesn't know how to do the latter (creating the objects), as you don't provide a default constructor. To make the above code work as is, you need to specify all arguments for the non-default ctor of each object, like:
Foo* pFoo = new Foo[10]{1,2,3,4,5,6,7,8,9,0}; // this will work
The better alternative is to use std::vector
instead. If you wonder why the latter works without the need for objects that don't have a default constructor, it is because it uses the placement new
, and initializes the elements on demand.
Here you can see how to do it with placement new
.
EDIT
The code
Foo* pFoo = new Foo[10]{1,2,3,4,5,6,7,8,9,0};
compiles in gcc, but clang fails to compile it (with -std=c++11
flag). Follow up question here.
Upvotes: 4
Reputation: 275395
You asked it to construct a buffer of n Squares. It tried to do so, and failed, becuase it could not construct them.
If you want memory for n
squares, use new std::aligned_storage_t<sizeof(Square),alignof(Square)>[Count]
. Then use placement new to construct each element in turn.
But that is a dumb idea. It is complex, opaque, and error prone.
In short, Stop managing memory manually.
vector
solves this problem for you.
Create a vector of vector of squares. Or create a flat vector, and do math to find the index. It does the aligned storage bit internally, and uses placement new to construct on demand, so you do not havr to write that dangerous tricky code yourself.
Vectors let you grow them dynamically and efficiently, and replace manually managed arrays nicely. But you need to grow them from the least to the greatest element. If you do not want that, use vectors of unique ptrs to Squares, and allocate as needed (or, std::experimental::optional<Square>
if you use C++17/1z compilers).
Or just use a map from pair<int,int>
to Square
and make it sparse. Note that you'll beed to emplace and not call []
if you want no default ctor for a Square. Again, you can use unique ptrs to Square instead of Squares directly.
There are many, many options.
Also consider having a default Square ctor: regular types are awesome.
Upvotes: 2