C++ private class dynamic n-dimensional array - c++

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;
...
};

Related

How to workaround "redefinition of default parameter" in class constructor

Consider the following example:
class Rectangle{
Rectangle(int x, int y, int width, int height);
Rectangle(int topLeft_x, int topLeft_y, int bottomRight_x, int bottomRight_y);
};
A Rectangle object may be built giving (x,y) coordinates plus width and height or giving top left points pair and bottom right points pair.
While this is correct from an object oriented point of view, this is not from a compiler point of view, returning the error "member function already defined or declared"
While I normally fix this condition easily in case of member function, simply changing the name according to what it does, this is not possible for constructors.
What is the simpler and correct way to workaround this issue keeping both the way to construct the object?
Another possible solution (other than the pair suggested by #VladfromMoscow) is a static method to perform construction. This lets you give them distinct names, since their argument lists are so similar. Thisi is called the Named Constructor Idiom
class Rectangle
{
public:
static Rectangle createRectangle(int x, int y, int width, int height)
{
return Rectangle(x,y,width,height);
}
static Rectangle createRectangleCorners(int x1, int y1, int x2, int y2)
{
return Rectangle(x1,y1,x2-x1, y2-y1);
}
private:
// Doesn't have to be private, but this forces users to use the above
// constructors
Rectangle(int x, int y, int width, int height);
}
You wrote already yourself
top left points pair and bottom right points pair
So what you need is to define class Point and use this type in the constructor declaration.
Otherwise the constructors are declared like
class Rectangle{
Rectangle(int, int, int, int);
Rectangle(int, int, int, int);
};
As you see these declarations do not make sense even if you will write multiline comments.:)
Another approach is to declare the first constructor like
class Rectangle{
Rectangle(int x, int y, unsigned int width, unsigned int height);
Rectangle(int topLeft_x, int topLeft_y, int bottomRight_x, int bottomRight_y);
};
However this approach is unsafe because each integer literal specified as the third or fourth argument must be casted.
Instead of the class Point you could use standard class std::pair. For example
#include <utility>
//...
class Rectangle{
Rectangle(int x, int y, int width, int height);
Rectangle( const std::pair<int, int> &topLeft, const std::pair<int, int> &bottomRight);
};
Another way on how to solve this problem is by using Tag dispatching:
Instead of using methods with different names, give them a new parameter, e.g.,
struct useCorners {};
struct useDimension {};
class Rectangle
{
Rectangle(useCorners, int topLeft, int topRight, int bottomLeft, int bottomRight)
{ ...
}
Rectangle(useDimension, int topLeft, int topRight, int width, int height)
{ ...
}
};

Creating a 2D array on construction with a variable length

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

Initialize pointer to array

I am trying to initialize pointer to struct array in my class constructor but it do not working at all...
class Particles {
private:
struct Particle {
double x, y, z, vx, vy, vz;
};
Particle * parts[];
public:
Particles (int count)
{
parts = new Particle [count]; // < here is problem
}
};
Remove those [] from declaration. It should be
Particle *parts;
Using C++, you can use benefits of std::vector:
class Particles {
// ...
std::vector<Particle> parts;
public:
Particles (int count) : parts(count)
{
}
};
Particle * parts[];
This is an array of pointers. To initialise this, you would need to loop through the array, initialising each of the pointers to point at a dynamically allocated Particle object.
You probably want to just make parts a pointer:
Particle* parts;
The new[] expression returns a pointer to the first element of the array - a Particle* - so the initialisation will work just fine.
Try this:
class Particles {
private:
struct Particle {
double x, y, z, vx, vy, vz;
};
Particle * parts;
public:
Particles (int count)
{
parts = new Particle [count]; // < here is problem
}
};

Constructor in implementation versus header

