Using Vector for a 2D Grid
How can I use std::vector
to implement a simple 2D grid or matrix?
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.
Approach 1: Vector of Vectors
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.
Approach 2: Single Vector with Custom Indexing
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.
Advanced Features
You can extend either approach with more advanced features:
Range Checking
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];
}
Iterators
Implement custom iterators for row-wise or column-wise traversal.
Resizing
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;
}
Operations
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.
Dynamic Arrays using std::vector
Explore the fundamentals of dynamic arrays with an introduction to std::vector