derivable classes

M

mem

Concrete classes are ones that can be instantiated directly, as long as it
doesn't have pure virtual function.

However, people suggest that "Don't derive from concrete classes."

Does this mean that either make it abstract base class for it to be
derivable or keep them as "concrete" not to be derived at all? Then what's
the purpose of "concrete classes" of ordinary virtual function(s)?

Thanks!
 
P

Phlip

mem said:
Concrete classes are ones that can be instantiated directly, as long as it
doesn't have pure virtual function.

However, people suggest that "Don't derive from concrete classes."

Does this mean that either make it abstract base class for it to be
derivable or keep them as "concrete" not to be derived at all? Then what's
the purpose of "concrete classes" of ordinary virtual function(s)?

OO is not about classes and objects; it is about messages and methods.

The odds you will derive from a concrete class are low. But you must divorce
the idea of "a class without pure virtual functions" from that of a "class
that should be instantiated". Pragmatically, some classes that should be
instantiated will also be inherited.

Your design should duplicate no behavior. This means, in practice, that
interfaces tend to migrate up an inheritance graph, and implementation
migrates down, into non-pure functions. But the rule against duplication is
more important than the rule "Don't derive from concrete classes".

A related rule, "Don't derive unless you then override a method," is more
important. Read the /Effective C++/ books for a good explanation.
 
J

JKop

class Rectangle
{

};


class Square : Rectangle
{


};



Above is an instance in which a Concrete Class has been inherited from.
There's nothing wrong with it because:

A) A Rectangle is a fully-fledged thing, you can describe one and talk about
it very specificly. You can simply have a rectangle, no frills.

B) A Square is a Rectangle.



The reason people may have come up with the rule "Don't inherit from a
concrete class", I shall explain as follows:


Class Vehicle
{


};


Class Car : public Vehicle
{


};

Let's assume that Vehicle is a concrete class, with no virtual functions,
pure or otherwise. Thus, it's a concrete class. Thus, you can create an
instance of it:

Vehicle FunkyVehicle;

And similarly you can create an object Car:

Car FunkyCar;


But what you'll see here is that a Vehicle is NOT a fully-fledged object!
Try describe a Vehicle. How does a Vehicle work? How many wheels does a
vehicle have? Does it run on petrol or deisel. You can't just simply have a
Vehicle! But you CAN just simply have a Rectangle.



In summation:

You're human, you have a brain - decide for yourself if what you're doing is
good and proper.



-JKop
 
H

Howard

JKop said:
class Rectangle
{

};


class Square : Rectangle
{


};




B) A Square is a Rectangle.

Actually, this is one of the best examples of what is *not* a good example
for inheritance. :)

In mathematical terms, sure, we all know that a square is a special case of
a rectangle, where all sides are of equal length. But that's simply adding
a *restriction* on the rectangle in order to define the square, not
providing a new version of the rectangle with special behavior or added
properties, which is what inheritance is more about.

To illustrate the problem, think about what a rectangle can have as
properties: length and width. And either of those can potentially be
changed, independently! It makes perfect sense for a rectangle to have
SetWidth() and SetHeight() functions to set those properties. But calling
such a function in the square class would be disastrous! Sure, you could
override those to set both width and height whenever either is called, but
that would be *really* confusing to someone using your objects, (especially
if using polymorphism through pointers to the base class).

I believe Meyers covers this exact case in one of his books.

Sorry if this muddles things for the OP, but I think we need to give a
better simple example of inheritance to newbies, something like "A
SalariedEmployee is an Employee".

-Howard
 
J

jeffc

mem said:
Concrete classes are ones that can be instantiated directly, as long as it
doesn't have pure virtual function.

That's redundant. If it has a pure virtual function, it's abstract, not
concrete.
However, people suggest that "Don't derive from concrete classes."

Does this mean that either make it abstract base class for it to be
derivable or keep them as "concrete" not to be derived at all? Then what's
the purpose of "concrete classes" of ordinary virtual function(s)?

Yes, I've heard that guideline and generally I don't follow it and I've
never run into problems. I think Meyers wrote about this in "Effective C++"
or "More Effective C++". I don't understand your last sentence. But I
think you can derive from concrete classes without running into trouble.
 
H

Howard

mem said:
Concrete classes are ones that can be instantiated directly, as long as it
doesn't have pure virtual function.

However, people suggest that "Don't derive from concrete classes."

Does this mean that either make it abstract base class for it to be
derivable or keep them as "concrete" not to be derived at all? Then what's
the purpose of "concrete classes" of ordinary virtual function(s)?

Thanks!

I'm unsure what a "concrete" class is. Is it one that is "intended" to be
instantiated from?

If so, then in real life you are bound to have cases where a class that is
intended to be instantiated may also end up being inherited from.

In that case, you'll want to be sure to look at what functions you make
virtual, because someone deriving from you class will make decisions about
what to override based upon the default behavior, and if you later change
that default behavior, then they will need to revisit the idea of overriding
that behavior again.

This, I think, is the basic reason given to avoid deriving from such
classes, and to instead make an abstract class from which both you and
others can derive. That way, if you change your class' behavior, it won't
break theirs.

If by "concrete" you mean that no virtual functions exist at all, then that
class is not "intended" to be derived from, and obviously shouldn't be.
(But I doubt that's the meaning, since it's too obvious such a class is not
intended as a base class.)

-Howard
 
J

jeffc

Howard said:
I'm unsure what a "concrete" class is. Is it one that is "intended" to be
instantiated from?

