Using std::vector
to implement a 2D grid or matrix is a common and efficient way to handle two-dimensional data in C++. There are primarily two approaches: using a vector of vectors, or using a single vector with custom indexing.
This is the most straightforward approach, where we create a vector of vector elements:
#include <iostream>
#include <vector>
class Grid {
private:
std::vector<std::vector<int>> data;
size_t rows, cols;
public:
Grid(size_t r, size_t c)
: rows(r), cols(c),
data(r, std::vector<int>(c, 0)){}
int& at(size_t r, size_t c){
return data[r][c];
}
const int& at(size_t r, size_t c) const{
return data[r][c];
}
void print() const{
for (const auto& row : data) {
for (int val : row) {
std::cout << val << ' ';
}
std::cout << '\n';
}
}
};
int main(){
Grid grid(3, 4);
// Set some values
grid.at(0, 0) = 1;
grid.at(1, 2) = 5;
grid.at(2, 3) = 9;
// Print the grid
grid.print();
}
1 0 0 0
0 0 5 0
0 0 0 9
This approach is intuitive and allows for rows of different lengths if needed. However, it may have slightly more overhead due to multiple allocations.
This approach uses a single vector to store all elements, which can be more efficient:
#include <iostream>
#include <vector>
class Grid {
private:
std::vector<int> data;
size_t rows, cols;
public:
Grid(size_t r, size_t c)
: rows(r), cols(c), data(r * c, 0){}
int& at(size_t r, size_t c){
return data[r * cols + c];
}
const int& at(size_t r, size_t c) const{
return data[r * cols + c];
}
void print() const{
for (size_t i = 0; i < rows; ++i) {
for (size_t j = 0; j < cols; ++j) {
std::cout << at(i, j) << ' ';
}
std::cout << '\n';
}
}
};
int main(){
Grid grid(3, 4);
// Set some values
grid.at(0, 0) = 1;
grid.at(1, 2) = 5;
grid.at(2, 3) = 9;
// Print the grid
grid.print();
}
1 0 0 0
0 0 5 0
0 0 0 9
This approach uses a single allocation and can be more cache-friendly, potentially offering better performance for large grids.
You can extend either approach with more advanced features:
Add bounds checking in the at()
 function:
int& at(size_t r, size_t c){
if (r >= rows || c >= cols) {
throw std::out_of_range(
"Grid indices out of range");
}
return data[r * cols + c];
}
Implement custom iterators for row-wise or column-wise traversal.
Add functions to resize the grid:
void resize(size_t new_rows, size_t new_cols){
std::vector<int> new_data(
new_rows * new_cols, 0);
for (size_t i = 0; i <
std::min(rows, new_rows); ++i) {
for (size_t j = 0; j <
std::min(cols, new_cols); ++j) {
new_data[i * new_cols + j] = at(i, j);
}
}
data = std::move(new_data);
rows = new_rows;
cols = new_cols;
}
Implement mathematical operations like matrix addition or multiplication.
Remember, the choice between these approaches depends on your specific use case. The vector of vectors is more flexible but might be slightly slower, while the single vector approach is generally faster but less flexible for non-rectangular grids.
Answers to questions are automatically generated and may not have been reviewed.
std::vector
Explore the fundamentals of dynamic arrays with an introduction to std::vector