Efficient / elegant two dimensional container - c++

Need for two dimensional array of objects that could be something like:
myContainer<myObject*> *a = new myArray<myObject*>( 20, 20 ); // passing int's as width & height
later accessing values would be done with methods such as:
mylist<myObject*> getRow( int );
mylist<myObject*> getColumn( int );
mylist<myObject*> getDiagonalRow( int );
implementations of those could be something like:
myList<myObject*> myContainer::getRow( int a ){
if( a < 0 && a>=this->height )
return;
myList<myObject*> hlp;
for( int i=0; i<this->width; i++)
hlp.append( this->arr[a][i] );
return hlp; // returns a copy. Could also be a pointer if created with new.
}
Other methods could follow similar lines, ie. creating a list object and filling it with what was requested.
My questions: Can anyone think of elegant way to create a container class I'm describing here. Which could for example avoid creating and filling of list-objects but still maintaining the abstraction and/or usability. Or please advice if I have missed something in STL etc. that has something like this.

STL has the valarray container which can be viewed as row and columns using slices, but you have to do it manually or wrap it into a wrapper class. Also the slices represents the values of the valarray (they are not a copy), but it is designed to be used with numbers and to be a bit optimized, it doesn't have any iterator and cannot be grown. it's doesn't follow the usual STL container concept. but it can still be used as a quick and dirty workaround if you can't use boost.
std::valarray<float> array(16);
// we can view it as a 4x4 matrix.
// this represents the first line
array[std::slice(0,4,1)];
// and the second column
array[std::slice(1,4,4)];
// you cannot use the sliced array directly. they don't
// have operator[], but they have operator=(valarray), and
// valarray has a constructor that takes sliced arrays as input.

What is the speed you are aiming for?
I can image to get a getRow() and getColumn() and getDiagonal()
in constant time when you handle with something list-like in the background.e.g. vector.
(if you just return a pointer)
Should the container grow or is the size fixed after the initialization?
If your container size is m and n you can store m std::vectors of size n,
n std::vectors of size m, and m+n std::vectors of different sizes.
Describing the rows, coloumns, and diagonals.
Then the get methods are easy to implement and in a at() or [] method/operator
three entries are to be changed.

Related

Accessing methods of objects held in multidimensional vectors of unique_ptr's

I have a 2 dimensional structure of objects initialized as thus:
std::vector<std::shared_ptr<tile> > appearance;
for (int x = 0; x < building_data.x_width; x++)
{
appearance.push_back
(std::shared_ptr<tile>(new tile[building_data.y_length]));
}
now, as far as I can figure out, the only way to access a member function of a tile in this is to use
appearance.at(x).get()[y].member_function()
which is confusing and cumbersome, and I feel like I'm missing something.
Previously, I had used tile** for the same structure, and the syntax of
tile[x][y] was nice but the raw pointers were a headache.
So, is there a better way access functions of an object held in an array, where the first element in the array is pointed to by a smart pointer held in a vector? Wordy but its the best I have.
You can use the -> operator to access members of the object managed by the shared_ptr. It's the same syntax you use with raw pointers.
However, you're going to run into problems with delete as mentioned in Dantez's answer.
Also, it looks like you're building some sort of board of tiles, perhaps for a game? Have you considered replacing the multidimensional array with with a 1D vector and some accessor functions?
// board_width and height should be integers
std::vector<Tile> board;
board.reserve(board_width * board_height);
for (unsigned y_axis = 0; y_axis < board_height; ++y_axis)
{
for (unsigned x_axis = 0; x_axis < board_width; ++x_axis)
{
board.push_back(Tile());
}
}
...
vec2 index_to_coords(unsigned index)
{
return vec2(index % board_width, index / board_width);
}
...
unsigned coords_to_index(const vec2& coords)
{
return (static_cast<unsigned>(coords.y) * board_width) + static_cast<unsigned>(coords.x);
}
First of all, shared_ptr is not designed to work with arrays. When there is no more references, it calls delete instead of delete[] which results in undefined behaviour if managed object is an array. You can read about it here.
As for accessing shared_ptr object, you can use operator* to dereference it.
Also, if you know vector's final size, you may want to reserve some space to avoid reallocation.
I agree with Fibbles, but have an alternate idea to provide. Fibble's approach is actually quite common even in C, because multidimensional structures (matrices) are just much easier that way.
However, if you do insist on the two dimensional concept, you can nest vectors. Consider:
typedef std::vector< tile > TileRow;
typedef std::vector< TileRow > Tiles;
At first, this may be a bit confusing, so to be clear that creates:
std::vector< std::vector< tile > > t;
However, with the typedef, thats
Tiles t;
Now, that's empty. To use it you'd need to push in some rows, and for each row push in some columns. You might not like that, so...you can use the assign function to set some rows. If, for example, you needed a matrix of 10 rows by 10 columns, you might
t.assign( 10, TileRow( 10, tile() ) );
This assume tile has a default constructor, pushing 10 rows of TileRow, each with 10 columns of default constructed tiles.
Now, t[ 1 ] returns a reference to the row 1. As such, t[ 1 ][ 1 ] is a reference to the tile at location 1,1, much like an array.
Yet, now you have no allocation/deallocation issues.
Something similar can be done with std::array, even better.
typedef std::array< tile, 10 > TileRow;
typedef std::array< TileRow, 10 > Tiles;
Tiles t;
At which point, t is ready with default initialized tiles.

