"In object-oriented programming (OOP), inheritance is a way to reuse code of existing objects..."
http://en.wikipedia....ed_programming)
I know Wiki is not the best source, but...
Yes, it is
a way, but one of many ways. This particular type of inheritance is called "
Implementation inheritance" -- where the subclass is taking parts of it's implementation from the base. The other type is "
Interface inheritance" -- where the base only contains pure virtual methods, so only an interface is inherited. Often, inheriting a base class involves both types of inheritance occurring at the same time -- e.g. this is what your example does. You need to be aware that in your case "public Shape" is actually doing two distinct things, i.e. inheriting the implementation details, and inheriting the interface.
Other languages try to make this more explicit -- e.g. Java uses
extends for implementation inheritance, and
implements for interface inheritance.
If you keep scrolling down that wiki link, it explains that "
Implementation inheritance is the mechanism whereby a subclass re-uses code in a base class. ... In most quarters, class inheritance for the sole purpose of code reuse has fallen out of favor".
If you search for "
inheritance vs composition" or "
composition vs inheritance", you'll find a lot of material explaining why composition is preferable to implementation inheritance.
Now, in Bregma's example, I don't see the 'trouble', since the Circle class would include the necessary variables to calculate it's own area. Yet, the Circle class needs the x, y, width and height variables. I believe it's not in-logic to say the a circle has a width, for example.
The circle requires only a 'radius' value, so either width or height is wasted. The fact that you've got a useless variable in your Circle class is a '
code smell' indicating that the foundations of this design are flawed.
Using interface inheritance and composition, you could use:
class Shape { public: virtual int Area() const = 0; }//interface
class Circle : public Shape { int radius; public: int Area() const { return pi*radius*radius; } };//interface inheritance
struct Bounds { int width, height; int Area() const { return width * height; } };
class Rectangle : public Shape { Bounds b; public: int Area() const { return b.Area(); } };//interface inheritance and compositionNow if you want every Shape to have an x/y position, you could change the above to:
struct Pos { int x, y; };
class Shape { public: virtual int Area() const = 0; virtual Pos Position() const = 0; }//interface
class Circle : public Shape { Pos pos; public: int Pos Position() const { return pos; } ... }...but if every derived class is doing the same thing, you can instead use implementation inheritance for those cases, e.g.
struct Pos { int x, y; };
class Shape { public: virtual int Area() const = 0; Pos Position() const { return pos; } protected: Pos pos; }//interface + base implementationSo, in those specific cases (where all derived class must do the same thing), this is ok. However, this should be quite rare in good designs -- inheritance isn't used very often (
some OOP classes make it seem like you should be using inheritance every day, but it should actually be rare), especially with large inheritance trees. So given that -- if you're not often using inheritance, then you're not often going to run into the situation where lots of derived classes are doing the same thing, so you won't often have to use implementation inheritance.
Back to the circle -- the problem here is that when the base class defined width/height, it places no
invariants on those variables. As far as the base is concerned, those variables can have any value. However, the circle does have an invariant -- the width and height must always be equal (otherwise it would be an ellipse, not a circle).
In practice, you can just deal with this™, but in theory this is a clear violation of OO principles. A derived class cannot add new invariants to the base class's functionality, otherwise you're violating
substitutability.
This becomes a much bigger issue when the base class does have invariants, but the derived class has different invariants. Private allows a class designer to completely encapsulate the logic behind maintaining a class's invariants within the logic for that class -- as long as everything is private, if you get an assertion failure indicating an invariant has been violated, then the only place you need to look is at the code for that class. If the members were public, you'd instead have to possibly look at the whole code-base. If the members were protected, you'd instead have to look at all of the code for the class, and the code of any class that inherits from it. Where possible, it's best to use private to tightly contain your class-invariant logic in as small a section of the code-base as possible.
We can also consider here that OOP and OO Design are two different things. You can use an ad-hoc design, and implement it using the tools that OOP languages give you. Or, you can create a design using OO theory, and implement it using a non-OOP language.
i.e. just because you're writing code in an OOP language, that doesn't actually mean that you're using OO theory in your decisions -- lots of "bad" OO code is simply code that's written using OOP but ignores the theory behind it.
Also, as always, you can't properly critique a class, such as Shape, without also knowing what exactly is the problem that it's supposed to solve. In a vacuum with no particular problems in mind, then there's infinite different ways to describe a shape, and none are better than any other. It's only once you're given a specific problem that we can really look at specific ways to implement a solution.
e.g. if the purpose of the program was to simply calculate area values, I'd probably go with:
struct Rect { int w,h; };
struct Circle { int r; };
void CalcAreas( std::vector<int>& out, const std::vector<Rect>& in );
void CalcAreas( std::vector<int>& out, const std::vector<Circle>& in );
Edited by Hodgman, 10 November 2012 - 01:05 AM.