The constructor should, to my knowledge, be defined in the implementation file but I've only been able to find examples with the class inside one main file instead of split into a .h and .cpp file
All I need to know is if my following code is separated in an acceptable manner..
Entity.h:
using namespace std;
class cEntity {
private:
/*-----------------------------
----------Init Methods---------
-----------------------------*/
int *X, *Y;
int *Height, *Width;
public:
/*-----------------------------
----------Constructor----------
-----------------------------*/
cEntity (int,int, int, int);
/*-----------------------------
----------Destructor-----------
-----------------------------*/
~cEntity ();
/*-----------------------------
----------Set Methods----------
-----------------------------*/
/*Set X,Y Methods*/
void setX(int x){*X=x;};
void setY(int y){*Y=y;};
void setXY(int x, int y){*X=x; *Y=y;};
/*Set Height, Width Methods*/
void setHeight(int x){*Height=x;};
void setWidth(int x){*Width=x;};
void setDimensions(int x, int y){*Height=x; *Width=y;};
/*-----------------------------
----------Get Methods----------
-----------------------------*/
/*Get X,Y Methods*/
int getX(){return *X;};
int getY(){return *Y;};
/*Get Height, Width Methods*/
int getHeight(){return *Height;};
int getWidth(){return *Width;};
};
and Entity.cpp:
#include "Entity.h"
cEntity::cEntity (int x, int y, int height, int width) {
X,Y,Height,Width = new int;
*X = x;
*Y = y;
*Height = height;
*Width = width;
}
cEntity::~cEntity () {
delete X, Y, Height, Width;
}
I would also like to say thanks to everyone for being so helpful, especially on my first question!
cEntity::cEntity (int x, int y, int height, int width) {
is correct
X,Y,Height,Width = new int;
not so much. That sets Width to a new int, but not the rest. You probably intended:
X = new int(x);
Y = new int(y);
Height = new int(height);
Width = new int(width);
Note that this method of construction will not work for objects without assignment/copy, like references. For some objects, it's also slower than constructing them in place. As such, the preferred way to construct is like so:
cEntity::cEntity (int x, int y, int height, int width) {
:X(new int(x))
,Y(new int(y))
,Height(new int(height))
,Width(new int(width))
{}
This is better, but if any exceptions are thrown, you'll have to somehow deallocate the ones that were allocated. Better is to make each of those members a std::unique_ptr<int>, so they'll deallocate themselves and save you many headaches.
Yes, it's OK.
However, there is a problem with your constructor and destructor.
What your code actually does is allocating one int and your destructor deallocates one int also.
Anyway, there is no need to use pointers here.
Somewhat better implementation (if we don't use smart pointers), could be:
[Entity.h]
private:
/*Private fields*/
int X, Y;
int Height, Width;
[Entity.cpp]
cEntity::cEntity (int x, int y, int height, int width) {
X = x;
Y = y;
Height = height;
Width = width;
}
cEntity::~cEntity () {
}
And one more thing. Try to avoid using namespace std; in your header files. If you do, you force those who include your header to use this using statement and it can provoke namespace clashes.
Your separation is fine. The implementations of those functions is wrong, but you've separated them from the declaration suitably. (They don't allocate or free as many objects as you think they do.)
Yes. For the separation at least, that's generally the best way to do it.
As for the actual implementation you have some issues. I am not really sure what you are trying to do with the constructor or if you have the correct data types for the class member variables but something seems off.
Any method defined in the class directly is implicitly inlined, including the constructor.
I.e.
class MyClass
{
public:
MyClass() {};
};
defines an inline constructor, which may (or may not) improve your code performance,
Whereas
class MyClass
{
public:
MyClass();
};
MyClass::MyClass()
{
};
is not inlined, and therefore won't have those benefits. Both options are correct C++ though.
Just my 2 cents.
P.S And yes, when you decide to store pointers inside a class in this manner, you open a Pandora box.

How can I use 3-dimensional data as a class property?

It's been a long time since I worked with C++, but I have a class that uses 3-dimensional data and I can't figure out how I can make this work. I need the sizes of the dimensions to be defined in the constructor. I tried this in the header:
class CImage
{
public:
float values[][][];
...
}
and this in the constructor:
CImage::CImage(int cols, int rows, int depth)
{
values[cols][rows][depth];
}
but this returns the error: "declaration of `values' as multidimensional array must have bounds for all dimensions except the first".
Also using this in the constructor does not work:
values = new float[cols][rows][depth];
I also tried using vector, but without much success. Header:
vector<vector<vector<float> > > values;
Nothing in constructor. No compiler errors, but when I try to set a value:
values[c][r][d] = value;
the program crashes.
It seems so basic, but I just can't figure it out...
The program crashes when accessing that vector because it is empty, i.e. there are no elements at those indexes.
The best way to go about this is to make a linear, one-dimensional, vector (or even an array), and access it with a pair of operator()'s, see C++FAQ Lite for details. Or use boost::multi_array.
For example:
#include <vector>
#include <iostream>
class CImage
{
int X, Y, Z;
std::vector<float> values;
public:
CImage(int cols, int rows, int depth)
: X(cols), Y(rows), Z(depth),
values(cols*rows*depth) {}
float operator()(int x, int y, int z) const
{
return values[Z*Y*x + Z*y + z];
// or you lay it out differently in memory
// if you please, transparent to the user:
// return values[x + X*y + X*Y*z];
}
float& operator()(int x, int y, int z)
{
return values[Z*Y*x + Z*y + z];
// likewise, in a different layout
// return values[x + X*y + X*Y*z];
}
};
int main()
{
CImage ci(3,3,3);
ci(2,2,2) = 7.0;
std::cout << ci(2,2,2) << '\n';
}
Just to poiny out why Cubbi is right, this would be the constructor of the 3d-vector:
vector<vector&ltvector&ltfloat>>> values;
// create vector [dim0][dim1][dim2]
// init value: init
size_t dim0 = 3;
size_t dim1 = 3;
size_t dim2 = 3;
float init = 0.42f;
values = vector<vector<vector<float>>>
(
dim0,
vector<vector<float>>
(
dim1,
vector<float>
(
dim0,
init
)
)
);
Nice, isn't it?
Besides, you cannot declare a float values[][][]; because arrays reside on the stack, so the compiler has to know at compile time what size that array has (exception to this: C99 variable length arrays, but this would'nt be C++). You could declare a float*** values; (using new float[c][r][d]; in the ctor), but this is awful, too.