我正在构建一个需要支持二维数组来保存数据网格的应用程序。我有一个包含2d数据网格的类Map
。我想使用向量而不是数组,我想知道使用2d向量的最佳实践是什么。我应该有MapCells的矢量向量吗?或者它应该是MapCells指针向量的向量?或引用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);
}
}
}
基本上这样做的方法是让我遇到最少的麻烦(关于内存管理或其他任何事情)?
答案 0 :(得分:8)
当你想要一个正方形或二维网格时,做一些类似于编译器对多维数组(实数,不是数组指针数组)的操作,并存储一个你正确索引的大数组。
使用以下Matrix类的示例:
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
}
};
矩阵类的变体比比皆是,有很多方法可以针对特定用途进行定制。这是一个不到100行的例子,它可以满足你所需要的80%或更多:
#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;
};
答案 1 :(得分:3)
如果整个矩阵的宽度和高度基本恒定,您也可以使用单个vector
,并使用(row * columnCount) + column
寻址单元格。这样,整个事物将存储在单个内存块中,而不是存储在每行的几个碎片块中。 (当然,你正在做一个正确的事情,将这个概念包装在一个新的类中 - 我只是在谈论幕后实现。)
向量向量具有不幸的属性,如果在顶部插入一行,std::vector
将为所有其他行执行复制构造(或可能分配),因为它将它们向下移动一个位置。这又涉及为每一行重新分配存储并单独复制每行的单元格中的项目。 (C ++ 0x可能会更好。)
如果你知道你经常会这样做,单个大内存块的优点是你可以在顶部插入一个新行,std::vector
只需要移动所有单元格转发columnCount
个地方,因此它会严重减少堆操作的数量(释放/重新分配单个块)。
虽然正如你的建议,向量指针的向量将具有进一步的优势,它只需要向前移动许多指针值,并且包含所有行指针的块的大小将更小,进一步减少堆操作的影响。
当然,确保这些内容对应用程序性能的实际影响的唯一方法是使用各种实现对其进行计时并进行比较。这就是为什么你通过在新类中隐藏这些细节来做正确的事情。
答案 2 :(得分:0)
使用矢量并将2个维度转换为一个维度。 例如。如果你的矩阵的宽度为W,高度为H,那么将x,y映射到向量中的索引就是x * W + y。
如果您的矩阵是稀疏的,您可能需要使用std :: map,其中键是一对(x和y)。
答案 3 :(得分:0)
在我的项目中,我经常使用此simple grid class。