Creating a 2D array on construction with a variable length - c++

How can a setup a class that I can have a private 2D array that's size is determined by the variables passed in via the constructor?
I've tried this:
class World {
private:
const int WIDTH;
const int HEIGHT;
bool map[][];
public:
World(const int MAX_X, const int MAX_Y) : WIDTH(MAX_X), HEIGHT(MAX_Y) {
map = new bool[WIDTH][HEIGHT];
}
};
But I get a bunch of errors about how declaration of ‘map’ as multidimensional array must have bounds for all dimensions except the first and array size in operator new must be constant even though it is.
I've also tried it this way but it didn't work either:
class World {
private:
const int WIDTH;
const int HEIGHT;
bool map[WIDTH][HEIGHT];
public:
World(const int MAX_X, const int MAX_Y) : WIDTH(MAX_X), HEIGHT(MAX_Y) {
//map = new bool[WIDTH][HEIGHT];
}
};
I get a invalid use of non-static data member ‘World::WIDTH’ on the const int WIDTH line and a quite useless error: from this location on the map declaration line.
What am I doing wrong?
Edit:
I'd prefer to avoid using vectors since I haven't "learned" them yet in this class. (by learned I mean I know how to use them but the professor hasn't discussed them and doesn't like us using outside knowledge)

You could use a vector.
class World
{
typedef std::vector<bool> Tiles;
typedef std::vector<Tiles> WorldMap;
World(unsigned int width, unsigned int height)
{
for (unsigned int i = 0; i < width; i++)
{
m_map.push_back(Tiles(height));
}
}
private:
WorldMap m_map;
};
Or you could use templates, if you know the world size at compile time.
template <unsigned int Width, unsigned int Height>
class World
{
private:
bool m_map[Width][Height];
};
Or you could use raw pointers, since a 2d array is really just an array of pointers to arrays.
class World
{
// Make sure you free this memory.
World(unsigned int width, unsigned int height)
{
m_map = new bool*[width];
for(unsigned int i = 0; i < height; ++i)
{
m_map[i] = new bool[width];
}
}
private:
bool** m_map;
};
I suggest using one of the first two options.

Arrays must have their sizes determined at compile-time, not run time.
You should choose a different container if you want run-time sizing. Possibly:
- std::vector<bool> // and then simulate a 2d array using indexing
- std::vector<std::vector<bool>> // if you insist on using [][] syntax

Related

Changing multi_array_ref data block after instantiation

