Thread: Inheritance: assign base class to derived class

  1. #1
    Registered User
    Join Date
    Oct 2006
    Posts
    250

    Inheritance: assign base class to derived class

    I have defined a class Matrix, which represents a generic m x n matrix. I derive from this class a specialized Matrix3D class, which represents a 3 x 3 matrix. I run into problems when I try to use the functionality of the base class with my derived class. Specifically my base class contains a member that computes the transpose of the matrix and returns it as a new matrix:

    Code:
    class Matrix
    {
    	public:
    		Matrix(int m, int n);
    		Matrix transpose();
    };
    Now when I try to apply this member to my derived class, i.e.

    Code:
    class Matrix3D: public Matrix
    {
    	public:
    		Matrix3D(): Matrix(3, 3);
    };
    
    Matrix3D M;
    
    Matrix3D N = M.transpose();
    This results in a compiler error "error: conversion from 'Matrix' to non-scalar type 'Matrix3D' requested". Now this may be explained by a derived class not knowing exactly what the base class looks like as the private members of the base class are hidden from it and it can therefore not get a base type assigned.
    The solution to my problem would be as simple as reimplementing the transpose() member in the Matrix3D class, but this seems to defeat the purpose of inheriting from this class. So how would I solve this problem in a 'correct' manner?

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    hmm... is there even a need for Matrix3D in the first place? Anyway, one simple solution is to have transpose() return void, and change it to transpose the object itself. Then, you can write:
    Code:
    Matrix3D M;
    
    Matrix3D N(M);
    
    N.transpose();
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    hmm... is there even a need for Matrix3D in the first place?
    Strictly taken, no. However, I am using 3 x 3 matrixes for the most part, and some irregular matrices for the other. Everytime I have a Matrix object inside my class, I need a dedicated constructor call to resize the matrix so it can contain 3 x 3 elements, which is tedious. Plus, having a dedicated Matrix3D class serves clarity, I can immediately see and be certain that this matrix contains 3 x 3 elements with a minimum of effort.

    Anyway, one simple solution is to have transpose() return void, and change it to transpose the object itself.
    Though of that myself, but that means that I change the contents of the matrix which is not what I want. Therefore I have to create a new matrix manually, which is a possibility, but is tedious again. Consider the case where I use this matrix in a larger expression. I would have to create a separate statement in which to create the transpose matrix, which does not serve clarity IMO.

  4. #4
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    1) A derived type can be assigned to a variable of type base. That's because a base variable can be used to call all public functions and member variables in the base class. Since a derived object has all those same functions and member variables as the base class, calling those functions and member variables for a derived object that is masquerading as a base object will succeed.

    2) However, you cannot assign a base type to a derived class type. That's because a variable of type derived can be used to call any public function or member variable in the derived class. But if a base object is hiding in a variable of type derived, calling some functions and member variables in the derived class will not succeed because none of the functionality added by the derived class will be present in a base object.

    The bottom line is: transpose() returns a Matrix object, so your variable must be of type Matrix. Why would you expect that a Matrix object returned by transpose() to have the functionality of a Matrix3D object? You can't squeeze lemons and get orange juice.
    Last edited by 7stud; 01-22-2007 at 12:25 PM.

  5. #5
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    Since all you want to do is call a function in the base class, a solution is to assign your derived object to a base type variable, and then use the base variable to call transpose().
    Let's say I have the following: Matrix3D A = B * (C * B.transpose()); B and C are both Matrix3D. If I were to assign the B.transpose() to a base type, A, B, and C would be base types and my problem would not exist. However, I find the use of a Matrix class undesirable for reasons previously stated. Is there any possibility to go about this another way, except for reimplementing transpose in the Matrix3D class?

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    To be honest, I am not sure what is the best way to go about doing this. One option is to make use of covariant return types with a helper function:
    Code:
    class Matrix
    {
    public:
        Matrix(int m, int n) {}
        virtual ~Matrix() {}
    
        virtual Matrix* transpose() const
        {
            Matrix* temp = new Matrix(*this);
            temp->transposeSelf();
            return temp;
        }
    protected:
        void transposeSelf()
        {
            // change contents of object
        }
    };
    
    class Matrix3D : public Matrix
    {
    public:
        Matrix3D() : Matrix(3, 3) {}
    
        virtual Matrix3D* transpose() const
        {
            Matrix3D* temp = new Matrix3D(*this);
            temp->transposeSelf();
            return temp;
        }
    };
    
    int main()
    {
        Matrix3D M;
    
        Matrix3D* N = M.transpose();
    
        delete N;
    }
    Another option is to hide the base class' transpose() while using a helper function:
    Code:
    class Matrix
    {
    public:
        Matrix(int m, int n) {}
        virtual ~Matrix() {}
    
        Matrix transpose() const
        {
            Matrix temp(*this);
            temp.transposeSelf();
            return temp;
        }
    protected:
        void transposeSelf()
        {
            // change contents of object
        }
    };
    
    class Matrix3D : public Matrix
    {
    public:
        Matrix3D() : Matrix(3, 3) {}
    
        Matrix3D transpose() const
        {
            Matrix3D temp(*this);
            temp.transposeSelf();
            return temp;
        }
    
        // Matrix::transpose() does not exist in this scope.
    };
    
    int main()
    {
        Matrix3D M;
    
        Matrix3D N = M.transpose();
    }
    Note that for the latter transpose() is not polymorphic.

    As for whether these two options I propose are good solutions at all, I do not really know.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    That's what virtual functions are for: you store everything in base class variables and then let polymorphism do its thing(i.e. call different transpose() functions depending on the type of the object stored in the base class variable). In your case, it sounds like you might want to make the transpose() function a pure virtual function(i.e. no definition in the base class).

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,412
    In your case, it sounds like you might want to make the transpose() function a pure virtual function(i.e. no definition in the base class).
    Apparently not. From what I understand, Matrix3D's transpose() is supposed to work in exactly the same way as Matrix's transpose(), except that it should return a Matrix3D instead of a Matrix.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User
    Join Date
    Sep 2001
    Posts
    752
    Define explicit constructors for Matrix3D from Matrix.

    Code:
    #include <iostream>
    #include <assert.h>
    
    class matrix {
       public:
          matrix(int i) : priv_i(i) {}
          int get_i() const { return priv_i; }
       private:
          int priv_i;
    };
    
    class matrix_3 : public matrix {
       public:
          matrix_3() : matrix(3) {}
    
          // We can construct this from a normal matrix
          matrix_3(matrix const & M) : matrix(M) {
             assert(M.get_i() == 3);
          }
    
          // We can assign a normal matrix to this
          matrix_3 const & operator= (matrix const & M) {
             assert(M.get_i() == 3);
             
             // If you know a better way to call the base's copy operator, let me
             // know how
             matrix::operator=(M);
          }
    };
    
    int main (void) {
       matrix_3 foo;
       std::cout << foo.get_i() << '\n';
    
       matrix bar = foo;
       std::cout << bar.get_i() << '\n';
    
       matrix_3 weeble = bar;
       std::cout << weeble.get_i() << '\n';
    
       return 0;
    }
    EDIT: Please don't take the code too literally. It doesn't really implement matrices, but it shows the technique you are asking for, and it compiles.
    Last edited by QuestionC; 01-22-2007 at 01:11 PM.
    Callou collei we'll code the way
    Of prime numbers and pings!

  10. #10
    Registered User
    Join Date
    Sep 2001
    Posts
    752
    Incidentally, I'd like to point out that if you really do spend most of your time using 3x3 matrices, then from an efficiency standpoint, it makes a lot of sense to just define a Matrix3D class which does not derive from Matrix, which means defining a new transpose() anyhow.

    Your Matrix class carries with it a good bit of RTTI (Runtime Type Information) baggage that the Matrix3D class has no need for.
    Callou collei we'll code the way
    Of prime numbers and pings!

  11. #11
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    Quote Originally Posted by QuestionC
    Incidentally, I'd like to point out that if you really do spend most of your time using 3x3 matrices, then from an efficiency standpoint, it makes a lot of sense to just define a Matrix3D class which does not derive from Matrix, which means defining a new transpose() anyhow.

    Your Matrix class carries with it a good bit of RTTI (Runtime Type Information) baggage that the Matrix3D class has no need for.
    You are absolutely right there and it is something I might do in the future. At the moment however, I have a flawlessly working Matrix class, which I would like to keep using. Incidentally it also teaches me a lot about inheritance. I am pursuing this topic somewhat for its own sake, to get to know the language better.

  12. #12
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    QuestionC, actually I tried overloading operator=, but that didn't work. Taking a look at your code it suddenly seems that I need a constructor taking a Matrix argument and *not* an overloaded operator=.

    Simply defining the Matrix3D class as follows actually works:

    Code:
    class Matrix3D : public Matrix
    {
    public:
    	Matrix3D(): Matrix(3, 3){}
    	Matrix3D(const Matrix &M): Matrix(M){}
    };
    Now just to figure out why this works and why it works this way. Could anyone perchance explain why I need a constructor instead of an operator= overload?

  13. #13
    Registered User
    Join Date
    Sep 2001
    Posts
    752
    Well... you need the constructor if it's an initialization, and the assignment operator if it's an assignment...
    So you generally need both

    Code:
    main() {
      Matrix3D foo; // Called Matrix3D()
      Matrix bar(3,3); // Called Matrix(int, int)
      Matrix3D weeble = bar; // Called Matrix3D(Matrix const &)
    
      foo = bar; // Called Matrix3D::operator=(Matrix const &)
    }
    Quote Originally Posted by MWAAAHAAA
    QuestionC, actually I tried overloading operator=, but that didn't work. Taking a look at your code it suddenly seems that I need a constructor taking a Matrix argument and *not* an overloaded operator=.

    Simply defining the Matrix3D class as follows actually works:

    Code:
    class Matrix3D : public Matrix
    {
    public:
    	Matrix3D(): Matrix(3, 3){}
    	Matrix3D(const Matrix &M): Matrix(M){}
    };
    Now just to figure out why this works and why it works this way. Could anyone perchance explain why I need a constructor instead of an operator= overload?
    Callou collei we'll code the way
    Of prime numbers and pings!

  14. #14
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    Well... you need the constructor if it's an initialization, and the assignment operator if it's an assignment...
    It all boils down to the following:

    Code:
    #include <cstdio>
    
    class Base
    {
    public:
    	Base transpose(){printf("Base::transpose()\n");};
    };
    
    class Derived : public Base
    {
    public:
    	Derived(){printf("Derived()\n");}
    	Derived(const Base &b): Base(b){printf("Derived(const Base &b)\n");}
    	Derived &operator=(const Base &b){printf("Derived::operator=(const Base &b)\n"); Base::operator=(b); return *this;}
    };
    
    int
    main()
    {
    	Derived d;
    	Derived dT = d.transpose();
    }
    This code compiles and runs with the following output:
    Code:
    Derived()
    Base::transpose()
    Derived(const Base &b)
    The only call being made in the Derived (thus, Matrix3D) object, is the constructor call with the Base (Matrix) object as an argument. Even though an assignment is taking place. I can remove the Derived::operator= overload, but that has no effect on the program as apparently only a constructor call is made.

    Removing the Derived::Derived(const Base &b) constructor member, will result in a program that does not compile even when an operator= overload is present.

    I have verified this in my Matrix and Matrix3D classes. I actually get the correct results.

    Is this the way it is supposed to be? Does this behavior vary from compiler to compiler?

    EDIT: how do I remove the emoticons?
    Last edited by MWAAAHAAA; 01-22-2007 at 07:21 PM.

  15. #15
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    EDIT: how do I remove the emoticons?
    Go Advanced/click on "Disable Smilies in text" located below the textarea you type in.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Two conceptual questions
    By AntiScience in forum C++ Programming
    Replies: 3
    Last Post: 11-01-2007, 11:36 AM
  2. Inheritance - Problem calling methods
    By rusty_turkey in forum C++ Programming
    Replies: 5
    Last Post: 05-31-2007, 04:36 AM
  3. simple question
    By SuperNewbie in forum C++ Programming
    Replies: 2
    Last Post: 01-24-2005, 03:03 PM
  4. Replies: 1
    Last Post: 11-27-2001, 01:07 PM
  5. Exporting Object Hierarchies from a DLL
    By andy668 in forum C++ Programming
    Replies: 0
    Last Post: 10-20-2001, 01:26 PM