Yes. It's a normal class.
If so, then in real life you are bound to have cases where a class that is
intended to be instantiated may also end up being inherited from.

This can only happen if a programmer makes it so. The suggestion that the
OP was asking about was that programmers do not make it so.
If by "concrete" you mean that no virtual functions exist at all,...
No.

then that
class is not "intended" to be derived from, and obviously shouldn't be.

No, that's not necessarily true (but it could be.) If a designer writes a
class with some functions that are not virtual, he might still intend for
that class to be derived from, but is saying those functions should not be
overridden.
 
J

jeffc

Howard said:
Actually, this is one of the best examples of what is *not* a good example
for inheritance. :) In mathematical terms, sure, we all know that a square is a special case of
a rectangle, where all sides are of equal length. But that's simply adding
a *restriction* on the rectangle in order to define the square, not
providing a new version of the rectangle with special behavior or added
properties, which is what inheritance is more about.

Howard, good point. However, this might make more sense

class Square
class Rectangle: public Square

A lot of this sort of thing is context dependent. It depends on which
constraints you are going to be modeling, and what you want your objects to
be able to do. If all you're going to do is ask things like area,
perimeter, and corner to corner distance, then this design works OK. The
invocation of the base class (Square) constructor from the Rectangle
constructor looks a little goofy, but it works. If different things are
important in your design, then this won't work. For example, if you're
writing polymorphic code to put square shapes into square holes, then a
rectangle isn't substitutable for a square.

But these design problems are nothing new. You always model what's
important. If you're modeling the animal world, and you get down to the
point in your hierarchy where you split egg layers from mammals, then what
do you do with a duck billed platypus? It depends on whether egg laying is
important to you, or if hair and warm-bloodedness are important to you.

class Square
{
public:
Square(int);
virtual int area();
private:
side;
};
class Rectangle
{
public:
Rectangle(int, int);
int area();
private:
side2;
};
Square::Square(int val) : side(val) {}
int Square::area()
{ return side*2; }

Rectangle::Rectangle(int val1, int val2) : Square(val1), side2(val2) {}
int Rectangle::area()
{ return side * side2; }
 
J

jeffc

jeffc said:
But these design problems are nothing new. You always model what's
important. If you're modeling the animal world, and you get down to the
point in your hierarchy where you split egg layers from mammals, then what
do you do with a duck billed platypus? It depends on whether egg laying is
important to you, or if hair and warm-bloodedness are important to you.

By the way, this problem doesn't just exist for computer programmers. It
also exists for zoologists. They didn't know where to put the duck billed
platypus either. There are often no perfect solutions. They decided to put
it with the mammals. They decided that being hairy and warm-blooded in this
case were more important distinctions than laying eggs. But if they wanted
they could have created a completely different split off the hierarchy and
made a special case for this animal.
 
J

JKop

jeffc posted:
class Square
class Rectangle: public Square


Let's say I'm a painter and I paint portraits. I demand that all my
paintings be upon a square canvas. The customer supplies the canvas:


PaintPortrait(Square* Canvas);


But, if a Rectangle IS_A square, as you've specified, then the customer
could supply the painter with a rectangle:


int main(void)
{
Rectangle Canvas;

PaintPortrait(&Canvas);
}



Then again, you may simply just ignore the IS_A principle. It's your code
after all, and if it functions correctly, then it functions correctly.


-JKop
 
J

jeffc

JKop said:
jeffc posted:


Let's say I'm a painter and I paint portraits. I demand that all my
paintings be upon a square canvas. The customer supplies the canvas:

PaintPortrait(Square* Canvas);

But, if a Rectangle IS_A square, as you've specified, then the customer
could supply the painter with a rectangle:

At what point did you stop reading my post? Right before this?

"If different things are important in your design, then this won't work.
For example, if you're
writing polymorphic code to put square shapes into square holes, then a
rectangle isn't substitutable for a square."

You do understand you just described the exact situation I already
mentioned, right?
 
P

puppet_sock

Howard said:
Actually, this is one of the best examples of what is *not* a good example
for inheritance. :)

It's the classic "A's can X, B is an A, B can't X" trap.

All birds gotta fly,
Ostrich is a bird,
Ostrich can't fly.

All rectangles can have two different side lengths,
Square is a rectangle,
Square cannot have two different side lengths.
I believe Meyers covers this exact case in one of his books.

It's also covered fairly well in "C++ FAQs (2nd Edition)"
by Marshall P. Cline, Greg A. Lomow, and Mike Girou.

Basically, you have to give up one (or more)
of the three lines of the trap. And you detect the trap
in advance by insisting that derived classes can substitute
for the parent class. So, ask "Can I use a square every
time I need a rectangle?" If, in the context of the case,
the answer is no, then it is likely to be a bad choice for
public derivation.

Or, "Can I use an ostrich every time a bird is wanted?"
The context may say no, or it may say yes. For example,
when ostrich is required to fly, you could adjust things
so that somebody buys an airline ticket for the bird.
Or you could redefine "fly" in some other way such that
ostriches can fly. Or you could change the middle line,
such that "bird" and "ostrich" each derived from some
other entity, say "protoBird" that did not have any
characteristics about flying. Or you could relax the
requirement that birds must fly, say, by allowing the
call to "Fly" to throw an exception. In any case, you
have to drop one or more lines from the three of the trap.

But get the FAQ book as the coverage there is much better
than I've managed here.
Socks
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,169
Messages
2,570,919
Members
47,460
Latest member
eibafima

Latest Threads

Top