I have the following classes:
class ServoPart {
protected:
virtual void doJob(byte* job) = 0;
private:
bool moving;
Servo servo;
};
// the following classes only have a constructor so I can use two ServoParts to inherit from
class Major: public ServoPart {};
class Minor: public ServoPart {};
class Arm: public Major, public Minor {
private:
void move() {
// do stuff
if (target == current) {
moving = false;
}
};
public:
void doJob(byte* job) {/* do stuff */};
};
I can't use virtual inheritance (I think) because the Major and Minor need to control one servo each which can't be the same. However, when the Arm is done moving, it should set the moving
member to false. Intellisense shows ServoPart::moving
, when im typing moving
.
Would this access of moving
be ambiguous? If yes, how can I fix this? Is my assumption about virtual inheritance, that I can't use it because I have two different servos, correct?
Yes, it is ambiguous and the compiler will complain.
You can write Major::moving
or Minor::moving
within the code of Arm
's member functions to specify which you want.
I question whether you have a proper "isa" relationship here. An arm is not a motor. An arm has two motors. You should be using composition here instead of inheritance. Note that Major
and Minor
don't have any virtual functions so there is no reason to prefer inheritance.
Try:
class Arm {
Major major;
Minor minor;
void move() {
...
major.moving= false;
minor.moving= false;
...
It is more obvious now how to refer to the components. But with (multiple) inheritance it is the same idea. I think also you are introducing the names Major
and Minor
just to get around the limit of inheriting more than one copy. If you used composition, you can avoid that:
class Arm {
ServoPart major;
ServoPart minor;
...
Or, since they are the same type now, perhaps make an array instead. This makes it easy to "broadcast" the same operation to all of them:
class Arm {
ServoPart joints[2];
void move() {
...
for (auto j : joints) j.moving= false;
Your comment indicates ServoPart
has a virtual doJob
function. You should separate that into a pure interface that has no data members, and derive your single-motor class from that. Your composited class can also inherit from that interface.
struct Servo_Interface {
virtual void doJob() =0;
};
class Joint : Servo_Interface {
bool moving;
Servo servo;
public:
void doJob() override;
};
class Arm : Servo_Interface {
Joint joints[2];
public:
void doJob() override;
};
Note that the implementation of doJob
in Arm
can call doJob
for each of the components it contains, but it is a separate function that does not have any automatic reliance on them. Both single Joint
and Arm
instances can be treated polymorphically since they have a common interface.