Assume we have the following class:
#include <vector>
class Matrix {
 private:
  std::vector<std::vector<int>> data;
};
First of all I would like suggest you to implement a default constructor:
#include <vector>
class Matrix {
 public:
  Matrix(): data({}) {}
 private:
  std::vector<std::vector<int>> data;
};
At this time we can create Matrix instance as follows:
Matrix one;
The next strategic step is to implement a Reset method, which takes two integer parameters that specify the new number of rows and columns of the matrix, respectively:
#include <vector>
class Matrix {
 public:
  Matrix(): data({}) {}
  Matrix(const int &rows, const int &cols) {
    Reset(rows, cols);
  }
  void Reset(const int &rows, const int &cols) {
    if (rows == 0 || cols == 0) {
      data.assign(0, std::vector<int>(0));
    } else {
      data.assign(rows, std::vector<int>(cols));
    }
  }
 private:
  std::vector<std::vector<int>> data;
};
At this time the Reset method changes the dimensions of the 2D-matrix to the given ones and resets all its elements. Let me show you a bit later why we may need this.
Well, we can create and initialize our matrix:
Matrix two(3, 5);
Lets add info methods for our matrix:
#include <vector>
class Matrix {
 public:
  Matrix(): data({}) {}
  Matrix(const int &rows, const int &cols) {
    Reset(rows, cols);
  }
  void Reset(const int &rows, const int &cols) {
    data.resize(rows);
    for (int i = 0; i < rows; ++i) {
      data.at(i).resize(cols);
    }
  }
  int GetNumRows() const {
    return data.size();
  }
  int GetNumColumns() const {
    if (GetNumRows() > 0) {
      return data[0].size();
    }
    return 0;
  }
 private:
  std::vector<std::vector<int>> data;
};
At this time we can get some trivial matrix debug info:
#include <iostream>
void MatrixInfo(const Matrix& m) {
  std::cout << "{ \"rows\": " << m.GetNumRows()
            << ", \"cols\": " << m.GetNumColumns() << " }" << std::endl;
}
int main() {
  Matrix three(3, 4);
  MatrixInfo(three);
}
The second class method we need at this time is At. A sort of getter for our private data:
#include <vector>
class Matrix {
 public:
  Matrix(): data({}) {}
  Matrix(const int &rows, const int &cols) {
    Reset(rows, cols);
  }
  void Reset(const int &rows, const int &cols) {
    data.resize(rows);
    for (int i = 0; i < rows; ++i) {
      data.at(i).resize(cols);
    }
  }
  int At(const int &row, const int &col) const {
    return data.at(row).at(col);
  }
  int& At(const int &row, const int &col) {
    return data.at(row).at(col);
  }
  int GetNumRows() const {
    return data.size();
  }
  int GetNumColumns() const {
    if (GetNumRows() > 0) {
      return data[0].size();
    }
    return 0;
  }
 private:
  std::vector<std::vector<int>> data;
};
The constant At method takes the row number and column number and returns the value in the corresponding matrix cell:
#include <iostream>
int main() {
  Matrix three(3, 4);
  std::cout << three.At(1, 2); // 0 at this time
}
The second, non-constant At method with the same parameters returns a reference to the value in the corresponding matrix cell:
#include <iostream>
int main() {
  Matrix three(3, 4);
  three.At(1, 2) = 8;
  std::cout << three.At(1, 2); // 8
}
Finally lets implement >> operator:
#include <iostream>
std::istream& operator>>(std::istream& stream, Matrix &matrix) {
  int row = 0, col = 0;
  stream >> row >> col;
  matrix.Reset(row, col);
  for (int r = 0; r < row; ++r) {
    for (int c = 0; c < col; ++c) {
      stream >> matrix.At(r, c);
    }
  }
  return stream;
}
And test it:
#include <iostream>
int main() {
  Matrix four; // An empty matrix
  MatrixInfo(four);
  // Example output:
  //
  // { "rows": 0, "cols": 0 }
  std::cin >> four;
  // Example input
  //
  // 2 3
  // 4 -1 10
  // 8 7 13
  MatrixInfo(four);
  // Example output:
  //
  // { "rows": 2, "cols": 3 }
}
Feel free to add out of range check. I hope this example helps you :)