ここでの継承の使用virtual
は偽りであり、ニシンです。 virtual
継承は、共通の基本クラスから一度だけ派生するために使用されます。一致するメンバー署名の宣言を 1 つだけ持ち込まないでください。
したがって、FastCar
具象クラスには実際には 2 つではなく 3 つのメンバーがあります。
virtual void ICar::drive()
virtual void IFastCar::drive()
virtual void IFastCar::driveFast()
この 2 つdrive()
は関連しているように見えるため、設計に欠陥があるように見えます。virtual
継承は必要ありません。おそらくIFastCar
から派生させたいでしょうICar
。
class ICar {
public:
virtual void drive() = 0;
};
class IFastCar : public ICar {
public:
virtual void driveFast() = 0;
};
class Car : public virtual ICar {
public:
void drive() {};
};
class FastCar : public IFastCar {
public:
void drive() {};
void driveFast() {};
};
int main()
{
FastCar fc;
fc.drive();
fc.driveFast();
return 0;
}
Car
次に、使用する基本的な機能を実装したい場合は、からFastCar
派生させることができます。そのため、継承が必要になります。ひし形の上部のすぐ下のポイントで継承を適用することを忘れないでください。FastCar
Car
virtual
virtual
class ICar {
public:
virtual void drive() = 0;
};
class IFastCar : virtual public ICar {
public:
virtual void driveFast() = 0;
};
class Car : public virtual ICar {
public:
void drive() {};
};
class FastCar : public IFastCar, public Car {
public:
void driveFast() {};
};
int main()
{
FastCar fc;
fc.drive();
fc.driveFast();
ICar* fc_a = new FastCar;
fc_a->drive(); // invokes Car::drive() via domination
return 0;
}
上記のコードをコンパイルして実行すると、目的の動作が得られますが、コンパイラの警告が発生します。MSVC10 では次のように表示されます。
1>main.cpp(19): warning C4250: 'FastCar' : inherits 'Car::Car::drive' via dominance
1> main.cpp(13) : see declaration of 'Car::drive'
これは、実装の継承アーキテクチャが台無しになる可能性があるという警告です。実際、ほとんどの場合、おそらくそうです (ただし、この場合はそうではありません)。これは、特に数年後にコードを理解しようとしている保守プログラマーにとって、非常に混乱する可能性があります。この混乱を解消し、すべてのコンパイラの警告を回避するために、私は (原則として)実装の継承よりも姉妹クラスへの委譲を好みます。
class ICar {
public:
virtual void drive() = 0;
};
class IFastCar : virtual public ICar {
public:
virtual void driveFast() = 0;
};
class ICarImpl
{
public:
void drive_impl() {};
};
class IFastCarImpl
{
public :
void driveFast_impl() {};
};
class Car : public virtual ICar, protected ICarImpl {
public:
void drive() { drive_impl(); }
};
class FastCar : public IFastCar, protected ICarImpl, protected IFastCarImpl {
public:
void driveFast() { driveFast_impl(); }
void drive() { drive_impl(); }
};
int main()
{
FastCar fc;
fc.drive();
fc.driveFast();
ICar* fc_a = new FastCar;
fc_a->drive();
return 0;
}
これにより、実装の継承という通常の目標が達成されます。つまり、複数の具象クラスで共有できる共通のコード セットを 1 つだけ保持することで、保守が容易になります。