Adding element to Array of Objects in C++

How do I add an element to the end of an array dynamically in C++?
I'm accustomed to using vectors to dynamically add an element. However, vectors does not seem to want to handle an array of objects.
So, my main goal is having an array of objects and then being able to add an element to the end of the array to take another object.
EDIT**
Sorry, its the pushback() that causes me the problems.
class classex
{
private:
int i;
public:
classex() { }
void exmethod()
{
cin >> i;
}
};
void main()
{
vector <classex> vectorarray;
cout << vectorarray.size();
cout << vectorarray.push_back();
}
Now I know push_back must have an argument, but What argument?
Now I know push_back must have an argument, but What argument?
The argument is the thing that you want to append to the vector. What could be simpler or more expected?
BTW, you really, really, really do not want exmethod as an actual method of classex in 99% of cases. That's not how classes work. Gathering the information to create an instance is not part of the class's job. The class just creates the instance from that information.
Arrays are fixed sized containers. So enlarging them is not possible. You work around this and copy one array in a bigger and gain space behind the old end, but that's it.
You can create a array larger than you currently need it and remember which elements are empty. Of course they are never empty (they at least contain 0's), but that's a different story.
Like arrays, there are many containers, some are able to grow, like the stl containers: lists, vectors, deques, sets and so on.
add a Constructor to set i (just to give your example a real world touch) to your example classex, like this:
class classex {
public:
classex(int& v) : i(v) {}
private:
int i;
};
An example for a growing container looks like this:
vector <classex> c; // c for container
// c is empty now. c.size() == 0
c.push_back(classex(1));
c.push_back(classex(2));
c.push_back(classex(3));
// c.size() == 3
EDIT: The question was how to add an element to an array dynamically allocated, but the OP actually mean std::vector. Below the separator is my original answer.
std::vector<int> v;
v.push_back( 5 ); // 5 is added to the back of v.
You could always use C's realloc and free. EDIT: (Assuming your objects are PODs.)
When compared to the requirement of manually allocating, copying, and reallocating using new and delete, it's a wonder Stroustrup didn't add a keyword like renew.

What's the proper way to declare and initialize a (large) two dimensional object array in c++?

I need to create a large two dimensional array of objects. I've read some related questions on this site and others regarding multi_array, matrix, vector, etc, but haven't been able to put it together. If you recommend using one of those, please go ahead and translate the code below.
Some considerations:
The array is somewhat large (1300 x 1372).
I might be working with more than one of these at a time.
I'll have to pass it to a function at some point.
Speed is a large factor.
The two approaches that I thought of were:
Pixel pixelArray[1300][1372];
for(int i=0; i<1300; i++) {
for(int j=0; j<1372; j++) {
pixelArray[i][j].setOn(true);
...
}
}
and
Pixel* pixelArray[1300][1372];
for(int i=0; i<1300; i++) {
for(int j=0; j<1372; j++) {
pixelArray[i][j] = new Pixel();
pixelArray[i][j]->setOn(true);
...
}
}
What's the right approach/syntax here?
Edit:
Several answers have assumed Pixel is small - I left out details about Pixel for convenience, but it's not small/trivial. It has ~20 data members and ~16 member functions.
Your first approach allocates everything on stack, which is otherwise fine, but leads to stack overflow when you try to allocate too much stack. The limit is usually around 8 megabytes on modern OSes, so that allocating arrays of 1300 * 1372 elements on stack is not an option.
Your second approach allocates 1300 * 1372 elements on heap, which is a tremendous load for the allocator, which holds multiple linked lists to chunks of allocted and free memory. Also a bad idea, especially since Pixel seems to be rather small.
What I would do is this:
Pixel* pixelArray = new Pixel[1300 * 1372];
for(int i=0; i<1300; i++) {
for(int j=0; j<1372; j++) {
pixelArray[i * 1372 + j].setOn(true);
...
}
}
This way you allocate one large chunk of memory on heap. Stack is happy and so is the heap allocator.
If you want to pass it to a function, I'd vote against using simple arrays. Consider:
void doWork(Pixel array[][]);
This does not contain any size information. You could pass the size info via separate arguments, but I'd rather use something like std::vector<Pixel>. Of course, this requires that you define an addressing convention (row-major or column-major).
An alternative is std::vector<std::vector<Pixel> >, where each level of vectors is one array dimension. Advantage: The double subscript like in pixelArray[x][y] works, but the creation of such a structure is tedious, copying is more expensive because it happens per contained vector instance instead of with a simple memcpy, and the vectors contained in the top-level vector must not necessarily have the same size.
These are basically your options using the Standard Library. The right solution would be something like std::vector with two dimensions. Numerical libraries and image manipulation libraries come to mind, but matrix and image classes are most likely limited to primitive data types in their elements.
EDIT: Forgot to make it clear that everything above is only arguments. In the end, your personal taste and the context will have to be taken into account. If you're on your own in the project, vector plus defined and documented addressing convention should be good enough. But if you're in a team, and it's likely that someone will disregard the documented convention, the cascaded vector-in-vector structure is probably better because the tedious parts can be implemented by helper functions.
I'm not sure how complicated your Pixel data type is, but maybe something like this will work for you?:
std::fill(array, array+100, 42); // sets every value in the array to 42
Reference:
Initialization of a normal array with one default value
Check out Boost's Generic Image Library.
gray8_image_t pixelArray;
pixelArray.recreate(1300,1372);
for(gray8_image_t::iterator pIt = pixelArray.begin(); pIt != pixelArray.end(); pIt++) {
*pIt = 1;
}
My personal peference would be to use std::vector
typedef std::vector<Pixel> PixelRow;
typedef std::vector<PixelRow> PixelMatrix;
PixelMatrix pixelArray(1300, PixelRow(1372, Pixel(true)));
// ^^^^ ^^^^ ^^^^^^^^^^^
// Size 1 Size 2 default Value
While I wouldn't necessarily make this a struct, this demonstrates how I would approach storing and accessing the data. If Pixel is rather large, you may want to use a std::deque instead.
struct Pixel2D {
Pixel2D (size_t rsz_, size_t csz_) : data(rsz_*csz_), rsz(rsz_), csz(csz_) {
for (size_t r = 0; r < rsz; r++)
for (size_t c = 0; c < csz; c++)
at(r, c).setOn(true);
}
Pixel &at(size_t row, size_t col) {return data.at(row*csz+col);}
std::vector<Pixel> data;
size_t rsz;
size_t csz;
};

How to Implement a multidimensional array if the dimension is unknown at compile-time?

I want to implement a function that gets as a parameter a dimension "n" of an array of integers. This function also gets values "k_1, k_2, ..., k_n" defining the size of the array. Then this function will fill this n-dimensional array.
How do I implement this efficiently with C++?
For example for n = 3 I would use
vector < vector < vector < int > > > array;
But I don't know the dimension at compile time.
Use a one-dimensional array, and fake the other dimensions using multiplication of offsets for indexing, and you can pass the dimension sizes in by vector, i.e.
std::vector<int> create_md_array(const std::vector<int> & dimensions)
{
int size = std::accumulate(dimensions.begin(), dimensions.end(), 1, std::multiplies<int>());
return std::vector<int>(size);
}
You have a couple of choices. You can implement it yourself, basically multiplying the coordinates by the sizes to linearize the multi-dimensional address, and just have a simple std::vector<whatever> to hold the data.
Alternatively, you could use std::valarray and friends to accomplish the same. It has a set of classes that are specifically intended for the kind of situation you describe -- but they're used so rarely that almost nobody understands them. Writing the code yourself stands a good chance of being easier for most people to read and understand.

pointer arithmetic on vectors in c++

i have a std::vector, namely
vector<vector<vector> > > mdata;
i want pass data from my mdata vector to the GSL function
gsl_spline_init(gsl_spline * spline, const double xa[], const double ya[], size_t size);
as ya. i already figured out that i can do things like
gsl_spline_init(spline, &(mgrid.front()), &(mdata[i][j][k].front()), mgrid.size());
this is fine if i want to pass the data from mdata for fixed i,j to gsl_spline_init().
however, now i would need to pass along the first dimension of mdata, so for fixed j,k.
i know that for any two fixed indices, all vectors along the remaining dimensions have the same length, so my vector is a 'regular cube'. so the offset between all the values i need should be the same.
of course i could create a temporary vector
int j = 123;
int k = 321;
vector<double> tmp;
for (int i = 0: i < mdata.size(); i++)
tmp.push_back(mdata[i][j][k]);
gsl_spline_init(spline, &(mgrid.front()), &(tmp.front()), mgrid.size());
but this seems too complicated. perhaps there is a way to achieve my goal with pointer arithmetic?
any help is greatly appreciated :)
You really can't do that without redesigning the array consumer function gsl_spline_init() - it relies on the data passed being a contiguous block of data. This is not the case with you three-level vector - not only it is a cube but also each level has a separate buffer allocated on heap.
This can't be done. Not only with vectors, but even with plain arrays only the last dimension is a contiguous block of data. If gsl_spline_init took an iterator instead of array, you could try to craft some functor to choose appropriate data but I'm not sure it's worth trying. No pointer arithmetic can help you.