is-a 关系(第 13 章)

← 返回虚函数 | ← 主页


目录


🔴is-a 的含义

🔺is-a 是公有继承的语义:派生类对象也是一个基类对象,可以在任何需要基类的地方使用派生类

class Animal {
public:
    void eat();
};
 
class Dog : public Animal {  // Dog is-a Animal
    void bark();
};
 
Animal* p = new Dog();  // ✅ 合法:Dog is-a Animal

🔺公有继承建立的是 is-a 关系,意味着:

  • 派生类对象可以赋值给基类指针/引用
  • 派生类对象可以调用基类的 public 方法
  • 基类能做的事,派生类都能做

🔴访问权限

🔺基类成员在派生类中的可见性:

基类成员派生类内部能否访问外部通过派生类能否访问
public✅ 能✅ 能
protected✅ 能❌ 不能
private❌ 不能❌ 不能

🔺protected 的意义:专门为派生类开放,外部不可见

class Base {
protected:
    int x;  // 派生类可访问,外部不可访问
};
 
class Derived : public Base {
public:
    void show() { cout << x; }  // ✅ 派生类内部可访问 protected
};
 
Derived d;
// d.x;  // ❌ 外部不能访问 protected

🔴is-a vs has-a vs is-implemented-as

🔺三种关系的选择:

关系实现方式例子
is-a(是一种)公有继承午餐是一种食物
has-a(有一个)包含/组合午餐有一个饮料
is-implemented-as(用…实现)私有继承用栈实现队列
// is-a:公有继承
class Lunch : public Food {};
 
// has-a:组合(优先选这个)
class Lunch {
    Drink d;  // 午餐"有"一个饮料
};
 
// is-implemented-as:私有继承
class Queue : private Stack {};  // 用栈的功能实现队列,但不暴露栈的接口

🔺原则:能用组合就不用继承;只有真正存在 is-a 关系时才用公有继承


🔴派生类不继承的内容

🔺以下内容不会被继承:

  • 构造函数
  • 析构函数
  • 赋值运算符 operator=
  • 友元函数(不是成员函数)

🔺派生类需要自己定义这些,或者显式调用基类版本


本节小结

要点说明
is-a 语义派生类对象可用于任何需要基类的场合
protected专为派生类开放,外部不可见
优先组合has-a 关系用组合,不要滥用继承
不继承的内容构造函数、析构函数、赋值运算符、友元