CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 13 of 13
  1. #1
    Join Date
    Jun 2002
    Posts
    114

    Inheritence Constructor Question

    I have a bunch of inheritent classes that have error checking. If the base class checks for the common stuff, does the child class automatically call that constructor, or would I have to type it in? So if I had a base class check of negative numbers and the child class can't have any negative or odd numbers, how could I get the Child() constuctor to use the Base() constructor so I don't have to type the code out again?

  2. #2
    Ejaz's Avatar
    Ejaz is offline Elite Member Power Poster
    Join Date
    Jul 2002
    Location
    Lahore, Pakistan
    Posts
    4,211
    When u inherite classes from other classes, (Suppose B is base class and D is drived class), then when u create an instance of D, first the ctor of B will execute and after that, ctor of D will execute.

    Any code in these scope will be execute in their respective turns. Similarly, when the object will be destroyed, first the ~ctor of D will execute and after that ~ctor of B will execute in turns.

    U can cosider it a dimond formation. So, if u have written some code in ctor of B, then u won't have to write it again in the ctor of D.

    If I'm not able to clarify ur doubts, then kindly reply with some sample code, so that, it will make very easy to understand the exact problem.

    Regards,

    Ejaz.
    Last edited by Ejaz; December 3rd, 2002 at 02:12 AM.

  3. #3
    Join Date
    Jun 2001
    Location
    Switzerland
    Posts
    4,443
    Is this what you are looking for?
    Code:
    class base
    {
    public:
    	base():val_(0){};
    	base(int val){if(val < 0) val_ = 0; else val_ = val;}
    private:
    	int val_;
    };
    
    class derived:public base
    {
    public:
    	derived(){}
    	derived(int val):base(val){}
    	/*...*/
    };
    Gabriel, CodeGuru moderator

    Forever trusting who we are
    And nothing else matters
    - Metallica

    Learn about the advantages of std::vector.

  4. #4
    Join Date
    May 2000
    Location
    Phoenix, AZ [USA]
    Posts
    1,347
    Basically, if you have a derived class, the base class's constructor
    will always be called. If you never call the constructor, the default
    constructor will be called. If no constructor at all exists, a default
    constructor will be created for you by the compiler. If there exists
    a constructor that has arguments, then a compile error will be
    received. As Gabriel mentioned, you can explicitly state which
    constructor to call [if the default constructor doesn't provide the
    proper behavior]. Some constructor [of some sort] WILL be
    called though.

    --Paul

  5. #5
    Join Date
    Jun 2002
    Posts
    114
    Code:
    class Quadrilateral
    {
    public:
    	Quadrilateral(int x1=1, int y1=1, int x2=1, int y2=2, int x3=2, int y3=2, int x4=2, int y4=1);
    	int getX(int corner);
    	int getY(int corner);
    	void setX(int corner, int x);
    	void setY(int corner, int y);
    	
    private:
    	int m_x[5], m_y[5];
    
    };
    
    
    class Trapezoid : public Quadrilateral
    {
    public:
    	Trapezoid(int x1=1, int y1=1, int x2=1, int y2=2, int x3=2, int y3=2, int x4=2, int y4=1);
    };
    
    
    class Parallelogram : public Trapezoid
    {
    public:
    	Parallelogram(int x1=1, int y1=1, int x2=1, int y2=2, int x3=2, int y3=2, int x4=2, int y4=1);
    };
    
    
    class Rectangle : public Parallelogram
    {
    public:
    	Rectangle(int x1=1, int y1=1, int x2=1, int y2=2, int x3=2, int y3=2, int x4=2, int y4=1);
    };
    
    
    class Square : public Rectangle
    {
    public:
    	Square(int x1=1, int y1=1, int x2=1, int y2=2, int x3=2, int y3=2, int x4=2, int y4=1);
    };
    
    
    Quadrilateral::Quadrilateral(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4)
    {
    	if(x1==x2)
    	{
    		if(x1==x3||x1==x4)
    		{
    			Quadrilateral();
    		}
    	}
    	else if(x1==x3 && x1==x4)
    	{
    		Quadrilateral();
    	}
    	else if(x2==x3 && x2==x4)
    	{
    		Quadrilateral();
    	}
    	else if(y1==y2)
    	{
    		if(y1==y3||y1==y4)
    		{
    			Quadrilateral();
    		}
    	}
    	else if(y1==y3 && y1==y4)
    	{
    		Quadrilateral();
    	}
    	else if(y2==y3 && y2--y4)
    	{
    		Quadrilateral();
    	}
    	else
    	{
    		m_x[0] = x1;
    		m_y[0] = y1;
    		m_x[1] = x2;
    		m_y[1] = y2;
    		m_x[2] = x3;
    		m_y[2] = y3;
    		m_x[3] = x4;
    		m_y[3] = y4;
    	}
    	
    
    }
    That's what I got. The quadrilateral constructor checks to make sure the shape has four sides. The trapezoid checks to see if it has one parallel set of sides. The parallelogram checks to make sure 2 sets are parallel. The rectangle checks to make sure they are perpendicular. The square checks to make sure the sides are even.

    When checking the square, if the other constrcutors come out false, it changes the numbers to a square and keeps going. How do I get the other constructors called when I declare a square? I don't want to have to write the same code over and over, even though some parts are the same the way I have it now.

  6. #6
    Join Date
    Jun 2001
    Location
    Switzerland
    Posts
    4,443
    That is not good design, if you ask me. In my oppinion, if the ctor of a class is called with wrong parameters, it should fail (i.e. throw an exception). If the ctor "bends" the parameters so they fit, subtle bugs can creep in your code. Example:
    Code:
    //...
    Square s(x1, y1, x2, y2, x4, y3, x4, y4);
    Suppose that the parameter I marked red is simply a typo. In this case, it's best if the program fails consistently, instead of doing something behind the scenes so the program failes later or behaves unexpectedly.

    Secondly, I'm not sure that public inheritance is correct here. Public inheritance models an IS-A relationship. This means that you can use the derived class wherever you can use the base class with identical effects. Now, of course that a square is a rectangle, mathematically speaking, but from the view of the program that might not be true. Suppose Rectangle has two member functions SetHeight() and SetWidth(). Of course, Square will overrid these functions, so both set the height and the width, as they must be equal for a square. Thsi leads to the fact that calling Squares SetWidth() has not the same effect as calling Rectangles SetWidth(), because it also sets the height. The IS-A relationship doesn't hold any more.
    Gabriel, CodeGuru moderator

    Forever trusting who we are
    And nothing else matters
    - Metallica

    Learn about the advantages of std::vector.

  7. #7
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    Not to mention that there are six places in the constructor where it creates a pointless temporary Quadrilateral object.
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


  8. #8
    Join Date
    Jun 2002
    Posts
    114
    So if I have these classes derived from the quadrilateral class, how should I check to make sure the shapes are valid?

  9. #9
    Join Date
    Jun 2001
    Location
    Switzerland
    Posts
    4,443
    If I were to use inheritance, I would define an abstract base class, say Shape, that would provide the common operations for all shapes (e.g. Draw() ). Then I would deriev each class from Shape, and make sure that the ctors throw if called with wrong parameters.
    Gabriel, CodeGuru moderator

    Forever trusting who we are
    And nothing else matters
    - Metallica

    Learn about the advantages of std::vector.

  10. #10
    Join Date
    Mar 2002
    Location
    California
    Posts
    1,582
    Originally posted by Graham
    Not to mention that there are six places in the constructor where it creates a pointless temporary Quadrilateral object.
    What Graham is referring to, for instance, is the following.

    Code:
    else if(x1==x3 && x1==x4)
    {
         Quadrilateral();
    }
    You cannot call the default constructor from a non-default constructor this way. If there is common code that needs to be executed between the different constructors, you should create another non-virtual method to handle it, called possibly init(), and call it from each of the constructors.

    Jeff

  11. #11
    Join Date
    Jun 2002
    Posts
    114
    So the init() function checks the quadrilateral, then I call that from the trapezoid constructor, and check some other stuff. How would I call init and the trapezoid stuff for parallelogram?

  12. #12
    Join Date
    Mar 2002
    Location
    California
    Posts
    1,582
    So the init() function checks the quadrilateral, then I ...
    I don't know what you are trying to do--it's not clear from the code. All I'm saying is that your calls to Quadrilateral() do absolutely nothing except take time. It creates a temporary of type Quadrilateral and the promply destroys it.

    Your class hierarchy has some real problems, and it will always be troublesome. Inheriting square from rectangle is a classic example of something you should not do. Either Sutter or Meyer discusses it in some detail. Basically, it violates the substitution principle. You cannot use a Square everywhere that a Rectangle can be used. When you change the width of the rectangle (really a square in this case), you may be surprised to learn the the height has also changed.

    You would be far better off with a Shape class and inheriting Square and Rectangle from it.

    Jeff

  13. #13
    Join Date
    Apr 1999
    Location
    Altrincham, England
    Posts
    4,470
    Actually, I think the design can be radically improved quite simply. The basic problem is that "Quadrilateral" is doing too much work. Shift the burden, and it becomes simpler:
    Code:
    class Quadrilateral
    {
    public:
        //default ctors are fine....
    
        // BUT we want this class to be abstract
        virtual ~Quadrilateral() = 0;
    
        // accessor functions getX(), etc.
    
    protected:
        int m_x[4]; // Not 5, surely?
        int m_y[4];
    };
    
    // Got to have a definition for this...
    Quadrilateral::~Quadrilateral() {}
    
    class Trapezoid : public Quadrilateral
    {
    public:
        Trapezoid(int x1=1, int y1=1, int x2=1, int y2=2, int x3=2, int y3=2, int x4=2, int y4=1)
        {
            // Do trapezoid-specific stuff here
        }
    };
    
    class Parallelogram : public Trapezoid
    {
    public:
        Parallelogram(int x1=1, int y1=1, int x2=1, int y2=2, int x3=2, int y3=2, int x4=2, int y4=1)
        {
            // Parallelogram-specific stuff here
        }
    };
    
    // etc
    Now this uses protected members (which I'm not a fan of), but it does illustrate that there's nothing intrinsically wrong with deriving all those shapes from an abstract quadrilateral class. This a different kettle of fish from deriving a square from a rectangle or a circle from an ellipse (both of those could conceivably derive from an abstract "Conic" class).

    As an alternative (avoiding protected members) make the accessor functions pure virtual and get the indivdual quadrilaterals to define their own (private) data members. But this would just duplicate the definitions.
    Correct is better than fast. Simple is better than complex. Clear is better than cute. Safe is better than insecure.
    --
    Sutter and Alexandrescu, C++ Coding Standards

    Programs must be written for people to read, and only incidentally for machines to execute.

    --
    Harold Abelson and Gerald Jay Sussman

    The cheapest, fastest and most reliable components of a computer system are those that aren't there.
    -- Gordon Bell


Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured