Skip to main content
replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link

The only function signatures allowed by the C++ standard both require an int return type. See this questionthis question for details.

Putting using namespace std at the top of every program is a bad habita bad habit that you'd do well to avoid. It's particularly bad to use it when writing include headers.

By default, a compiler-generated destructor is concrete rather than virtual, but this leads to problems with collections of objects. See this questionthis question for more details on why.

The only function signatures allowed by the C++ standard both require an int return type. See this question for details.

Putting using namespace std at the top of every program is a bad habit that you'd do well to avoid. It's particularly bad to use it when writing include headers.

By default, a compiler-generated destructor is concrete rather than virtual, but this leads to problems with collections of objects. See this question for more details on why.

The only function signatures allowed by the C++ standard both require an int return type. See this question for details.

Putting using namespace std at the top of every program is a bad habit that you'd do well to avoid. It's particularly bad to use it when writing include headers.

By default, a compiler-generated destructor is concrete rather than virtual, but this leads to problems with collections of objects. See this question for more details on why.

Source Link
Edward
  • 67.2k
  • 4
  • 120
  • 284

I have found a number of things that could help you improve your code.

Fix your formatting

There are abundant examples here of C++ code that is well formatted. This code has peculiar and inconsistent indentation that makes it difficult to tell when a function begins and ends. Fixing that would improve readability.

Don't use std::endl unless you really need to flush the stream

The difference between std::endl and '\n' is that std::endl actually flushes the stream. This can be a costly operation in terms of processing time, so it's best to get in the habit of only using it when flushing the stream is actually required. It's not for this code.

Don't overspecify member functions

The code currently includes these two lines in Matrix.h:

Matrix  operator -()const;
Matrix  Matrix::operator -(const Matrix &t)const;

The first one is fine, but the second one is overspecified. Because it's already inside the Matrix class, it's not necessary to tell the compiler that it's inside the Matrix class. It makes the code more cluttered and harder to read as well as being inconsistent, so that second line shoud instead be written like this:

Matrix operator-(const Matrix &t) const;

Don't use void main()

The only function signatures allowed by the C++ standard both require an int return type. See this question for details.

Don't abuse using namespace std

Putting using namespace std at the top of every program is a bad habit that you'd do well to avoid. It's particularly bad to use it when writing include headers.

Make sure you have all required #includes

The Matrix.h file refers to std::ostream uses atoi but doesn't #include <iostream>. Also, carefully consider which #includes are part of the interface (and belong in the .h file, as with this one) and which are solely part of the implementation (and therefore should only be listed in the .cpp file.)

Always put the class' own include first

If you get into the habit of always putting the #include file for the class first, you'll never have to worry about having insufficient include files in the header. That is, for this case, if the first line of Matrix.cpp had been #include "Matrix.h", your compiler would have been able to have pointed out the previous point to you.

Fix your inserter

There is a friend in the code that looks like this:

friend std::ostream & operator <<(std::ostream & os , const Matrix & m )
{
    for(int i=0;i<m.rows;i++)
    {
        for(int j=0;j<m.cols;j++)
            cout <<m.Mat[i][j]<<"  " ;
        cout <<endl ;
    }
    return os ;
}

The error is that it is ignoring the pass std::ostream and using cout instead. I'd write it like this:

friend std::ostream &operator<<(std::ostream &os, const Matrix &m) {
    for (int i=0; i < m.rows; ++i) {
        for (int j=0; j < m.cols; ++j) {
            os << m.Mat[i][j] << "  " ;
        }
        os << '\n';
    }
    return os;
}

Note that I've used the pre-increment rather than post-increment operators (not a big deal here, but a good habit to use) and introduced more whitespace to make it easier to read. I've also added curly braces to make it very clear to the person reading the code what was actually intended.

Use the C++ form of #include files

The code includes this line

#include <assert.h>

However that puts everything into the global namespace. Better would be to explicitly use the C++ version which looks like this:

#include <cassert>

This puts things into the std:: namespace and is a better way to do this for the standard C headers.

Don't use this-> in member functions

Using this->rows in a constructor is distracting and unhelpful. Simply use rows instead. If you're trying to avoid a name clash between passed parameters and internal variables, simply rename one of them. One common idiom is to prefix every member data item with m_.

Throw exceptions rather than asserts

It's good you're checking for problems in the code, but the C++ way of handling those kinds of things is by throwing an exception rather than termminating the program via an assert.

Allow for range checking

Because rows and cols are private member functions, and because operator[] does no range checking, there is no way for the user to provide range checking because there is no way to recover the dimensions of the matrix.

Make your base class destructor virtual

By default, a compiler-generated destructor is concrete rather than virtual, but this leads to problems with collections of objects. See this question for more details on why.

Consider return a reference from operator=

The current operator= has this signature:

void operator=(const Matrix &otherM ) const;

This has two problems that I can see. First, it shouldn't be const because it clearly alters the object (by definition!). Second, declaring it as return void isn't necessarily wrong, but it would prevent doing assignment chaining and returning the value of an assignment. Consider:

int a, b, c, d;
a = b = c = 5;
if (10 == (d = c+a)) {
    std::cout << "Addition works as expected\n";
}

That works just fine for int, but if we try that with your Matrix class it won't work for two reasons. First, there's no default constructor, and second, operator= returns void.

Prefer a single allocation

Instead of doing multiple allocations in the constructor, it would be simpler to do only a single allocation. This is both faster and simpler.

Be wary of returning pointers to internal data

There is a serious problem with the implementation of operator[] because it returns, in effect, a pointer to internal data. Here are just two examples for why that's wrong:

int *kaboom;          // we use this later
const Matrix s = m;   // make a const copy
{ 
    const Matrix a = s;  // make a local copy
    a[1][1] = 5;         // this alters a const object! not good.
    kaboom = a[1];       // use operator[]
}  // a is now destroyed
kaboom[1] = 0;           // points to freed memory!  not good.