1. is-a关系
- 使用公有继承,派生类对象也是基类对象,对于基类对象的任何可执行操作,在派生类中同样可以执行
举个例子:
在游戏中有各种各样的游戏角色,比如法师、弓箭手、奶妈,他们统一属于游戏角色这个类,在这里,游戏角色就是基类,而里面各种各样的角色就属于继承类。基类中定义了一些角色通用动作,比如跑、跳、走等,而子类通过公有继承可以同样使用或者改写这些通用动作但是不能删除这些动作,而且子类内部可能有些属于自己独有的动作(比如弓箭手的射箭动作)。在这里,法师等就是is-a游戏角色。
相关代码如下所示:
class gameRole
{
public:
virtual void run() { cout << "gameRole run" << endl; }
virtual void jump() { cout << "gameRole jump" << endl; }
virtual void walk() { cout << "gameRole walk" << endl; }
};
class archer :public gameRole
{
public:
void run() override{ cout << "magicArmor run" << endl; }
void jump() override{ cout << "magicArmor jump" << endl; }
void walk() override{ cout << "magicArmor walk" << endl; }
void shoot() { cout << "shoot" << endl; }
};
2. has-a关系
- has-a就是使用组合方式或者私有继承方式。描述的是一种从属关系。
举个例子:
同样用游戏角色这个例子,每个游戏角色一般有一把武器,可能是斧头、长剑或者法杖,这里即每个游戏角色has-a武器。
使用组合方式实现:
class weapon
{
public:
void Use() { use(); }
protected:
virtual void use() = 0;
};
class arrow : public weapon
{
protected:
void use() override { cout << "use archer" << endl; }
};
class wand : public weapon
{
protected:
void use() override { cout << "use wand" << endl; }
};
class gameRole
{
public:
gameRole(shared_ptr<weapon> pWeapon) :m_pWeapon(pWeapon){}
void useWeapon(){ m_pWeapon->Use(); }
void chgWeapon(shared_ptr<weapon> pw){ m_pWeapon = pw; }
protected:
shared_ptr<weapon> m_pWeapon;
private:
};
class archer :public gameRole
{
public:
archer(shared_ptr<weapon> pWeapon) :gameRole(pWeapon){}
};
使用私有继承实现
class weapon
{
public:
void Use() { use(); }
protected:
virtual void use() = 0;
};
class arrow : public weapon
{
protected:
void use() override { cout << "use archer" << endl; }
};
class wand : public weapon
{
protected:
void use() override { cout << "use wand" << endl; }
};
class gameRole
{
public:
virtual void useWeapon() = 0;
};
class archer :public gameRole,private arrow
{
public:
void useWeapon() { Use(); }
};
组合方式和私有继承方式的优缺点对比:
组合关系:
优点:
1. 通过获取指向其它的具有相同类型的对象引用,可以在运行期间动态地定义(对象的)组合。比如在上述代码中可以通过使用chgWeapon改变使用的武器。
2. “黑盒”复用,被包含对象的内部细节对外是不可见。不破坏封装,整体类与局部类之间松耦合,彼此相对独立。
3. 整体类对局部类进行包装,封装局部类的接口,提供新的接口,具有较好的可扩展性。
缺点:
1. 整体类不能自动获得和局部类同样的接口,比继承实现需要的代码更多。
私有继承:
优点:
1. 简单易用,使用语法关键字即可轻易实现
2. 易于修改或扩展那些父类被子类复用的实现
缺点:
1. 编译阶段静态决定了层次结构,不能在运行期间进行改变,比如上述代码中当archer私有继承arrow,就不能再改变其使用武器。
2. 破坏了封装性,由于“白盒”复用,父类的内部细节对于子类而言通常是可见的,比如arrow中的所有public和protected成员对于archer来说都是可见的。
3. 子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性。当父类的实现更改时,子类也不得不会随之更改。