views:

485

answers:

6

In a C++ physics simulation, I have a class called Circle, and Square. These are Shapes, and have a method called push(), which applies force to it. There is then a special case of Circle, call it SpecialCircle, in which push() should exhibit slightly different properties. But in fact, there is also SpecialSquare() which should exhibit the same force properties. So I'd like to have an abstract base class called Shape which takes care of Circles and Squares, but then I'd also like an abstract base class called Special, which applies special properties to force().

What's the best way to design this class structure?

So far, I've got:

class Shape {
    virtual void push();
};

class Circle : public Shape {};

class Square : public Square {};

class Special {
    virtual void push();
};

class SpecialCircle : public Circle, Special {};

class SpecialSquare : public Square, Special {};

Of course, the above won't compile, since Special::push() and Shape::push() conflict. I get "error: request for member ‘push’ is ambiguous", as expected.

How can I re-organize my class structure so that Circle and Square can share certain properties with each other, but SpecialCircle and SpecialSquare can still inherit from Shape, and also inherit modified functionality from Special?

Thanks.

ps., is this the diamond inheritance problem?

+8  A: 

Another solution (it may or may not fit your needs, it depends on the details of your implementation):

  • Have the class Behavior, and let NormalBehavior and SpecialBehavior inherit from it.
  • Have the class Shape, and let Square and Circle inherit from it. Let Shape be an aggregate type, with a Behavior member (i.e. you pass a Behavior object to the various Shape constructors). In other words, let a Shape have a Behavior.
  • Delegate the actual differences in the behavior of shapes to methods of the Behavior hierarchy.

Conversely, you can:

  • Have the class PhysicalObject, and let NormalObject and SpecialObject inherit from it;
  • Have the class Shape, and let Square and Circle inherit from it;
  • Let a PhysicalObject have a Shape.

Prefer aggregation over inheritance. This is an application of the Bridge pattern. The advantage of this strategy with respect to having Square, SpecialSquare, Circle, and SpecialCircle, is that tomorrow you'll have to add Rectangle, Hexagon and so on, and for each shape you add you'll have to implement two classes (duplicated code is evil); this is, in my opinion, the real issue that Bridge addresses.

Federico Ramponi
A: 

Have a SpecialShape from Shape and SpecialCircle and SpecialSquare from SpecialShape.

Loki
+3  A: 

It's said that every problem in software can be solved by adding an additional layer of indirection.

Herb Sutter has an excellent article on how to solve your problem: Multiple Inheritance - Part III

In short, you use intermediate classes to 'rename' the virtual functions. As Herb says:

Renaming Virtual Functions

If the two inherited functions had different signatures, there would be no problem: We would just override them independently as usual. The trick, then, is to somehow change the signature of at least one of the two inherited functions.

The way to change a base class function's signature is to create an intermediate class which derives from the base class, declares a new virtual function, and overrides the inherited version to call the new function

Here's a long example using your classes:

class Shape {
public:
    virtual void push() = 0;
};

class Circle : public Shape 
{
public:
    void push() {
        printf( "Circle::push()\n");
    }
};

class Square : public Shape 
{
public:
    void push() {
        printf( "Square::push()\n");
    }
};

class Special {
public:
    virtual void push() = 0;
};


class Circle2: public Circle
{
public:
    virtual void pushCircle() = 0;
    void push() {
        pushCircle();
    }
};

class Square2: public Square
{
public:
    virtual void pushSquare() = 0;
    void push() {
        pushSquare();
    }
};


class Special2 : public Special
{
public:
    virtual void pushSpecial() = 0;
    void push() {
        pushSpecial();
    }
};



class SpecialCircle : public Circle2, public Special2 
{
public:
    void pushSpecial() {
        printf( "SpecialCircle::pushSpecial()\n");
    }
    void pushCircle() {
        printf( "SpecialCircle::pushCircle()\n");
    }

};

class SpecialSquare : public Square2, public Special2 
{
public:
    void pushSpecial() {
        printf( "SpecialSquare::pushSpecial()\n");
    }
    void pushSquare() {
        printf( "SpecialSquare::pushSquare()\n");
    }

};

int main( int argc, char* argv[])
{
    SpecialCircle sc;
    SpecialSquare ss;

    // sc.push();   // can't be called - ambiguous
    // ss.push();
    sc.pushCircle();
    ss.pushSquare();

    Circle* pCircle = ≻
    pCircle->push();

    Square* pSquare = &ss;
    pSquare->push();

    Special* pSpecial = ≻
    pSpecial->push();

    pSpecial = &ss;
    pSpecial->push();


    return 0;
}
Michael Burr
A: 

Well, if the special and normal circles can be both applied forces to, and the special circle has another method that applies special forces, why not have two interfaces and two methods?

struct Applicable {
    virtual ~Applicable() { }

    // if it applies force, better be explicit with naming it.
    virtual void applyForce() = 0;
};

struct SpecialApplicable {
    virtual ~SpecialApplicable() { }

    virtual void applySpecialForce() = 0;
};

struct Shape {
    virtual ~Shape() { }
    Size getSize();
    Point getPosition();
    // ...
};

struct Circle : Shape, Applicable {
    virtual void applyForce() { /* ... */ }
}

struct SpecialCircle : Circle, SpecialApplicable {
    virtual void applySpecialForce() { /* .... */ } 
};

If it doesn't make sense if there is both a special and a normal apply method (which the name of the class - SpecialCircle - suggests), then why not do even this:

struct Circle : Shape, Applicable {
    virtual void applyForce() { /* ... */ }
}

struct SpecialCircle : Circle {
    // applies force, but specially
    virtual void applyForce() { /* .... */ } 
};

You can also put the applyForce into the Shape class. It also depends on the environment in which those classes are used. What, in any case, you really should avoid is having the same method in two base classes that appear in two difference base-lattices. Because that inevitable will lead to such ambiguity problems. The diamond inheritance is when you use virtual inheritance. I believe there are other good answers on stackoverflow explaining that. It isn't applicable for your problem, because the ambiguity arises because the method appears in two base class sub-objects of different types. (It only solves such cases where the base classes have the same type. In those cases, it will merge the base classes and there will only be one base class sub-object contained - inherited by virtual inheritance)

Johannes Schaub - litb
+2  A: 

Rather than thinking of code reuse through inheritance, the use of mixins will give you the code reuse you want without the problems of multiple inheritance.

If you are unfamiliar with the technique, do a search on SO or Google. Make sure you search for both "mixin" and "Curiously Recurring Template Pattern". There are heaps of great articles around to get you started.

Daniel Paull
+1  A: 

When you have to inherit from multiple interfaces with the same method the compiler can't tell which one are you trying to call, you can fix this by overriding such method and call the one you want.

class SpecialCircle : public Circle, Special {
  public:
    virtual void push() { Special::push(); }
};
class SpecialSquare : public Square, Special {
  public:
    virtual void push() { Special::push(); }
};

But in this case I think the correct OO approach is to factor out the push behavior in its own class, like Federico Ramponi have suggested.

Ismael