views:

296

answers:

3

I'm trying to find a nice inheritance solution in C++.

I have a Rectangle class and a Square class. The Square class can't publicly inherit from Rectangle, because it cannot completely fulfill the rectangle's requirements. For example, a Rectangle can have it's width and height each set separately, and this of course is impossible with a Square.

So, my dilemma. Square obviously will share a lot of code with Rectangle; they are quite similar.

For examlpe, if I have a function like:

bool IsPointInRectangle(const Rectangle& rect);

it should work for a square too. In fact, I have a ton of such functions.

So in making my Square class, I figured I would use private inheritance with a publicly accessible Rectangle conversion operator. So my square class looks like:

class Square : private Rectangle
{
    public:
        operator const Rectangle&() const;
};

However, when I try to pass a Square to the IsPointInRectangle function, my compiler just complains that "Rectangle is an inaccessible base" in that context. I expect it to notice the Rectangle operator and use that instead.

Is what I'm trying to do even possible?

If this can't work I'm probably going to refactor part of Rectangle into MutableRectangle class.

Thanks.

+4  A: 

You can make a class ImmutableRectangle, without any mutators and with only const methods, from which you can correctly derive both Rectangle, and, separately, ImmutableSquare and, from that, Square. Note that, shorn of mutability, the IS-A relationship does hold -- an immutable square IS-A immutable rectangle: mutability is the only serious issue, so by factoring it out you can get some substantial code reuse (for all const uses -- ones that don't in fact use, or need, mutability).

Introducing mutability along the inheritance is OK as long as no class invariants of the (immutable) base actually rely on the immutability characteristic; and of course an immutable object can be properly constructed from a const pointer or reference to the mutable version (presumably in a separate inline friend function to avoid giving the base class a dependency on the derived class;-) for reasonably-handy use.

Edit: one comment understandably expresses qualms because "a mutabe is not an immutable": to reason about this, you need to understand what "IS-A" means... and it does not mean the Korzybski-denied "is of identity": it means the LSP. Go through the rigmarole of constraints this means: covariance, contravariance, weaker-equal preconditions, stronger-equal postconditions, etc, as they apply to the const methods of the base (immutable) and derived (mutable) classes. You'll see that class invariants are the only issue, as I mentioned in the previous paragraph, so just avoid asserting immutability as a class invariant and you're in clover;-).

Maybe it would help to name the base class NotNecessarilyMutableRectangle since it doesn't assert immutability as a class invariant; that very precise naming might be philosophically reassuring but perhaps a trifle un-handy in everyday coding.

Alex Martelli
It may be "OK" - I certainly gave a +1 - but it is a little disconcerting to have the "IsA" principle broken like that. A mutable most definitely IsNotA immutable.
Steve314
Alex Martelli
I'm not sure you need an edit - you already mentioned invariants. My point is, I guess, that intuition and theoretical principle don't always exactly match. A good approach can still sometimes be a bit disconcerting. In another context, inheritance as extending rather than IsA would have been far less jarring - but there's just something about shapes, probably because of all those schoolroom and textbook examples.
Steve314
OTOH - it could be a name issue. MutableRectangle IsA Rectangle, and there's no reason why Rectangle can't have all the immutable functionality built in. To me that's less jarring.
Steve314
@Steve, you may be right wrt the naming -- maybe the base names for classes representing mathematical entities such as shapes should _imply_ immutability at least in the sense of "not necessarily mutable" (more mathematically and philosophically treatable) and a `Mutable` prefix specifically indicate the mutable classes.
Alex Martelli
"a trifle un-handy" A trifle not-necessarily-handy? The ambiguity is just between "immutable", meaning "this object does not permit change", and "immutable", meaning "this *interface* to the object does not permit change". If a player in some sport is "unstoppable", it doesn't mean she never stops, but in contrast "immutable data" does usually mean it cannot change by any means. I like the solution of `Rectangle` being the name of the base interface, with `MutableRectangle` a specialization, and perhaps `ImmutableRectangle` another specialization, with a function to "freeze" a Rectangle.
Steve Jessop
@Steve Jessop - yes, but the name implies immutability as a class invariant. The trouble is that mutability is a viral trait - just one mutating operation and the object is mutable, irrespective of how many non-mutating operations there may be. I came back to say that and basically "What about Rectangle IsA RectangleBase?", but "Rectangle Has ConstRectangleOps" is good. The clarification that immutability isn't a class invariant, but the non-mutating payload is - that makes a difference. OTOH, English has no obligation to provide la mot juste.
Steve314
@Steve Jessop: Immutable objects have a contract with any user promising that they will not change. Two immutable objects that compare equal will always compare equal, and may be used interchangeably. I would suggest having a base genericShape class with properties to read shape attributes; it can spawn mutable and immutable descendants; things which don't change a shape themselves but don't care if someone else might do so can accept objects of the generic class. The immutable classes might do well to accept the generic-class objects in their copy constructors.
supercat
A: 

I believe, though I'm not certain, that you have to use an explicit cast to invoke that conversion operator in that context. The ImmutableRectangle base is a common and effective solution. Quite similarly, you can use a more abstract solution such as:

/**
 * Base for rectangular classes; name it whatever you want.
 */
class RectangularBase {
public:

    virtual unsigned int getValue(int) const = 0;

};

/**
 * Base for specific rectangular classes; also named whatever.
 */
template<unsigned int Dimensions>
class Rectangular : public RectangularBase {
public:

    virtual unsigned int getValue(int index) const { return values[index]; }

private:

    unsigned int values[Dimensions];

};

/**
 * A square is Rectangular but has only one significant dimension.
 */
class Square : public Rectangular<1> {
public:

    unsigned int getSideLength() const { return getValue(0); }

};

/**
 * A Rectangle is Rectangular and has two significant dimensions.
 */
class Rectangle : public Rectangular<2> {
public:

    unsigned int getWidth() const { return getValue(0); }
    unsigned int getHeight() const { return getValue(1); }

};
Jon Purdy
+1  A: 

Well, I'm surprised. It seems privately inheriting a class A prevents you from using operator A outside the class.

You can solve your problem by making a member Rectangle for square and using it for the cast:

class Square {
    Rectangle r;
    public:
        operator const Rectangle&() const {
            return r;
        }
};

This should compile and work. And I believe it won't give you that much more work to do if any.

Shiroko
This worked well for me (just had to add "r." in front of a few things). Although I don't understand why private inheritance affects the public interface of a class. I thought private meant private.
Imbue