C++——继承进阶

1. 多重继承

一个子类可以同时继承多个父类,这种继承方式称为多重继承。子类将拥有所有父类的成员变量和成员函数。

代码示例:

c++ 复制代码
#include <iostream>

class Animal {
public:
    void eat() {
        std::cout << "Animal is eating." << std::endl;
    }
};

class Mammal {
public:
    void giveBirth() {
        std::cout << "Mammal is giving birth." << std::endl;
    }
};

class Bat : public Animal, public Mammal {
public:
    void fly() {
        std::cout << "Bat is flying." << std::endl;
    }
};

int main() {
    Bat bat;
    bat.eat();
    bat.giveBirth();
    bat.fly();
    return 0;
}

2. 名词歧义

当不同的父类拥有相同名称的成员,并且被多重继承到同一个子类时,就会发生名词歧义。编译器无法确定子类对象调用的是哪个父类的成员。

代码示例:

c++ 复制代码
#include <iostream>

class A {
public:
    void print() {
        std::cout << "Class A" << std::endl;
    }
};

class B {
public:
    void print() {
        std::cout << "Class B" << std::endl;
    }
};

class C : public A, public B {
public:
    // 发生歧义,需要避免
    // void print() {
    //     std::cout << "Class C" << std::endl;
    // }
};

int main() {
    C c;
    //c.print(); // 发生歧义,编译错误
    //c.A::print(); // 使用作用域分辨符指定调用A类的print()
    //c.B::print(); // 使用作用域分辨符指定调用B类的print()
    return 0;
}

3. 菱形继承

菱形继承指的是一个基类被两个或多个子类继承,而这些子类又共同派生出一个新的子类。这种继承关系的逻辑结构图类似于菱形。

菱形继承存在的问题: 间接基类会被间接子类重复继承,导致成员变量被构造两次甚至多次,造成数据冗余和浪费。

代码示例:

c++ 复制代码
#include <iostream>

class Person {
public:
    Person(int age) : age(age) {}
    int age;
};

class Student : public Person {
public:
    Student(int age, int studentId) : Person(age), studentId(studentId) {}
    int studentId;
};

class Teacher : public Person {
public:
    Teacher(int age, int teacherId) : Person(age), teacherId(teacherId) {}
    int teacherId;
};

class TeachingAssistant : public Student, public Teacher {
public:
    TeachingAssistant(int age, int studentId, int teacherId) : Student(age, studentId), Teacher(age, teacherId) {}
};

int main() {
    TeachingAssistant ta(25, 1001, 2001);
    // 菱形继承,Person类的age成员被继承两次,存在问题
    std::cout << "Age: " << ta.Student::age << std::endl; // 访问Student继承的age
    std::cout << "Age: " << ta.Teacher::age << std::endl; // 访问Teacher继承的age
    return 0;
}

4. 虚继承

为了解决菱形继承带来的问题,可以使用虚继承。虚继承使用 virtual 关键字修饰继承方式,可以写在 public 之前或之后。被虚继承的类称为虚基类。

代码示例:

c++ 复制代码
#include <iostream>

class Person {
public:
    Person(int age) : age(age) {}
    int age;
};

class Student : virtual public Person {
public:
    Student(int age, int studentId) : Person(age), studentId(studentId) {}
    int studentId;
};

class Teacher : virtual public Person {
public:
    Teacher(int age, int teacherId) : Person(age), teacherId(teacherId) {}
    int teacherId;
};

class TeachingAssistant : public Student, public Teacher {
public:
    TeachingAssistant(int age, int studentId, int teacherId) : Student(age, studentId), Teacher(age, teacherId), Person(age) {} 
    // 需要在构造函数初始化列表中显式调用虚基类的构造函数
};

int main() {
    TeachingAssistant ta(25, 1001, 2001);
    // 虚继承,Person类的age成员只被继承一次
    std::cout << "Age: " << ta.age << std::endl; // 直接访问age
    return 0;
}

5. 继承中函数的重载和覆盖

重载: 函数名称相同,参数列表不同(参数数量或类型不同)。重载发生在同一个类域中,多个重载函数都可以被调用。

覆盖 (也称为隐藏): 内层作用域和外层作用域名称相同,内层覆盖外层。覆盖发生在不同的作用域,被覆盖的成员无法直接调用。 函数参数是否相同不影响覆盖,名称相同即可。成员函数和成员变量也可以发生覆盖。

代码示例:

c++ 复制代码
#include <iostream>

class Base {
public:
    void print(int a) {
        std::cout << "Base print(int): " << a << std::endl;
    }

    void print(int a, int b) {
        std::cout << "Base print(int, int): " << a << ", " << b << std::endl;
    }

    int data = 10;
};

class Derived : public Base {
public:
    void print(int a) { // 覆盖 Base::print(int)
        std::cout << "Derived print(int): " << a << std::endl;
    }

    int data = 20; // 覆盖 Base::data
};

int main() {
    Derived d;
    d.print(5); // 调用 Derived::print(int)
    d.Base::print(5, 10); // 调用 Base::print(int, int)
    std::cout << "Derived data: " << d.data << std::endl; // 输出 20
    std::cout << "Base data: " << d.Base::data << std::endl; // 输出 10

    return 0;
}
相关推荐
用户490558160812510 分钟前
当控制面更新一条 ACL 规则时,如何更新给数据面
后端
林太白12 分钟前
Nuxt.js搭建一个官网如何简单
前端·javascript·后端
码事漫谈13 分钟前
VS Code 终端完全指南
后端
该用户已不存在38 分钟前
OpenJDK、Temurin、GraalVM...到底该装哪个?
java·后端
怀刃1 小时前
内存监控对应解决方案
后端
码事漫谈1 小时前
VS Code Copilot 内联聊天与提示词技巧指南
后端
Moonbit1 小时前
MoonBit Perals Vol.06: MoonBit 与 LLVM 共舞 (上):编译前端实现
后端·算法·编程语言
Moonbit1 小时前
MoonBit Perals Vol.06: MoonBit 与 LLVM 共舞(下):llvm IR 代码生成
后端·程序员·代码规范
Moonbit2 小时前
MoonBit Pearls Vol.05: 函数式里的依赖注入:Reader Monad
后端·rust·编程语言
bobz9652 小时前
ThanosRuler
后端