Custom matrix object in C++ I have been getting back into C++ today, after a few years of Python and R. I am completely rusty, but decided to create a matrix object to re-familiarise. In reality I wouldn't use this class, since I know Boost has matrix objects, but it's good practice! 

It can be compiled with g++ -std=c++11 matrix.cpp -o m. 

Any thoughts or comments are most welcome. 

#include &lt;iostream&gt;
#include &lt;vector&gt;

class matrix {
    // Class: matrix
    //
    // Definition: 
    // creates a 2d - matrix object as a vector of vectors and 
    // populates with (int) zeros.
    //
    // Methods: get_value, assign_value, get_row.

    // create vector of vectors
    std::vector&lt;std::vector&lt;int&gt; &gt; m;
    public:
        // constructor for class, matrix dimension (rows=X, cols=Y).
        matrix( int X, int Y) {
        m.resize(X, std::vector&lt;int&gt;(Y, 0));
    }

    class row {
        //class for matrix row object. Pass in a 
        // vector and overload `[]`. 

        std::vector&lt;int&gt; _row;
        public:
            // constructor
            row(std::vector&lt;int&gt; r) : _row(r) {
            }

            // overload `[]` to return y element.
            // note `.at()` does a range check and will throw an error 
            // if out of range
            int operator[]( int y) {
                return _row.at(y);
            }
    };

    // overload [] to return x element
    row operator[]( int x) {
        return row(m.at(x));
    }

    int get_value ( int x, int y ) {
        //  Function: get_value
        //  Definition: returns value `v` of element 
        //  `xy` in matrix `M`. 
        return m[x][y];
    }

    void assign_value ( int x, int y, int v ) {
        //  Function: assign_value 
        //  Definition: Assigns value `v` to element 
        //  `xy` in matrix. 
        m[x][y] = v;
    }

    std::vector&lt;int&gt; get_row(int y, int X){
        // Function get_row
        // Definition: returns a vector object with row 
        // of x-values of length X at y.
        std::vector&lt;int&gt; ROW;
        for ( int i=y; i&lt;=y;i++){
            for (int j=0; j&lt;=X-1;j++){
                ROW.push_back(m[i][j]);
            }
        }
        return ROW;
    }


};



int main(){

    // specify matrix dimensions
    int N = 10; // number of rows
    int M = 10; // number of cols

    // create a matrix object 
    matrix mm(N,M);

    // print elements
    int i, j;
    for (i=0; i&lt;=N-1;i++){
        for (j=0;j&lt;=M-1; j++){
            std::cout &lt;&lt; mm[i][j];
        }
        std::cout &lt;&lt; std::endl;
    }

    // grab a value and print it to console
    std::cout &lt;&lt; mm.get_value(0,0) &lt;&lt; std::endl;

    // assign a new value (v = 1) to element (0,0)
    mm.assign_value(0,0,1); 

    // re-print the updated matrix
    for (i=0; i&lt;=N-1;i++){
        for (j=0;j&lt;=M-1; j++){
            std::cout &lt;&lt; mm[i][j];
        }
        std::cout &lt;&lt; std::endl;
    }


    // `get_row()` test
    std::vector&lt;int&gt; R = mm.get_row(0, M);
    for( int i: R){
        std::cout &lt;&lt; i &lt;&lt; ' ';
    }
}
That's some nicely presented code.  I found it very easy to read and understand.
A vector of rows isn't the best structure for a matrix.  The reason is that each vector has its storage elsewhere, so you lose locality of access.  A better structure is a flat array (or vector) of elements, and a knowledge of the stride from one row to the next.  (we can make the stride be the same as the row length, for simplicity; separate members for width and stride can be useful in more advanced scenarios).
    std::vector&lt;int&gt; m;
    std::size_t width;
    
public:
    # Constraint: x * y must not overflow size_t
    matrix(std::size_t x, std::size_t y)
        : m(x*y, 0),
          width{x}
    {
    }

I've made the dimensions be size_t, as that's the natural type for a size or count in C++.
Now, when we need to index into the array, we need to multiply the y value by width and add x:
int get_value(std::size_t x, std::size_t y)
{
    return m[x + y*width];
}

We can improve on this, by returning a reference to the value.  Instead of having a &quot;get&quot; and &quot;set&quot; method, we have a single method (for now), and we can give it a more convenient name:
int&amp; operator()(std::size_t x, std::size_t y)
{
    return m[x + y*width];
}

This means that instead of having to write
mm.assign_value(0,0,1); 

we can instead use the more intuitive
mm(0,0) = 1;

Now it's time admit to a slight lie above.  We actually need two methods, because if we have a const matrix, we should be allowed to read, but not write, its elements.  So we also need:
const int&amp; operator()(std::size_t x, std::size_t y) const
{
    return m[x + y*width];
}


For printing, it's helpful to provide an operator&lt;&lt;().  Mine would look like this:
friend auto&amp; operator&lt;&lt;(std::ostream&amp; os, const matrix&amp; m)
{
    for (std::size_t row = 0;  row &lt; m.height;  ++row) {
        for (std::size_t col = 0;  col &lt; m.width;  ++col) {
            os &lt;&lt; m.m[col + row*m.width] &lt;&lt; ' ';
        }
        os &lt;&lt; '\n';
    }
    return os;
}

I added a height member to make this easier.

With these changes, see how much easier it is to use:
#include &lt;iostream&gt;

int main()
{
    // create a matrix object
    matrix mm(4,6);

    // print elements
    std::cout &lt;&lt; mm;

    // grab a value and print it to console
    std::cout &lt;&lt; mm(0,0) &lt;&lt; std::endl;

    // assign a new value (v = 1) to element (0,0)
    mm(0,0) = 1;

    // re-print the updated matrix
    std::cout &lt;&lt; mm;
}


Here's the full version of matrix after my edits:
#include &lt;ostream&gt;
#include &lt;vector&gt;

class matrix {
    std::vector&lt;int&gt; m;
    std::size_t width;
    std::size_t height;

public:
    matrix(std::size_t x, std::size_t y)
        : m(x*y, 0),
          width{x},
          height{y}
    {
    }

    int&amp; operator()(std::size_t x, std::size_t y)
    {
        return m[x + y*width];
    }

    const int&amp; operator()(std::size_t x, std::size_t y) const
    {
        return m[x + y*width];
    }

    friend auto&amp; operator&lt;&lt;(std::ostream&amp; os, const matrix&amp; m)
    {
        for (std::size_t row = 0;  row &lt; m.height;  ++row) {
            for (std::size_t col = 0;  col &lt; m.width;  ++col) {
                os &lt;&lt; m.m[col + row*m.width] &lt;&lt; ' ';
            }
            os &lt;&lt; '\n';
        }
        return os;
    }
};


Further exercises

If you actually want a public get_row() (and get_column()), these will need new implementations, perhaps copying values.
Think about providing a get_subarray(x, y, width, height) to give a view of part of the matrix - you'll need new members for offset and stride.  See how we can now more easily implement get_row() and get_column() using this new method.
Make the matrix a template, so we can have elements of whatever type we choose, rather than only int.