3

I have 3 classes A, B and C, declared as

class A {
    int varA;
};
class B {
    int varB;
};
class C : public A, public B {
    void setVar(int var) {
        varA = var;
        varB = var; // <- duplicate
    }
};

Now class C will have 2 variables varA and varB. In my case, both variables have the same meaning (eg. position of a physical object) and thus always need to be the same, since member functions will only do calculations on their own variable.


I thought about adding a parent class for A and B, but then the same problem persists:

class Parent {
public:
    int var;
};

class A : public Parent {};
class B : public Parent {};

class C : public A, public B {
    void setVar(int v) {
        A::var = v;
        B::var = v; // <- duplicate
        // or
        var = v; // <- compiler error: Ambiguous call
    }
};

Does anyone know an elegant solution to this problem?


Edit: Context

Classes A and B are Physics and Collidable respectively. The class Physics enables an object to have variables like acceleration, speed and position. And consists a couple of member functions to calculate the next position based on the time elapsed. The other class Collidable enables an object to interact (collide) with other objects, defining their behavior when a collision occurs while also checking whether they are in fact colliding or not. This class has variables such as position and a bounding box.

The overlapping variable is thus the position since both classes need it independently from each other. The class Collidable does not need to inherit from Physics because a wall for example does not need variables like acceleration and/or speed since it's static.

I wanted to use inheritance for C because that will be an object that is-a physics object and is-a collidable object. A has-a relation does not seem appropriate, hence I chose inheritance over composition.

Didii
  • 1,194
  • 12
  • 36
  • _'Does anyone know an elegant solution to this problem?'_ Don't do such design! **Why** do you need to redeclare a variable in an inherited class? That's what `protected` is for. – πάντα ῥεῖ Dec 16 '13 at 18:04
  • If I remember add the parent class you suggested, and do virtual inheritance: A and B virtually inherit Parent. It is not elegant, it is C++, for an elegant solution see Eiffel. – ctrl-alt-delor Dec 16 '13 at 18:04
  • 1
    [Virtual inheritance](http://stackoverflow.com/questions/21558/in-c-what-is-a-virtual-base-class) will solve this, but... from your description I think what you need is composition, not inheritance. Just let C contain both B and A as member objects. – jrok Dec 16 '13 at 18:06
  • 1
    The example will not compile because `varA` and `varB` are private members of their respective classes and thus not accessible through a member function of the derived class. – Matt Davis Dec 16 '13 at 18:07
  • 1
    You have fallen prey to an inheritance trap: inheritance violates the Single Responsibility Principle by giving you the opportunity to inherit both *interface* and *implementation*. Try to reconsider your design to cleanly separate interfaces from implementation, you should inherit from interfaces (to override `virtual`) and only the leaf class (`final`) should contain data. (that is actually what `virtual` inheritance ends up doing, in a round-about way) – Matthieu M. Dec 16 '13 at 19:21

1 Answers1

8

To solve your problem as written, you could use a virtual parent so that it's shared as one instance in the child class:

class Parent {
public:
    int var;
};

class A : public virtual Parent {};   // ** Note I added virtual here
class B : public virtual Parent {};   // ** Note I added virtual here

class C : public A, public B {
    void setVar(int v) {
        var = v; // No longer ambiguous.
    }
};

But this smells of a design that could use another look. Why do two unrelated classes both have the same data within them (in your original design)? Think about these things a bit and perhaps an alternate design will come to you (abstract interface in a base class, mechanism to tie the classes together using template algorithms, etc). Alternately if you provide more information about the relationship of your classes we can provide a C++-idiomatic solution.

EDIT:

Another idea that I believe I like better would be to seperate the position's state from the physics and collidable objects, adding one more level of indirection. So physics and collidable both would have pointers to an external position state object. Since it's external, both would refer to and be able to mutate the same shared state. The desired ownership would dictate the type of pointer used.

A rough sketch might look like this:

class Position {};

class A
{
public:
    explicit A(const shared_ptr<Position>& pos) : pos_(pos) {}

private:
    shared_ptr<Position> pos_;
};

class B
{
public:
    explicit B(const shared_ptr<Position>& pos) : pos_(pos) {}

private:
    shared_ptr<Position> pos_;
};

class C : public A, public B
{
public:
    explicit C(const shared_ptr<Position>& pos) : A(pos), B(pos) {}
};

int main()
{
    shared_ptr<Position> pos = make_shared<Position>();

    C object_of_type_C(pos);
}

Finally note that is-a, while a useful guideline, shouldn't really determine when you inherit. What it really means is that you want to take a C and transparently use it as a physics OR as a collidable without the code in question really knowing it's a C. If this represents your need then that's the real test for your design (see Liskov Substitution Principle).

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • Thanks for the answer. I'll update my question with some context about what these classes are. Been searching for a new design, but none other occurred to me yet. – Didii Dec 16 '13 at 18:41