I have been experimenting with boost multi_array_refs because of their ability to map a 2D (In my case) array view of the world onto arbitrary blocks of contiguous memory. With a multi_array_ref, a pointer to the contiguous memory is specified to the constructor. That works fine, but in my ultimate application, what I would really like to do is take a pre-existing multi_array_ref object, and point it to new buffers that are allocated later, on the fly. It seems like this should be possible, but I can't seem to figure out how to do it. Here's some skeleton code that I hope demonstrates the type of thing I'm attempting to do, although it obviously won't work as written.
const int XSIZE = 10;
const int YSIZE = 5;
typedef boost::multi_array_ref<int, 2> ARRAY_2D_REF;
class Test2D {
public:
Test2D(const int sizeX, const int sizeY);
~Test2D();
// I want to point this multi_array_ref to a buffer that gets
// allocated in the constructor.
ARRAY_2D_REF data;
private:
int xSize;
int ySize;
int *n;
};
// can't construct 'data' using ':' syntax here, because, the 'n'
// buffer has not been allocated yet so 'n' doesn't contain a valid
// address. This compiles okay, but segfaults if you try to use 'data'
// because 'n' contains garabge at this point.
Test2D::Test2D(const int sizeX, const int sizeY) : data(n, boost::extents[sizeX][sizeY]) // <<-- fail
{
xSize = sizeX;
ySize = sizeY;
// In the actual application, n will be populated by data arriving
// on TCP stream. The header on those mesasges contain total
// contiguous buffer size and X,Y dimensions, followed by the data.
n = (int*)malloc(xSize * ySize * sizeof(int));
// I want to set the multi_array_ref origin and define extents right
// here. The thought was to set the origin to 'n', and then
// resize(), but how? Of course I can't actually construct it here
// as shown. Seems like there should be a simple way to set
// (change) the origin pointer. There probably is in fact. But I
// can't seem to figure it out.
data(n, boost::extents[xSize][ySize]);
}
Initializers in the initializer list are evaluated in order of declaration of class members (members after base-classes).
So, you fix it all by moving the data member declaration after the other members.
#include <boost/multi_array.hpp>
static const int XSIZE = 10;
static const int YSIZE = 5;
typedef boost::multi_array_ref<int, 2> ARRAY_2D_REF;
class Test2D {
public:
Test2D(const int sizeX, const int sizeY)
: xSize(sizeX),
ySize(sizeY),
n (new int[xSize*ySize]),
data(n, boost::extents[xSize][ySize])
{ }
~Test2D() {
delete n;
}
Test2D(Test2D const&) = delete; // Rule Of Three!
private:
int xSize;
int ySize;
int *n;
public:
ARRAY_2D_REF data;
};
int main() {
Test2D wrapped(XSIZE, YSIZE);
}
Note also that
there is no place for malloc in C++
you should guard the type against copying, because otherwise you will do double-delete of the data buffer.
Other Thoughts
It really doesn't make any sense to be allocating the buffer from inside the constructor (body, or initializer-list regardless), if your comment says:
// In the actual application, n will be populated by data arriving
// on TCP stream. The header on those mesasges contain total
// contiguous buffer size and X,Y dimensions, followed by the data.
The constructor doesn't have the dependencies necessary to do the allocation, nor SHOULD it. There are two options:
if the buffer is always owned by the Test2D instance (and the data is going to be copied from the IO buffer(s)), then just use multi_array, not multi_array_ref. It does the allocations, safely, for you.
otherwise, pass in the buffer:
#include <boost/multi_array.hpp>
#include <memory>
static const int XSIZE = 10;
static const int YSIZE = 5;
static const int HEADER_SIZE = 16;
typedef boost::const_multi_array_ref<int, 2> ArrayCRef;
class Test2D {
public:
Test2D(int const* raw, int sizeX, int sizeY) : data(raw, boost::extents[sizeX][sizeY])
{ }
ArrayCRef::size_type xSize() const { return data.shape()[0]; }
ArrayCRef::size_type ySize() const { return data.shape()[1]; }
public:
ArrayCRef data;
};
int main() {
auto io_buf = std::make_unique<char[]>(XSIZE*YSIZE*sizeof(int)+HEADER_SIZE);
// TODO parse XSIZE/YSIZE from io_buf
auto raw = reinterpret_cast<int const*>(io_buf.get() + HEADER_SIZE);
Test2D wrapped(raw, XSIZE, YSIZE);
}

C++ private class dynamic n-dimensional array

