In my program, I had a std::vector<std::array<float, n_channels>> vecvec, where n_channels was a constant integer known at compile time. In the program, vecvec grows over time.
I now want to lift the constraint that n_channels must be known at compile time, so I changed the definition to std::vector<std::vector<float>> vecvec. n_channels is still a fixed value, which is known before vecvec is constructed (all elements of vecvec have the same length).
However, now my program is suddenly about 2.5x slower.
I assume this is because the memory of vecvec is suddenly fragmented, because it doesn't "know" that every element of vecvec will have the same size.
Is there a way I can have my cake and eat it too?
Do you want to have your cake and eat it too? Implement your own row-resizable 2D array class TODAY!!!
You can write your own 2D array class. By making rows contiguous in memory, you get all the benefits of using std::vector<std::array<...>>, but without the fixed compiletime sizes! To simplify the implementation, you can make it wrap std::vector.
In order to achieve full functionality, we should also create two "helper" classes. One of them represents a row in the array, and the other represents an iterator for that row. When we iterate through the 2D array, we'll be iterating over the rows of the array.
Row class
This is pretty straight-forward. It just contains a beginning and end pointer. The array is stored contiguously, so we don't actually store Rows, but it's still convenient to have them so we have a type to iterate over.
Because the Row class just represents a view of a row in the matrix, the Row class should NOT allocate or delete any memory. Additionally, I made all the member functions of the Row class constant so that operations can be performed on Rows returned directly from the RowIterator.
template<class T>
struct Row {
T* _start;
size_t _size;
// These are const because if we need the elements to be const
// We just make T const
T* begin() const noexcept { return _start; }
T* end() const noexcept { return _start + _size; }
size_t size() const noexcept { return _size; }
T& operator[](size_t index) const noexcept {
return _start[index];
}
// Implicitly convertible to Row<T const>
operator Row<T const>() const noexcept {
return {_start, _size};
}
};
RowIterator class
This one just implements the basic abilities of a random-access iterator. You can move it forward, backward, index into it, add or subtract integers from it, etc. If I subtract 5, for example, it moves back 5 rows.
template<class T>
struct RowIterator {
using value_type = Row<T>;
using element_type = Row<T>;
using reference_type = Row<T>;
using const_reference_type = Row<T>;
// Add other iterator traits as needed
Row<T> current;
void operator++() noexcept {
current._start += current._size;
}
void operator--() noexcept {
current._start -= current._size;
}
RowIterator<T> operator+(intptr_t rows) const noexcept {
return { Row<T>{current._start + rows * current._size, current._size } };
}
RowIterator<T> operator-(intptr_t rows) const noexcept {
return { Row<T>{current._start - rows * current._size, current._size } };
}
RowIterator<T>& operator+=(intptr_t rows) noexcept {
current._start += rows * current._size;
return *this;
}
RowIterator<T>& operator-=(intptr_t rows) noexcept {
current._start -= rows * current._size;
return *this;
}
Row<T> operator*() const noexcept {
return current;
}
bool operator==(RowIterator<T> other) const noexcept {
return current._start == other.current._start && current._size == other.current._size;
}
bool operator!=(RowIterator<T> other) const noexcept {
return current._start != other.current._start || current._size != other.current._size;
}
Row<T> operator[](intptr_t index) {
return (*this + index).current;
}
};
vector2D class
The 2D vector class stores it's elements contiguously in a vector, but to access them or iterator over them it returns Rows and RowIterators. Because a Row is just two values (a pointer and a size), this is really cheap to do, and the compiler should be able to optimize it easily.
Note that to preserve const correctness, I use Row<T const>, which creates a Row with constant elements. (This greatly simplifies the implementation of Row).
template<class T>
class vector2D : private std::vector<T> {
size_t rows;
size_t columns;
using std::vector<T>::data;
public:
size_t size() const noexcept {
return rows;
}
// Gets a particular row
Row<T> operator[](size_t index) noexcept {
return { data() + columns * index, columns };
}
// Get a particular row when const
Row<T const> operator[](size_t index) const noexcept {
return { data() + columns * index, columns };
}
RowIterator<T> begin() noexcept {
return { Row<T>{ data() , columns } };
}
RowIterator<T> end() noexcept {
return { Row<T>{ data() + columns * rows, columns } };
}
RowIterator<T const> begin() const noexcept {
return { Row<T const>{ data() , columns } };
}
RowIterator<T const> end() const noexcept {
return { Row<T const>{ data() + columns * rows, columns } };
}
template<size_t N>
void push_back(std::array<T, N> const& arr) {
if(arr.size() == columns) {
insert(end(), arr.begin(), arr.end());
rows++;
}
else
throw std::invalid_argument("Bad number of columns");
}
void push_back(Row<T> arr) {
if(arr.size() == columns) {
insert(end(), arr.begin(), arr.end());
rows++;
}
else
throw std::invalid_argument("Bad number of columns");
}
void push_back(Row<T const> arr) {
if(arr.size() == columns) {
insert(end(), arr.begin(), arr.end());
rows++;
}
else
throw std::invalid_argument("Bad number of columns");
}
void push_back(std::initializer_list<T> arr) {
if(arr.size() == columns) {
insert(end(), arr.begin(), arr.end());
rows++;
}
else
throw std::invalid_argument("Bad number of columns");
}
vector2D(size_t rows, size_t columns)
: std::vector<T>(rows * columns)
, rows(rows)
, columns(columns) {}
};
Benchmark results
Run the benchmark here
The benchmark results are in, and vector2D is just as fast as using a vector of arrays!!!
The test
The test has two parts:
Fill the 2D array with values
Sum all the values
To make things as general as possible, these are the functions I used. They can be used with std::vector<std::vector<...>>, std::vector<std::array<...>>, or our very own vector2D!
template<class List>
auto calculateSum2D(List const& list) {
using elem_t = std::decay_t<decltype(list[0][0])>;
elem_t initial = 0;
for(auto const& row : list) {
for(auto& elem : row) {
initial += elem;
}
}
return initial;
}
template<class List>
void fill(List& list, int rows, int cols) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
list[i][j] = i * j;
}
}
}
The results
We used Quickbench to obtain the results, and vector2D was 4.5 times faster than using a vector of vectors!
These results were obtained using the corresponding functions, written using quick bench!
// Benchmark using a vector of vectors
static void sumVector(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
std::vector<std::vector<double>> vect(rows, std::vector<double>(cols));
fill(vect, rows, cols);
auto sum = calculateSum2D(vect);
benchmark::DoNotOptimize(sum);
}
}
// Register the function as a benchmark
BENCHMARK(sumVector);
// Benchmark using a vector of arrays
static void sumArray(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
std::vector<std::array<double, cols>> vect(rows, std::array<double, cols>());
fill(vect, rows, cols);
auto sum = calculateSum2D(vect);
benchmark::DoNotOptimize(sum);
}
}
// Register the function as a benchmark
BENCHMARK(sumArray);
// Benchmark using vector2D implementation
static void sumvector2D(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
vector2D<double> vect(rows, cols);
fill(vect, rows, cols);
auto sum = calculateSum2D(vect);
benchmark::DoNotOptimize(sum);
}
}
// Register the function as a benchmark
BENCHMARK(sumvector2D);
Benchmarks v2: no repeated allocation
View benchmark 2 here
As it turns out, in the initial benchmark, most of the cost came from repeated allocations (in all cases, the object was re-allocated each iteration of the benchmark). To fix this, I moved the declaration out of the loop, so that it'd only occur once. I also adjusted the number of rows and columns so that there were more rows and fewer columns, so as to get a more realistic scenario where not the entire thing fits in the cache.
Once again, vector2D and vector<array> perform nearly identically, however this time vector<vector> does a much better job, and the gap is not nearly so impressive.
The reason for the difference in speedup is that this time around, the only differences were the result of poor cache locality, as each object was only being allocated once.
Summary
Based on the benchmark results, vector2D should bump your performance back up to what it was originally. Because your code presumably contains a mix of allocations and usage, you got a result somewhere between the two benchmarks (2.5 times slower for vector of vector). Because vector2D is contiguous and avoids the repeated heap allocations that plagued the vector-of-vector approach, it should be just as fast as a vector of arrays.
Related
I have a vector of vectors of strings. I want to find the lengths of the longest string in each column. All the subvectors are of the same length and have an element stored in it, so it would be rather easy to find it with two for loops and reversed indices.
vector<vector<string>> myvec = {
{ "a", "aaa", "aa"},
{"bb", "b", "bbbb"},
{"cc", "cc", "ccc"}
};
But is it possible to do it with iterators without using indices?
Assuming the inner vectors are all the same length, you can have something like
template <typename T>
class columnwise {
std::vector<std::vector<T>> * underlying;
struct proxy {
std::vector<std::vector<T>> * underlying;
std::vector<T>::difference_type offset;
struct iterator {
std::vector<std::vector<T>>::iterator outer;
std::vector<T>::difference_type offset;
using reference = typename std::vector<T>::reference;
using pointer = typename std::vector<T>::pointer;
iterator operator++() { ++outer; return *this; }
reference operator*() { return *(outer->begin() + offset); }
pointer operator->() { return (outer->begin() + offset).operator->(); }
bool operator==(iterator rhs) { return (outer == rhs.outer) && (offset == rhs.offset); }
};
public:
iterator begin() { return { underlying->begin(), offset }; }
iterator end() { return { underlying->end(), offset }; }
};
struct iterator {
// member type aliases
std::vector<std::vector<T>> * underlying;
std::vector<T>::difference_type offset;
iterator operator++() { ++offset; return *this; }
proxy operator*() { return { underlying, offset }; }
bool operator==(iterator rhs) { return (underlying== rhs.underlying) && (offset == rhs.offset); }
};
std::vector<T>::difference_type inner_size() { if (auto it = underlying->begin(); it != underlying->end()) { return it->size(); } return 0; }
public:
columnwise(std::vector<std::vector<T>> & vec) : underlying(&vec) {}
iterator begin() { return { underlying, 0 }; }
iterator end() { return { underlying, inner_size() }; }
};
Which iterates as you expect.
What about something like this?
std::vector<std::size_t> max_lengths(myvec.front().size(), 0);
for (auto const& strings : myvec) {
std::transform(max_lengths.begin(), max_lengths.end(), strings.begin(), max_lengths.begin(),
[](std::size_t l, std::string const& s) {
return std::max(l, s.size());
}
);
}
Demo
Here is a solution using C++20 ranges and lambdas that is similar to Nelfeals answer:
// returns a function returning the i-th element of an iterable container
auto ith_element = [](size_t i) {
return [i](auto const& v){
return v[i];
};
};
// returns a range over the i-th column
auto column = [ith_element](size_t i) {
return std::views::transform(ith_element(i)); // returns a range containing only the i-th elements of the elements in the input range
};
// returns the size of something
auto length = [](auto const& s){ return s.size(); };
// returns the max length of the i-th column
auto max_length_of_col = [column, length](auto const& v, size_t i) {
return std::ranges::max(
v | column(i) | std::views::transform(length)
);
};
I personally like how the ranges library helps you convey intent with your code, rather than having to prescribe the procedure to achieve your goal.
Note that if you replace the body of inner lambda in ith_element with the following block, it will also work for iterable containers without random access.
auto it = v.cbegin();
std::ranges::advance(it, i);
return *it
Demo
As a final remark, this solution lets you iterate over one column given an index of the column. I would advise against implementing a column iterator for vector<vector>: The existence of an iterator implies that something exists in memory that you can iterate over. The existence of columns is only implied by the additional information that you have given us, namely that all rows have the same length. If you do want iterators both for columns and rows, I would wrap your container in a new type (usually called matrix or similar) that properly conveys that intent. Then you can implement iterators for that new type, see Calath's answer.
EDIT:
I realized that my argument against a column iterator can be used as an argument against the column function in this answer as well. So here is a solution that let's you iterate over columns in a range-based for loop intead of iterating over column indices:
for (auto column : columns(myvec)){
std::cout << max_length(column) << std::endl;
}
Is it possible to get an entire column of a 2D matrix which is implemented as std::vector<std::vector<int32_t>>. We all know we can access an entire row using range for loop like for(const auto& row : matrix). Is there a similar way to access an entire column using range for loop like for(const auto& col : matrix)
You need a column access facet. Your facet will have:
a reference to the matrix, e.g. m_matrix
the column number, e.g. m_column
You specify the matrix and the column number to construct the facet. Then you need to define operator[] (row) { return this ->m_vector [row] [this ->m_column]; }. HTH.
While I see no way to do this with the actual std::vector, what you could do would be to implement your own class Matrix which stores a 2D vector (or which even inherits from one, which I don't recommend though - especially I'd make sure that my matrix class never contains a jagged array).
If you have done that, you could then create another class Column, which looks somewhat like this:
class Column
{
private:
Matrix* target; // << consider rather using smart pointers here
size_t column_index;
public:
int32_t& operator[](size_t index) { return (*target)[column_index][index]; }
const int32_t& operator[](size_t index) const { return (*target)[column_index][index]; }
};
So far so good, you now have a column class that accesses some matrix. Simplest approach would now be to also have some method for the size (that again accesses the matrix class) and then do a old fashioned for loop, for(size_t i=0; i<size; i++)...
If you now want a range based loop instead, what you'd have to do is to give Column an inner class Iterator that behaves as iterator and give Column two methods begin() and end() which each return a fitting Iterator.
class Column
{
...
public:
...
size_t size(); // I omit the details here
class Iterator
{
...
};
Iterator begin() { return Iterator(this, 0); }
Iterator end() { return Iterator(this, size()); }
};
This would be the base structure of such an iterator being used. Note that you'd had to actually write two such classes, one being constant.
Inside the iterator would look somewhat like this:
class Column::Iterator
{
private:
Column* father;
size_t index;
public:
Iterator(Column* father, size_t index) : father(father), index(index { }
Iterator operator++() { Iterator i = *this; i.index++; return i; }
Iterator operator++(int) { index++; return *this; }
int32_t& operator*() { return (*father)[index]; }
int32_t* operator->() { return &((*father)[index]); }
bool operator==(const Iterator& other) { return father == other.father and index == other.index; }
bool operator!=(const Iterator& other) { return not operator==(other); }
};
Note that I haven't tested the code.
More on this topic:
How to make the for each loop function in C++ work with a custom class
https://codereview.stackexchange.com/questions/74609/custom-iterator-for-a-linked-list-class
https://gist.github.com/jeetsukumaran/307264
It is possible using range based for loops, to access the column values, you just have a fixed index:
for(const auto& row : matrix) {
cout << row[5] << endl; // 5 is the fixed column index
}
From knowing that it's possible to write a little helper function which extracts a complete column into another std::vector<>:
std::vector<int> extract_column(const std::vector<std::vector<int>>& matrix, int col_idx) {
std::vector<int> result;
for(const auto& row : matrix) {
result.push_back(row[col_idx]);
}
Here's a complete example:
#include <iostream>
#include <vector>
std::vector<int> extract_column(const std::vector<std::vector<int>>& matrix, int col_idx) {
std::vector<int> result;
for(const auto& row : matrix) {
result.push_back(row[col_idx]);
}
return result;
}
int main() {
std::vector<std::vector<int>> matrix =
{ { 1, 2, 3 } ,
{ 4, 5, 6 } ,
{ 7, 8, 9 }
};
// print the middle row
for(auto elem : extract_column(matrix,1)) {
std::cout << elem << std::endl;
}
}
Output
2
5
8
See the online demo
That said, it should be noted that a std::vector<std::vector<T>> as solution for a matrix representation is suboptimal.
The better fit would be to represent all the matrix elements (for arbitrary dimensions) as a contiguous array, and apply some basic math, to access certain (groups) of matrix elements. The std::vector<std::vector<T>> uses separate allocations for the row elements
To illustrate:
+-+-+-+
row 1 |1|2|3|
+-+-+-+
+-+-+-+ +-+-+-++-+-+-++-+-+-+
row 2 |4|5|6| => |1|2|3||4|5|6||7|8|9|
+-+-+-+ +-+-+-++-+-+-++-+-+-+
+-+-+-+
row 3 |7|8|9| row 1 row 2 row 3
+-+-+-+
But how to this exactly would lead too far here, and was already answered in other questions at SO, and realized by a number of math frameworks.
I have two vectors. One that actually holds the data (let's say floats) and one that holds the indices. I want to pass at nth_element the indices vector, but I want the comparison to be done by the vector that actually holds the data. I was thinking about a functor, but this provides only the () operator I guess. I achieved that by making the data vector a global one, but of course that's not desired.
std::vector<float> v; // data vector (global)
bool myfunction (int i,int j) { return (v[i]<v[j]); }
int find_median(std::vector<int> &v_i)
{
size_t n = v_i.size() / 2;
nth_element(v_i.begin(), v_i.begin()+n, v_i.end(), myfunction);
return v_i[n];
}
You may use a functor like:
class comp_with_indirection
{
public:
explicit comp_with_indirection(const std::vector<float>& floats) :
floats(floats)
{}
bool operator() (int lhs, int rhs) const { return floats[lhs] < floats[rhs]; }
private:
const std::vector<float>& floats;
};
And then you may use it like:
int find_median(const std::vector<float>& v_f, std::vector<int> &v_i)
{
assert(!v_i.empty());
assert(v_i.size() <= v_f.size());
const size_t n = v_i.size() / 2;
std::nth_element(v_i.begin(), v_i.begin() + n, v_i.end(), comp_with_indirection(v_f));
return v_i[n];
}
Note: with C++11, you may use lambda instead of named functor class.
int find_median(const std::vector<float>& v_f, std::vector<int> &v_i)
{
assert(!v_i.empty());
assert(v_i.size() <= v_f.size());
const size_t n = v_i.size() / 2;
std::nth_element(
v_i.begin(), v_i.begin() + n, v_i.end(),
[&v_f](int lhs, int rhs) {
return v_f[lhs] < v_f[rhs];
});
return v_i[n];
}
I need to dynamically allocate 1-D and 2-D arrays whose sizes are given at run-time.
I managed to "discover" std::vector and I think it fits my purposes, but I would like to ask whether what I've written is correct and/or can be improved.
This is what I'm doing:
#include <vector>
typedef std::vector< std::vector<double> > matrix;
//... various code and other stuff
std::vector<double> *name = new std::vector<double> (size);
matrix *name2 = new matrix(sizeX, std::vector<double>(sizeY));
Dynamically allocating arrays is required when your dimensions are given at runtime, as you've discovered.
However, std::vector is already a wrapper around this process, so dynamically allocating vectors is like a double positive. It's redundant.
Just write (C++98):
#include <vector>
typedef std::vector< std::vector<double> > matrix;
matrix name(sizeX, std::vector<double>(sizeY));
or (C++11 and later):
#include <vector>
using matrix = std::vector<std::vector<double>>;
matrix name(sizeX, std::vector<double>(sizeY));
You're conflating two issues, dynamic allocation and resizable containers. You don't need to worry about dynamic allocation, since your container does that for you already, so just say it like this:
matrix name(sizeX, std::vector<double>(sizeY));
This will make name an object with automatic storage duration, and you can access its members via name[i][j].
What you're doing should basically work, however:
In general, don't dynamically allocate objects
If you want a vector, do this:
std::vector<double> vec(size);
not this:
std::vector<double>* vec = new std::vector<double>(size);
The latter gives you a pointer, which you have to delete. The former gives you a vector which, when it goes out of scope, cleans up after itself. (Internally, of course, it dynamically allocates objects, but the trick is that this is handled by the class itself, and you don't need to worry about it in your user code).
It is correct but could be made more efficient.
You could use the boost multidimensional arrays:
http://www.boost.org/doc/libs/1_47_0/libs/multi_array/doc/user.html
Or, you can implement your own class for it and handle the indexing yourself.
Perhaps something like this (which is not well tested):
#include <vector>
#include <cassert>
template <typename T, typename A = std::allocator<T> >
class Array2d
{
public:
typedef Array2d<T> self;
typedef std::vector<T, A> Storage;
typedef typename Storage::iterator iterator;
typedef typename Storage::const_iterator const_iterator;
Array2d() : major_(0), minor_(0) {}
Array2d(size_t major, size_t minor)
: major_(major)
, minor_(minor)
, storage_(major * minor)
{}
template <typename U>
Array2d(size_t major, size_t minor, U const& init)
: major_(major)
, minor_(minor)
, storage_(major * minor, u)
{
}
iterator begin() { return storage_.begin(); }
const_iterator begin() const { return storage_.begin(); }
iterator end() { return storage_.end(); }
const_iterator end() const { return storage_.end(); }
iterator begin(size_t major) {
assert(major < major_);
return storage_.begin() + (major * minor_);
}
const_iterator begin(size_t major) const {
assert(major < major_);
return storage_.begin() + (major * minor_);
}
iterator end(size_t major) {
assert(major < major_);
return storage_.begin() + ((major + 1) * minor_);
}
const_iterator end(size_t major) const {
assert(major < major_);
return storage_.begin() + ((major + 1) * minor_);
}
void clear() {
storage_.clear();
major_ = 0;
minor_ = 0;
}
void clearResize(size_t major, size_t minor)
{
clear();
storage_.resize(major * minor);
major_ = major;
minor_ = minor;
}
void resize(size_t major, size_t minor)
{
if ((major != major_) && (minor != minor_))
{
Array2d tmp(major, minor);
swap(tmp);
// Get minimum minor axis
size_t const dist = (tmp.minor_ < minor_) ? tmp.minor_ : minor_;
size_t m = 0;
// copy values across
for (; (m < tmp.major_) && (m < major_); ++m) {
std::copy(tmp.begin(m), tmp.begin(m) + dist, begin(m));
}
}
}
void swap(self& other)
{
storage_.swap(other.storage_);
std::swap(major_, other.major_);
std::swap(minor_, other.minor_);
}
size_t minor() const {
return minor_;
}
size_t major() const {
return major_;
}
T* buffer() { return &storage_[0]; }
T const* buffer() const { return &storage_[0]; }
bool empty() const {
return storage_.empty();
}
template <typename ArrRef, typename Ref>
class MajorProxy
{
ArrRef arr_;
size_t major_;
public:
MajorProxy(ArrRef arr, size_t major)
: arr_(arr)
, major_(major)
{}
Ref operator[](size_t index) const {
assert(index < arr_.minor());
return *(arr_.buffer() + (index + (major_ * arr_.minor())));
}
};
MajorProxy<self&, T&>
operator[](size_t major) {
return MajorProxy<self&, T&>(*this, major);
}
MajorProxy<self const&, T const&>
operator[](size_t major) const {
return MajorProxy<self&, T&>(*this, major);
}
private:
size_t major_;
size_t minor_;
Storage storage_;
};
While the points the other answers made were very correct (don't dynamically allocate the vector via new, but rather let the vector do the allocation), if you are thinking terms of vectors and matrices (e.g. linear algebra), you might want to consider using the Eigen matrix library.
You don't allocate containers dynamically. They can automatically manage memory for you if they themselves are not manually managed.
A vector grows when you add new items with push_back (or insert), you can choose its size from the start with arguments to the constructor, and you can resize it later with the resize method.
Creating a vector of vectors with your sizes with the constructor looks like this:
std::vector< std::vector<double> > matrix(size, std::vector<double>(sizeY));
This means: size instances of a std::vector<double>, each containing sizeY doubles (initialized to 0.0).
Sometimes you don't want to fill your stack and your memory requirement is large. Hence you may want to use vector> created dynamically especially while creating a table of a given row and col values.
Here is my take on this in C++11
int main() {
int row, col;
std::cin >> row >> col;
auto *arr = new std::vector<std::vector<int>*>(row);
for (int i=0; i<row; i++) {
auto *x = new std::vector<int>(col, 5);
(*arr)[i] = x;
}
for (int i=0; i<row; i++) {
for(int j=0; j<col; j++) {
std::cout << arr->at(i)->at(j) << " ";
}
std::cout << std::endl;
}
return 0;
}
#include < iostream >
#include < vector >
using namespace std;
int main(){
vector<int>*v = new vector<int>(); // for 1d vector just copy paste it
v->push_back(5);
v->push_back(10);
v->push_back(20);
v->push_back(25);
for(int i=0;i<v->size();i++){
cout<<v->at(i)<<" ";
}
cout<<endl;
delete v;
system("pause");
return 0;
}
If you don't need to resize the array sizes at run time, then you can just use standard arrays (allocated at runtime)!
However, if you do need to resize arrays at runtime, then you can use the following (revised) code:
#include <vector>
typedef std::vector< std::vector<double> > matrix;
//... various code and other stuff
std::vector<double> *name = new std::vector<double> (size);
matrix *name2 = new matrix(sizeX, std::vector<double>(sizeY));
In essence, all I've done is remove a single bracket (().
I am building an app that needs to have support for two dimensional arrays to hold a grid of data. I have a class Map that contains a 2d grid of data. I want to use vectors rather than arrays, and I was wondering what the best practices were for using 2d vectors. Should I have a vector of vectors of MapCells? or should it be a vector of vectors of pointers to MapCells? or references to MapCells?
class Map {
private:
vector<vector<MapCell>> cells;
public:
void loadMap() {
cells.clear();
for (int i = 0; i < WIDTH; i++) {
//How should I be allocating these?
vector<MapCell> row(HEIGHT);
for (int j = 0; j < HEIGHT; j++) {
//Should I be dynamically allocating these?
MapCell cell;
row.push_back(cell);
}
cells.push_back(row);
}
}
}
Basically what way of doing this is going to get me in the least amount of trouble (with respect to memory management or anything else)?
When you want a square or 2d grid, do something similar to what the compiler does for multidimensional arrays (real ones, not an array of pointers to arrays) and store a single large array which you index correctly.
Example using the Matrix class below:
struct Map {
private:
Matrix<MapCell> cells;
public:
void loadMap() {
Matrix<MapCell> cells (WIDTH, HEIGHT);
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
// modify cells[i][j]
}
}
swap(this->cells, cells);
// if there's any problem loading, don't modify this->cells
// Matrix swap guarantees no-throw (because vector swap does)
// which is a common and important aspect of swaps
}
};
Variants of matrix classes abound, and there are many ways to tailor for specific use. Here's an example in less than 100 lines that gets you 80% or more of what you need:
#include <algorithm>
#include <memory>
#include <vector>
template<class T, class A=std::allocator<T> >
struct Matrix {
typedef T value_type;
typedef std::vector<value_type, A> Container;
Matrix() : _b(0) {}
Matrix(int a, int b, value_type const& initial=value_type())
: _b(0)
{
resize(a, b, initial);
}
Matrix(Matrix const& other)
: _data(other._data), _b(other._b)
{}
Matrix& operator=(Matrix copy) {
swap(*this, copy);
return *this;
}
bool empty() const { return _data.empty(); }
void clear() { _data.clear(); _b = 0; }
int dim_a() const { return _b ? _data.size() / _b : 0; }
int dim_b() const { return _b; }
value_type* operator[](int a) {
return &_data[a * _b];
}
value_type const* operator[](int a) const {
return &_data[a * _b];
}
void resize(int a, int b, value_type const& initial=value_type()) {
if (a == 0) {
b = 0;
}
_data.resize(a * b, initial);
_b = b;
}
friend void swap(Matrix& a, Matrix& b) {
using std::swap;
swap(a._data, b._data);
swap(a._b, b._b);
}
template<class Stream>
friend Stream& operator<<(Stream& s, Matrix const& value) {
s << "<Matrix at " << &value << " dimensions "
<< value.dim_a() << 'x' << value.dim_b();
if (!value.empty()) {
bool first = true;
typedef typename Container::const_iterator Iter;
Iter i = value._data.begin(), end = value._data.end();
while (i != end) {
s << (first ? " [[" : "], [");
first = false;
s << *i;
++i;
for (int b = value._b - 1; b; --b) {
s << ", " << *i;
++i;
}
}
s << "]]";
}
s << '>';
return s;
}
private:
Container _data;
int _b;
};
If the whole matrix has a mostly constant width and height, you may as well use a single vector, and address cells with (row * columnCount) + column. That way the whole thing will be stored in a single memory block instead of in several fragmented blocks for each row. (Though of course you are doing the right thing to wrap this concept up in a new class - I'm just talking about the behind-the-scenes implementation.)
A vector of vectors has the unfortunate property that if you insert a row at the top, std::vector will perform a copy construction (or assignment, possibly) for all the other rows as it shifts them down by one place. This in turn involves reallocating the storage for every row and individually copying the items in the cells of every row. (C++0x will probably be better at this.)
If you know that you will be doing that kind of thing often, the advantage of a single large memory block is that you can insert a new row at the top and std::vector will only have to shift all the cells forward by columnCount places, so it will seriously reduce the number of heap operations (freeing/reallocating of individual blocks).
Although as you suggest, a vector of pointers to vectors would have the further advantage that it would only need to shift forward a lot of pointer values, and the size of the block containing all the row pointers will be much smaller, further lessening the impact of heap operations.
Of course, the only way to be sure of the actual impact of these things on the performance of an application is to time it with various implementations and compare them. This is why you're doing exactly the right thing by hiding these details inside a new class.
Use a vector and translate the 2 dimensions to one dimension.
E.g. if your matrix has a width of W and a height of H, then mapping x,y to the index in the vector is simply x*W+y.
If your matrix is sparse you may want to use an std::map where the key is a pair (x and y).
In my projects I often use this simple grid class.