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;
}
相关推荐
追逐时光者1 天前
一款使用 C# 编写专为 Windows 11 打造的文件资源管理器增强工具!
后端·.net
风象南1 天前
普通人用AI加持赚到的第一个100块
人工智能·后端
冰_河1 天前
QPS从300到3100:我靠一行代码让接口性能暴涨10倍,系统性能原地起飞!!
java·后端·性能优化
JavaGuide1 天前
7 道 RAG 基础概念知识点/面试题总结
前端·后端
桦说编程1 天前
从 ForkJoinPool 的 Compensate 看并发框架的线程补偿思想
java·后端·源码阅读
格砸1 天前
从入门到辞职|从ChatGPT到OpenClaw,跟上智能时代的进化
前端·人工智能·后端
蝎子莱莱爱打怪1 天前
GitLab CI/CD + Docker Registry + K8s 部署完整实战指南
后端·docker·kubernetes
哈密瓜的眉毛美1 天前
零基础学Java|第三篇:DOS 命令、转义字符、注释与代码规范
后端
用户60572374873081 天前
AI 编码助手的规范驱动开发 - OpenSpec 初探
前端·后端·程序员