I've been looking all around StackOverlow for an answer but i didn't find one so i hope it isn't duplication of any post around here.
so, i have the next problem.
lets say i have the next 2 classes: Rectangle (Which is built from another class but it doesn't concern us currently)
and Grid.
with they following constructors:
(Point Constructor for Rectangle private topLeft and bottomRight):
Point::Point(int x, int y) {this->x = x; this->y = y;}
(Rectangle Constructor and Class)
class Rectangle
{
public:
Rectangle(int l, int u, int w, int h, int color);
//int getColor() const;
//void setColor(int color);
//bool contains(const Point &p) const;
//void print() const;
private:
const Point topLeft, bottomRight;
int color;
};
Rectangle::Rectangle(int l, int u, int w, int h, int color) :
topLeft(l, u),
bottomRight(l + w, u + h)
{ this->color = color; }
(Grid Constructor and Class) (Lets assume I don't want to initialize the values of Rectangle in Grid just allocate them in memory)
class Grid
{
public:
Grid(int tileW, int tileH, int width, int height, int color);
//~Grid();
//Rectangle& getRectAt(const Point &p);
//void print() const;
private:
int count;
Rectangle **recs;
};
Grid::Grid(int tileW, int tileH, int width, int height, int color)
{
int index, index_c=0;
recs = new Rectangle *[width];
for (int index = 0; index < width; index++)
{
recs[index] = new Rectangle[index];
}
}
so, as you can understand i have problem in Grid constructor with the following Error
(Error 1 error C2512: 'Rectangle' : no appropriate default constructor available.)
but i just cant understand why it wont work, I've been suggested to allocate the Recs double pointer as 1 dimensional array (Array with the length of Width*Height) but what if Recs was 4 dimensional array ? How could you flat it properly and then index around the 4-dimensional array without having a headache calculating the index of each cell in the array.
another thing, we know that if it was int** and not recs** it would work perfectly
int **foo;
int height,width;
foo = new int* (height);
for (int index = 0; index<height; ++index)
foo[index] = new int[width];
so i just keep missing the way of doing n-dimensional arrays in C++.
The line recs[index] = new Rectangle[index]; tries to call Rectangle's default constructor index times. If you want to create multiple objects at once, you probably want to add default ctor and simple setter method to your Rectangle class
class Rectangle
{
public:
Rectangle(int l, int u, int w, int h, int color);
Rectangle() = default;
void set(int l, int u, int w, int h, int color);
private:
const Point topLeft, bottomRight;
int color;
};
Then, in creation loop:
for (int index_w = 0; index_w < width; index_w++)
{
recs[index_w] = new Rectangle[height]; //see note below
for (int index_h = 0; index_h < height; index_h++)
recs[index_w][index_h].set(/* some math with width, height and loop variables*/, color);
}
Note: I have changed index to height, because you want to create 2D array, so the total grid size is height * width. With index length in creation, you would create triangle-shaped grid instead (and more over, the first loop iteration would be recs[0] = new Rectangle[0] - a zero-length array).
As user Macro A mentioned, consider using std::vector<Rectangle> instead of raw pointers (2D array would be std::vector<std::vector<Rectangle>>)
Also, consider changing your design, because currently you are creating a grid H x W of Rectangle objects, where all points (except first/last) are duplicated across adjacent rectangles (each point is upper-left corner of one rectangle, upper-right corner of another, bottom-left...).
I propose a Grid class that holds 2D array of ints and has a method Rectangle getRectangle(int x, int y) which would return appropriate set of 2 points. Modyfying such Grid class would be much easier, and you wouldn't have to iterate over all Rectangles, just ints
You could use a placement new : you initially reserve enough place to store the array of objects, then individually construct them each in its own place.
In your code, it could become (more or less):
for (int index = 0; index < width; index++)
{
// first simple allocation for the array
recs[index] = (Rectangle *) malloc(sizeof(Rectangle) * height);
for (int j=0; j<height; j++) {
// individually build each rectangle in place
new(&recs[index][j]) Rectangle(index*tileW, j*tileH, tileW, tileH, color);
}
}
This is intended to do exactly what you need : build arrays of non default constructible objects.
Unrelated: as you use raw pointers and allocated arrays, do not forget to correctly free everything. Using std::vectors could save you from that...
You get error C2512 because you don't have a default constructor in Rectangle. The only constructor you have in Rectangle is parameterized, while a default constructor has the requirement that it must be callable without additional arguments provided.
Looking at the line recs[index] = new Rectangle[index];, you see there are no arguments for the 5 parameters the constructor accepts. For that line to compile, you need to create a default constructor either with a new constructor with the signature Rectangle() or default arguments for the parameters in your parameterized constructor, for example Rectangle(int l = 0, int u = 0, int w = 0, int h = 0, int color = 0);.
If you want to allocate the memory, you should create the default constructor and use std::vector<Rectangle> which you initialize with a size. Then you can later replace the objects by use of a copy-constructor, as shown in this example: http://ideone.com/KnUBPQ
As for creating an n-dimensional array, you've explicitly limited it to a two-dimensional array with your Rectangle and Point classes. If the array is going to be n-dimensional, your points need to be n-dimensional too:
template<int dimensions>
class Point
{
std::array<int, dimensions> coords;
...
};
or
class Point
{
std::vector<int> coords;
...
};

Passing a Vector of a Vector with variables to a function

I was trying to create an array with variables for the size(ie array[x][y]), which won't work. I stumbled upon a post that suggested using a vector of a vector:
vector<vector<int> > grid(GetGridXComponent(), vector<int>(GetGridYComponent()));
The GetGridXComponent() and GetGridYComponent() retrieves private variables from a class.
The code works inside the function, but I need to be able to access the vector, "grid", outside of the class. To do this, I tried to create a public instance:
vector<vector<int> > grid(GetGridXComponent(), vector<int>(GetGridYComponent()));
But of course, GetGridXComponent() and GetGridYComponent() won't work because it thinks that I'm creating a function and wants me to declare a type for GetGridXComponent().
Is there a way of going about this? Am I making it harder than it needs to be?
Thanks in advance.
Class Simulator
{
private:
int s_iGridXComponent;
int s_iGridYComponent;
public:
Simulator();
~Simulator();
int GetGridXComponent();
int GetGridYComponent();
void Function(vector<vector<int> >&);
vector<vector<int> > pelletGrid(GetGridXComponent(), vector<int>(GetGridYComponent()));
}
void Simulator::Function(vector<vector<int> > &grid)
{
code;
}
Assuming you are just trying to create a class member named pelletGrid that is a 2D vector it should be just something like:
class Simulator
{
private:
int s_iGridXComponent;
int s_iGridYComponent;
std::vector<std::vector<int>> pelletGrid;
public:
Simulator(const int x, const int y) :
s_iGridXComponent(x),
s_iGridYComponent(y),
pelletGrid(x, std::vector<int>(y))
{ }
void CreateGrid (const int x, const int y)
{
s_iGridXComponent = x;
s_iGridYComponent = y;
pelletGrid = std::vector<std::vector<int>>(x, std::vector<int>(y));
}
};
You can use either the constructor or the custom method in order to initialize the 2D vector depending on when you know the X/Y dimensions.

beginner question about arrays in structs in C++

I would like to create a struct and use it inside an other struct as an array. My problem is that I don't know how big array I would like to allocate, I will only know once I am in a function. I mean I would like to use [] instead of a pre-determined constant, like 10000.
I think if you look at my code it would be self-explanatory. Can you help me how to make this code work? Moreover it would help me a lot if you could tell me what is the name of the topic I am asking about (is it dynamic arrays?) and that where can I find articles/tutorials about this topic.
Here is the code with my broken way of thinking about arrays in structs.
#include <iostream>
using namespace std;
struct keyframe {
bool a;
int b;
int c;
};
struct keyframe_file {
const int num_views;
const int num_keyframes;
keyframe keyframes[];
};
int main() {
keyframe_file my_file;
my_file.num_views = 1;
my_file.num_keyframes = 6;
my_file.keyframes = new keyframe[my_file.num_keyframes];
my_file.keyframes[0].a = true;
my_file.keyframes[0].b = 5;
my_file.keyframes[0].c = 9;
return 0;
}
Use a std::vector.
struct keyframe_file {
const int num_views;
const int num_keyframes;
std::vector<keyframe> keyframes;
};
int main() {
keyframe_file frame;
frame.keyframes.resize(...);
}
If it suits your purpose, an STL container (std::vector) is easily one of the best options - the less memory management you have to worry about, the better.
In any case, look at the struct definition Nawaz posted above - that's exactly how it should be. Dynamic arrays in C++ are simply pointers. You have, however, allocated the memory properly in your code, but you haven't freed it (so it's leaking). Since you allocated with new [] you will need to
delete [] my_file.keyframes;
in order to free the memory properly.
Resizing is another issue: with a smart implementation, array resizing can be an amortized O(1) operation which is nice. When you resize, it will always take you O(n) since you need to copy all the elements into a new array of different size, but if you do it half as much, it becomes O(1). That is, double the array each time you need to resize. Here is a very quick example
void resize()
{
if(numOfElementsInArray == sizeOfArray)
{
ArrayType * arr = new ArrayType[sizeOfArray*2]; // Allocate a double size array
for(int i=0;i<sizeOfArray;++i)
currentArray[i] = arr[i];
delete [] currentArray; // Free memory in old array
currentArray = arr; // Set the array to our new one
sizeOfArray *= 2; // Double the size
}
}
NOTE: The example above does not take into account space complexity; that said, if you have 5000 elements, and remove all but 5, this method with not shrink it (which is probably what you will want to do for all practical purposes)
Your code appears to be almost correct, except for two things:
keyframes needs to be a keyframe* rather than a keyframe[]
You forgot to delete the memory you allocated
That is incomplete type. In C++, array must be provided with size, and the size must be known at compile time itself.
You're using new, with which you should be using pointer.
struct keyframe_file {
const int num_views;
const int num_keyframes;
keyframe *keyframes;
};
But std::vector<keyframe> is still a better choice, as #DeadMG already suggested.
By the way, the first two members are const in the struct, that means, they cannot be assigned value, as you're doing in your code. They must be initialized with values you want them to hold. That implies, now with vector, you've to include a constructor, to initialize the struct, as the struct is no more a POD.
struct keyframe_file {
const int num_views; //const member
const int num_keyframes; //const member
std::vector<keyframe> keyframes;
keyframe_file(int nviews, int nkeyframes)
: num_views(nviews), num_keyframes(nkeyframes), keyframes(nkeyframes){}
};
keyframe_file my_file(1,6); //done!
The suggested "Vector" is they safest way to do it.
But if it is only about making your code work (without resizing and stuff) the following should be working:
#include <iostream>
using namespace std;
struct keyframe {
bool a;
int b;
int c;
};
struct keyframe_file {
const int num_views;
const int num_keyframes;
keyframe* keyframes;
};
int main()
{
keyframe_file my_file = {1, 6}; // initialization needed bcause of 'const int'
my_file.keyframes = new keyframe[my_file.num_keyframes];
for (int i = 0; i < my_file.num_keyframes; i++)
{
my_file.keyframes[i].a = true;
my_file.keyframes[i].b = 5 + i;
my_file.keyframes[i].c = 9 - i;
}
return 0;
}
somewhere in your code, when you are done using the array you have to call delete [] my_file.keyframes; as already mentioned.
There's a basic rule when using dynamic arrays in c++, especially when using it inside structs or classes, and it's to delete what you no longer need.
If you want to make your struct dynamic, it's easy, just replace the [] with * and the array will become dynamic, but it's not over yet, there is a lot of work.
You have to construct the array and destory it, and destoroying it is possible and useful noly with destructors, like this:
struct keyframe_file
{
const int num_views;
const int num_keyframes;
keyframe* keyframes;
~keyframe_file() // this is the destructor
{
delete[] keyframes;
}
};
Yet even that code isn't going to work at all, since you are assigning values to constants in variable my_file after creating it, it's illegal in c++, you should then use classes instead.
Using classes with dynamic arrays is very easy and interesting and makes your code very good, you don't have to know too much to do that, just learn what is a constructor, an initializer, destructor, private and public and go on with the following code:
#include <iostream>
using namespace std;
struct keyframe
{
bool a;
int b,c;
};
class keyframe_file
{
public:
keyframe_file(int NV, int NKF):num_keyframes(NKF),num_views(NV)
{
keyframes = new keyframe[num_keyframes];
}
~keyframe_file()
{
delete[] keyframes;
}
private:
const int num_views;
const int num_keyframes;
keyframe* keyframes;
};
int main()
{
keyframe_file my_file(1,6);
return 0;
}
This code works very well, it allows you to assign value to the constants num_views and num_keyframes for one time when creating the object (variable) my_file.
Remember, you are a C++ programmer, be proud of that, and use classes instead of structs and dynamic arrays instead of static ones.
Hope that's useful.
Use pointers and apply to your structure!
int *p;
p = new int;
#include <iostream>
using namespace std;
struct keyframe {
bool a;
int b;
int c;
};
struct keyframe_file {
const int num_views;
const int num_keyframes;
keyframe *keyframes;
};
int main() {
keyframe_file my_file;
my_file.num_views = 1;
my_file.num_keyframes = 6;
for (int i = 0; i < my_file.num_keyframes; i++){
my_file.keyframes = new keyframe; //<---
}
my_file.keyframes[0].a = true;
my_file.keyframes[0].b = 5;
my_file.keyframes[0].c = 9;
return 0;
}

C++ Array Member of Constant Length (Initialisation of)

I have a class that contains an array. I want this array to be set at the length of a constant:
// Entities.h
class Entities
{
private:
const int maxLimit;
int objects[maxLimit];
int currentUsage;
public:
Entities();
bool addObject(int identifier);
void showStructure();
};
The main problem I'm having is with the constructor. I thought:
// Entities.cpp
Entities::Entities() : maxLimit(50)
{
currentUsage = 0;
cout << "Entities constructed with max of 50" << endl;
}
would have been sufficient...but not so. I don't know if I can use the initialiser list for array initialisation.
How can I initialise the objects array using the maxLimit const? I'm relatively new to classes in C++ but I have experience with Java. I'm mainly testing out this phenomenon of 'constness'.
The array must have a constant length. I mean a length that is the same for all objects of that class. That is because the compiler has to know the size of each object, and it must be the same for all objects of that particular class. So, the following would do it:
class Entities
{
private:
static const int maxLimit = 50;
int objects[maxLimit];
int currentUsage;
public:
Entities();
bool addObject(int identifier);
void showStructure();
};
And in the cpp file:
const int Entities::maxLimit;
I prefer to use an enumeration for that, because i won't have to define the static in the cpp file then:
class Entities
{
private:
enum { maxLimit = 50 };
int objects[maxLimit];
int currentUsage;
public:
Entities();
bool addObject(int identifier);
void showStructure();
};
If you want to have a per-object size of the array, then you can use a dynamic array. vector is such one:
class Entities
{
private:
const int maxLimit;
std::vector<int> objects;
int currentUsage;
public:
Entities();
bool addObject(int identifier);
void showStructure();
};
// Entities.cpp
Entities::Entities(int limit)
: maxLimit(limit), objects(limit), currentUsage(0)
{
cout << "Entities constructed with max of 50" << endl;
}
Best is to do as much initialization in the initialization list as possible.
You can use template argument if you need to set array size at compile time:
template<size_t maxLimit>
class Entities
{
int objects[maxLimit];
public:
Entities() {}
...
};
Entities<1000> inst;
to dynamically allocate the memory you may need to use the 'new' keyword like
objects would be defined like:
int * objects;
inside the constructor you would do:
objects = new int [maxLimit];
edit:
forgot to mention, you'll need to deallocate the array when you're done, probably in the destructor of the class.
delete[] objects;
const ints have to be initialized at declaration. If you don't know the value that it has to be at the time of declaration, you're going to have to adopt a different strategy.
You'll need to create the array in the constructor, while keeping a pointer outside. Is this what you want to do?
In your class :
private:
int maxLimit;
int* objects;
And outside:
Entities::Entities() : maxLimit(50)
{
currentUsage = 0;
cout << "Entities constructed with max of 50" << endl;
objects = new int[maxLimit];
}
Entities::~Entities()
{
delete [] objects;
}
If all objects have the same length, then length can be static. This makes it a constant integral expression allowed as an array bound:
class Entities
{
private:
static const int maxLimit = 50;
int objects[maxLimit];
int currentUsage;
//...
};
Remember that sizeof(Entities) is a valid expression. Each Entities object has that same size.
Use std::vector and you get the expected behaviour. No need to worry about pointers, copies, etc
#include <vector>
class Entities
{
private:
const int limit;
std::vector<int> objects;
public:
Entities(int a_limit)
: limit(a_limit), objects(a_limit)
{ }
void addObject(int identifier)
{
if (objects.size() == limit)
throw whatever;
objects.push_back(identifier);
}
};