Skip to content

C++ OOP 面试高频知识点 - 05

继承:单继承、多继承、菱形继承、虚继承

1. 继承的基本概念

继承(Inheritance) 是面向对象编程的核心原则之一,它允许一个类(派生类/子类)继承另一个类(基类/父类)的属性和行为。继承提高了代码的复用性,并建立了类之间的层次关系。

// 基类
class Animal {
public:
    void eat() {
        cout << "Animal is eating" << endl;
    }

    void sleep() {
        cout << "Animal is sleeping" << endl;
    }
};

// 派生类
class Dog : public Animal {
public:
    void bark() {
        cout << "Dog is barking" << endl;
    }
};

int main() {
    Dog dog;
    dog.eat();   // 继承自 Animal
    dog.sleep(); // 继承自 Animal
    dog.bark();  // Dog 自己的方法
    return 0;
}

2. 继承类型

2.1 单继承(Single Inheritance)

单继承是指一个类只继承自一个基类。这是最简单和最常见的继承方式。

class Shape {
public:
    virtual double area() const = 0;
};

class Rectangle : public Shape {
public:
    Rectangle(double w, double h) : width(w), height(h) {}

    double area() const override {
        return width * height;
    }

private:
    double width, height;
};

class Circle : public Shape {
public:
    Circle(double r) : radius(r) {}

    double area() const override {
        return 3.14159 * radius * radius;
    }

private:
    double radius;
};

2.2 多继承(Multiple Inheritance)

多继承是指一个类继承自多个基类。它可以组合多个类的功能,但也会带来一些复杂性。

class Swimmer {
public:
    void swim() {
        cout << "Swimming" << endl;
    }
};

class Flyer {
public:
    void fly() {
        cout << "Flying" << endl;
    }
};

class Duck : public Swimmer, public Flyer {
public:
    void quack() {
        cout << "Quacking" << endl;
    }
};

int main() {
    Duck duck;
    duck.swim();  // 继承自 Swimmer
    duck.fly();   // 继承自 Flyer
    duck.quack(); // Duck 自己的方法
    return 0;
}

2.3 菱形继承(Diamond Inheritance)

菱形继承是一种特殊的多继承情况,其中一个类继承自两个类,而这两个类又继承自同一个基类,形成菱形结构。

class Animal {
public:
    string name;
};

class Mammal : public Animal {};

class Bird : public Animal {};

class Bat : public Mammal, public Bird {}; // 菱形继承

int main() {
    Bat bat;
    // bat.name = "Bat"; // 错误:歧义
    bat.Mammal::name = "Bat"; // 必须明确指定继承路径
    cout << bat.Mammal::name << endl;
    return 0;
}

2.4 虚继承(Virtual Inheritance)

虚继承是解决菱形继承中基类成员重复继承问题的方法。通过在继承时使用 virtual 关键字,可以确保基类在派生类中只存在一个实例。

class Animal {
public:
    string name;
};

class Mammal : virtual public Animal {}; // 虚继承

class Bird : virtual public Animal {}; // 虚继承

class Bat : public Mammal, public Bird {}; // 菱形继承,但只有一个 Animal 实例

int main() {
    Bat bat;
    bat.name = "Bat"; // 现在没有歧义了
    cout << bat.name << endl;
    return 0;
}

3. 继承时的访问权限

继承时的访问权限取决于继承方式(public、protected、private)和基类成员的访问权限:

基类成员访问权限 public 继承 protected 继承 private 继承
public public protected private
protected protected protected private
private 不可访问 不可访问 不可访问

4. 继承的优缺点

4.1 优点

  1. 代码复用:避免重复编写相似的代码
  2. 层次结构:建立清晰的类层次关系
  3. 多态性:支持动态多态(虚函数)
  4. 可扩展性:可以通过继承添加新功能

4.2 缺点

  1. 复杂性:继承层次过深会使代码难以理解和维护
  2. 紧耦合:子类与父类耦合度高,修改父类可能影响子类
  3. 限制:单继承限制了功能组合的灵活性(Java 等语言)
  4. 菱形继承问题:需要使用虚继承解决

5. 常见问题和回答

问题 1:什么时候应该使用继承?

当类之间存在 "is-a" 关系时,应该使用继承。例如,Dog is an Animal

问题 2:什么时候不应该使用继承?

当类之间存在 "has-a" 关系时,应该使用组合而不是继承。例如,Car has an Engine

// 不推荐:继承
class Car : public Engine {};

// 推荐:组合
class Car {
private:
    Engine engine;
};

问题 3:如何解决菱形继承问题?

使用虚继承可以解决菱形继承问题,确保基类在派生类中只存在一个实例。

问题 4:继承和组合的区别是什么?

  • 继承:表示 "is-a" 关系,子类继承父类的属性和行为
  • 组合:表示 "has-a" 关系,对象包含其他对象

6. 最佳实践

  1. 保持继承层次简单:继承层次不超过 3-4 层
  2. 优先使用组合:在不确定时,优先使用组合
  3. 设计好访问权限:合理使用 public、protected、private 访问控制
  4. 使用虚继承:在需要时使用虚继承解决菱形继承问题

总结

继承是面向对象编程的核心原则之一,它提供了代码复用和层次结构建立的能力。C++ 支持单继承、多继承、菱形继承和虚继承等多种继承方式。在使用继承时,我们应该注意继承与组合的选择,保持继承层次的简单性,并正确解决菱形继承问题。


练习建议: 1. 设计一个几何图形继承体系,包含形状、矩形、圆形、三角形等 2. 实现虚函数和多态 3. 测试菱形继承和虚继承的行为 4. 比较继承和组合在不同场景下的应用