Constructor Overrides

I've got a question on something that I'm not quite clear on.  Maybe somebody can help me!

If I derrive a class from another class like so:

// Plant class...
class CPlant {
public:
   CPlant(int par_leaves):leaves(par_leaves) {};
   int leaves;
};

// Tulip class with different signature on constructor...
class CTulip : public CPlant {
public:
   CTulip(bool par_hasBloomed):hasBloomed(par_hasBloomed) {};
   bool hasBloomed;
};


Whenever you instantiate a derrived class, the parent class constructor is called first... so when you derrive a class and write a constructor, it's just the same as overriding by overload, any base-class method.  Thus I should be overriding the original constructor in the derrived type, or at least, if I always expect it to be called, invoke it in the new constructor in the derrived class like so:
// Call the base class constructor I hid by writing a new constructor for Tulip.
CTulip(bool par_hasBloomed, int par_leaves) {
   CPlant::CPlant(par_leaves);
   hasBloomed = par_hasBloomed;
}


Is what I said right   I think it is, but I'm not sure...  I expect that to be the case, but I'd like to know if I'm wrong... Big Smile
Thanks!

Gus



Answer this question

Constructor Overrides

  • kunallen

     Gus Crawford wrote:

    // Plant class...
    class CPlant {
    public:
       CPlant(int par_leaves):leaves(par_leaves) {};
       int leaves;
    };

    // Tulip class with different signature on constructor...
    class CTulip : public CPlant {
    public:
       CTulip(bool par_hasBloomed):hasBloomed(par_hasBloomed) {};
       bool hasBloomed;
    };



    Will not compile because there is no default constructor for the base class.

     Gus Crawford wrote:

    // Call the base class constructor I hid by writing a new constructor for Tulip.
    CTulip(bool par_hasBloomed, int par_leaves) {
       CPlant::CPlant(par_leaves);
       hasBloomed = par_hasBloomed;
    }



    Notation must be:

    CTulip(bool par_hasBloomed, int par_leaves) 
       : CPlant::CPlant(par_leaves)
    {
       hasBloomed = par_hasBloomed;
    }


    Or can be a special static value

    CTulip(bool par_hasBloomed) 
       : CPlant::CPlant(6)
    {
       hasBloomed = par_hasBloomed;
    }


    HTH

  • Kevin Packard

    No. You make a small mistake in your initial assumptions. A derived class should provide exactly the same methods as its base class, because it's supposed to be usable in place of the base class (Liskov Substitution Principle). However, the constructor and the destructor are exceptions to this rule. That makes sense: at the point where you create the object, you know its precise type. You cannot have a substitution there. For that reason, ctors and dtors call base ctors and dtors while normal methods (those with names) don't.

    The correct syntax would be
    CTulip(bool par_hasBloomed, int par_leaves)
        : CPlant(par_leaves)
        ,
    hasBloomed ( par_hasBloomed )
    {
    }
    which also shows the better way to initialize members. Your alternative created a temporary
    object, unrelated to 'this', which would be destroyed immediately.

  • Ryan K

    The part that confuses me, is that when I change the signature of the constructor, what decides what's passed in to a base-class constructor if I don't get a chance to tell it   The sample you've shown me gives me the idea that I should be calling the base-class constructor, but it says that I've been effectively making it [the call] from the wrong place, and I should be calling it on the scope of the object.  (Which makes more sense in retrospect.)

    The last question(s) then is this:
    CTulip(bool par_hasBloomed, int par_leaves) {
       //This is the base constructor call for this object...
       CPlant(par_leaves); 
       hasBloomed = par_hasBloomed;
    }

    The CPlant constructor is called as a peer-level method right
    Is the CPlant constructor then inherited along with everything else in Plant
    If I instantiate an object of the CTulip class, with the above constructor, how many times will the CPlant constructor be called   (I assume only once...)

  • weoili

    I think I get it now...

    It's just like writing a class with NO constructors at all, the compiler will always write a default ctor for you.  And if a base class needs to be created in order to instantiate a derrived class, and I didn't provide a default constructor, (CPlant(); right ), the compiler will create and call an empty one for me.

    Richter, Salters, thanks guys... Big Smile

  • celeon

    Mian,

    Thanks for your example.  I see now that I can control the construction of the base-class by setting up the default value (which I haven't messed with since I first started using C++!  Any parameters that have a default value assigned should occur in the parameter list after parameters that have to be passed into right )
    Thanks again.

    P.S. One of my problems with this whole thing is principal.  If I define a class with ONE constructor that has a list of parameters that I consider to be 'non-negotiable' (data must be passed in), how can I enforce that signature   Can I   Or do I always have to take special care in the way that I design my data-models or sub-components of a program

  • Bidmaron

    The order is wrong. First, the base class CPlant is initialized, using the default ctor, because you did'nt provide any arguments with the ) : CPlant(args) { syntax. Then you enter the body of CTulip::CTulip, and at that point you create the temporary CPlant(par_leaves). That temporary object is a second object (in addition to your base class subobject CPlant).
    You have two objects, and two constructors are called.
    Of course, since it's a temporary, its destructor is called before the assignment to hasBloomed .

    This is needed to satisfy the two OO invariants: the number of objects you ever had is total to the number of constructors called, and the number of objects you have left is the number of ctors called minus the number of dtors called.
    (Actually, an object is not created if a ctor fails with an exception, but that's of course an exception to the rule)

    You still don't inherit constructors.

  • B-me

    Default parameters help you the save constrcutors and coding.
    Any parametrs with a default value must after all parameters without one.

    If you define a constructor for a class that needs a parameter, and this constructor ist the only one, than nobody can construct an object without using this constructor!

    Note: a default constructor is only created when you do not define a default constructor. Same for the copy constructor.



  • Abdul Abu

     Gus Crawford wrote:
    The last question(s) then is this:
    CTulip(bool par_hasBloomed, int par_leaves) {
       //This is the base constructor call for this object...
       CPlant(par_leaves); 
       hasBloomed = par_hasBloomed;
    }

    The CPlant constructor is called as a peer-level method right
    Is the CPlant constructor then inherited along with everything else in Plant
    If I instantiate an object of the CTulip class, with the above constructor, how many times will the CPlant constructor be called   (I assume only once...)


    Using the constructor this way, will call the defualt constructor of CPlant first. Than it will call the special constructor CPlant(int par_leaves) again.

  • Eindhoven

    Hi Gus
    1. Yes any parameters that have a deafualt value will always come after the compulsory parameters.

    2. If you don't specify default values for the parameters of that one constructor, then the signature is enforced by the compiler. In any violation, its a compile time error. e.g.


    class CMyClass {      CMyClass ( int a, int b, int c) {           /* Do something. */     }  };   
     


    Now consider the following calls


     CMyClass myclass = CMyClass ( );      /* Error */  CMyClass myclass = CMyClass (1);     /* Error */ 
     


    And everything else will be error excepty for something like this


     CMyClass myclass = CMyClass (1, 2, 3); /* This will compile.*/ 
     


    Hence this will compile and will be the only allowed way to create an object of CMyClass.

    I hope this helps

    Regards

  • VBRookie

    Hi Gus Crawford!

    Well its not really like that. The compiler will surely provide a parameter-less for you until you define a constructor of your own. However consider the following code.


    // Plant class...
    class CPlant {
    public:
       CPlant(int par_leaves):leaves(par_leaves) {};
       int leaves;
    };

    However, as soon as you provide a constructor of your own (doesn't matter if it is paramaterized or parameter-less), the compiler stops providing a default constructor of its own. In the above case, there is no default constructor for the class CPlant. You can achieve the behaviour of default constructor using the default value.

    // Plant class...
    class CPlant {
    public:
       CPlant(int par_leaves = 0):leaves(par_leaves) {};
       int leaves;
    };

    Now if you will pass a value to the constructor, it will consider the value passed by you. Otherwise it will use the default value which happens to be zero in this case.

    Now coming back to the question of derived class. As mentioned by msalters, the base class is initialized before the execution enters the constructor of the derived class.

    Now based on the above facts, class CTulip can have following two constructors


    CTulip(bool par_hasBloomed, int par_leaves)
        : CPlant(par_leaves)
        ,
    hasBloomed ( par_hasBloomed )
    {
    } // mentioned by lsmasters


    or the simpler one

    // Notice this requires only 1 paramater
    CTulip(bool par_hasBloomed) :
      hasBloomed ( par_hasBloomed )
    {
        /* This will always initialzie the leaves member of        base class to 0. It is not flexible and not            recommended
        */
    }


    We can use the same default paramters for the CTulip class to make it more flexible and it goes on. The real thing that matters is your requirement and then your preference, perhaps Smile

    Regards


  • Constructor Overrides