I assume here that the array dimensions cannot be known at compile time. The "vector of vectors" technique, as described by John, basically works. However, a drawback is the necessity of allocating many separate memory blocks, one per elementary vector.
For example, if the 2-dimensional integer array is meant to be a 1920x1080 bitmap image, with one integer value per pixel, this means we are going to call malloc 1080 times at object creation time, and we are also going to call free/delete 1080 times when the object is released. This can be quite costly. Also, a lot of functionality in existing graphic libraries will insist on having a single storage block per image.
To work around these problems, we can instead use a single, private 1-dimensional standard vector, and arrange access functions so that expression mat[i][j] still works as expected. The easiest way to do this is to have mat[i] overloaded, so it returns the address of the beginning of the row. That way, the mat[i] pointer value can naturally be re-indexed with [j]. The resulting array access is not range-checked, just like for a regular std::vector object.
The idea is implemented in the code sample below:
#include <vector>
#include <iostream>
#include <fstream>
class array2d {
private:
size_t rowCount, colCount;
std::vector<int> vec;
public:
array2d(size_t m, size_t n) : rowCount(m), colCount(n)
{
size_t size = m*n;
vec = std::vector<int>(size, 0);
int* basePtr = vec.data();
for (size_t i=0; i < size; i++)
basePtr[i] = 0;
}
inline size_t getRowCount() { return rowCount; }
inline size_t getColCount() { return colCount; }
inline int* operator[](size_t rowId) // HERE !
{
// NOT range-checked just like std::vector
return (vec.data() + (rowId*(this->colCount)));
}
void setArrayFromFile(std::istream& file) {
for (size_t row = 0; row < rowCount; row++) {
int* rowStart = (*this)[row];
for (size_t col = 0; col < colCount; col++) {
file >> rowStart[col];
}
}
}
// specialized version to read from standard input:
void setArray()
{
setArrayFromFile(std::cin);
}
};
//---------------------------------------------------------
// EXAMPLE OF USE:
void printIntegerMatrix(array2d& matrix)
{
size_t rowCount = matrix.getRowCount();
size_t colCount = matrix.getColCount();
for (size_t row = 0; row < rowCount; row++) {
for (size_t col = 0; col < colCount; col++) {
std::cout << matrix[row][col] << " ";
}
std::cout << std::endl;
}
}
void processIntegerMatrix(array2d& matrix, const std::string& fileName)
{
// fill in matrix from a text file then print its contents on stdout:
std::ifstream inputStream{fileName};
if (!inputStream) {
std::cerr << " Cannot open file " << fileName << std::endl;
exit(EXIT_FAILURE);
}
matrix.setArrayFromFile(inputStream);
inputStream.close();
std::cerr << "After reading from file." << std::endl;
printIntegerMatrix(matrix);
}
int main(int argc, const char* argv[])
{
size_t rowCount = 3;
size_t colCount = 6;
// have a text file with some numbers in it:
std::string fileName{"numbers1.dat"};
std::cout << "matrix size: " << rowCount << 'x' << colCount << std::endl;
// create zero-filled matrix:
array2d mat1(rowCount, colCount);
processIntegerMatrix(mat1, fileName);
return EXIT_SUCCESS;
}