Overloaded operator inheritance

Hi, I'm creating a class called Matrix (basically 2D vectors), and a class SqMatrix (square matrix) inherits from Matrix, with methods like finding the determinant and the inverse, I also overloaded some operators like =, +, * to do basic arithmatic of matrices.

Now I have a problem: the operators return Matrix (or references to Matrix) objects, and now I also want SqMatrix to be able to use them (because the arithmatic are very similar) but they return Matrix, slicing down the SqMatrix "properties" of the objects.

I've thought of some ways to handle this:

1. Copy and paste the implementations and make minor changes
(which is not wise and makes the code hard to maintain)

2. Overload = to convert an object between Matrix and SqMatrix
(is this possible and, is this safe?)

3. Do not inherit from Matrix and simply declare all methods needed in the class Matrix
(but I don't want Matrix to do some occasionally impossible tasks, like finding the determinant)

4. Make a template to allow the return for whatever type (either Matrix or SqMatrix)
(are templates designed to work this way? Plus, what should be the template be - Matrix is general enough to be a base and Matrix shouldn't be a type of anything...)

Please note that a Matrix would end up to be a SqMatrix during calculations, so it seems that I have to use No.2...

Are there better designs?

Thanks very much.


EDITED: The declarations are quite long so I truncate them a little bit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Matrix
{
   public:
     // constructors
     Matrix(unsigned int m, unsigned int n);     // an m by n matrix
     Matrix();
        // need copy ctor!
     virtual ~Matrix() {}
   
     // some methods to access the content...or to print
     double GetElement(unsigned int row, unsigned int col) const
        { return itsMatrix[row][col]; }
     void SetElement(double element, unsigned int row, unsigned int col)
        { itsMatrix[row][col] = element; }
     
     void Input();
     void Print();
     // ...
     
     // operations
     void Trans();     // transpose
     
     // overloaded operators
     Matrix & operator= (const Matrix &);
     Matrix   operator+ (const Matrix &);
     Matrix   operator- (const Matrix &);
     Matrix   operator* (const double);
     Matrix   operator* (const Matrix &);
     
   protected:
     vector< vector<double> > itsMatrix;
};
// ========================================

class SqMatrix : public Matrix
{
   public:
     // constructors
     SqMatrix(unsigned int n): Matrix(n, n) {}   // a square matrix of size n
     SqMatrix(): Matrix() {}
        // need copy ctor!
     ~SqMatrix() {}
     
     // opearations
     SqMatrix Adj() const;           // adjoint
     double Det() const;             // determinant
     SqMatrix Min(unsigned int row, unsigned int col) const;     // minor
     double Cof(unsigned int row, unsigned int col) const;       // cofactor
     SqMatrix Inv() const;           // inverse
     // ...
};


Hmm...I've seen people using like double**, is this better as you can move the whole thing to the heap?
Last edited on
It's difficult to say what's best way for it without seeing class definitions. But first way and third way don't seem reasonable as you said. :)
Last edited on
Another possibility is to make the dimension of the matrix part of the type using templates, and then implement all of the operators as friend functions.

1
2
3
4
5
6
template< size_t M, size_t N >
class Matrix {
   friend template< size_t M >
   double determinant( const Matrix<M,M>& mat )
      { /* stuff */ }
};


The advantage to this is that you no longer need to specialize square matrices; the friend functions are callable only with square matrices to begin with. The disadvantage is that the dimensions of the matrices have to be known at compile time rather than runtime.

Otherwise, deriving from Matrix makes sense.

2. Overload = to convert an object between Matrix and SqMatrix

this is better way i think.
Because you don't have any extra variables in SqMatrix class , so it's easy to convert .
@jsmith
the dimensions of the matrices have to be known at compile time rather than runtime

Is this because instantiation is needed to generated the code?
You'd instantiate it by

 
Matrix< 4, 3 > m;


for instance, and the template parameters, as with all templates, have to be resolvable at compile time. In this case, since the template parameters are integers instead of types, this means that the matrix dimensions have to be compile-time constants.
You could provide virtual functions that provide addition, subtraction and so on, then provide exterrnal operators +, - and so on that call the respective virtual methods.

The advantage? You'd be able to define specialised operations for specialised classes derived from Matrix, and you'd still be able to use the operator syntax.
Thanks for the replies :)
My aim is to keep the code simple for the users (say, users do not have to know about the convertion to Matrix
when they + SqMatrixs up)

@kbw
How do you provide external operators, can you give me an example?
I'm still new...:P
Last edited on
kbw is referring to writing friend functions I believe. The difference is that there is no implicit "this" pointer in friend functions, so for example you would declare operator+ as follows:

1
2
3
4
class Matrix {
    // Note two parameters now, not one.
    friend Matrix operator+( const Matrix& m1, const Matrix& m2 );
};


Note that your operator+, operator-, and operator* functions should all be declared const.

I see.
Does is mean that the friend operator have to determine whether the references being passed in are SqMatrixs
or not, so as to call the corresponding methods?
(Oh, the virtual methods should know when I call them in the friend because I'm passing by ref, right?)
Last edited on
Yes, the virtual methods should ensure you call the operation on the right class.
Topic archived. No new replies allowed.