Click to See Complete Forum and Search --> : Inheritence Constructor Question


sjaguar13
December 2nd, 2002, 11:37 PM
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?

Ejaz
December 3rd, 2002, 12:14 AM
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.

Gabriel Fleseriu
December 3rd, 2002, 01:52 AM
Is this what you are looking for?

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){}
/*...*/
};

PaulWendt
December 3rd, 2002, 06:29 AM
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

sjaguar13
December 5th, 2002, 01:32 AM
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.

Gabriel Fleseriu
December 5th, 2002, 04:26 AM
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:

//...
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.

Graham
December 5th, 2002, 10:42 AM
Not to mention that there are six places in the constructor where it creates a pointless temporary Quadrilateral object.

sjaguar13
December 5th, 2002, 10:57 AM
So if I have these classes derived from the quadrilateral class, how should I check to make sure the shapes are valid?

Gabriel Fleseriu
December 5th, 2002, 11:16 AM
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.

jfaust
December 5th, 2002, 12:01 PM
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.


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

sjaguar13
December 5th, 2002, 12:07 PM
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?

jfaust
December 5th, 2002, 12:21 PM
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

Graham
December 5th, 2002, 04:23 PM
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:

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.