首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《C++ 程序设计》第 7 章-类的继承

《C++ 程序设计》第 7 章-类的继承

作者头像
啊阿狸不会拉杆
发布2026-01-21 12:38:21
发布2026-01-21 12:38:21
90
举报

引言

        在 C++ 面向对象编程中,继承(Inheritance) 是三大特性(封装、继承、多态)之一,它允许我们基于已有的类创建新的类,实现代码复用、建立类的层次结构。本章将全面讲解类的继承机制,从基础概念到实战应用,帮你彻底掌握继承的核心知识点。

本章知识思维导图

7.1 基类与派生类

        继承的核心思想是 “复用已有代码,扩展新功能”。被继承的类称为基类(Base Class) 或父类,通过继承创建的新类称为派生类(Derived Class) 或子类。

7.1.1 继承关系举例

生活中充满继承关系,例如:

  • 动物(基类)→ 狗、猫(派生类)
  • 图形(基类)→ 圆形、矩形(派生类)

UML 类图(PlantText UML)

代码语言:javascript
复制
@startuml
class Animal {
  + name: string
  + eat(): void
  + sleep(): void
}

class Dog {
  + bark(): void
}

class Cat {
  + meow(): void
}

Animal <|-- Dog
Animal <|-- Cat
@enduml
7.1.2 派生类的定义

派生类定义语法:

代码语言:javascript
复制
class 派生类名 : 继承方式 基类名1, 继承方式 基类名2, ... {
  // 派生类成员
};

示例代码

代码语言:javascript
复制
#include <iostream>
#include <string>
using namespace std;

// 基类:动物
class Animal {
protected:
    string name; // 动物名称(保护成员,派生类可访问)
public:
    // 构造函数
    Animal(string n) : name(n) {
        cout << "Animal构造函数被调用" << endl;
    }
    
    // 成员函数:吃
    void eat() {
        cout << name << "在吃东西" << endl;
    }
    
    // 成员函数:睡
    void sleep() {
        cout << name << "在睡觉" << endl;
    }
};

// 派生类:狗(公有继承Animal)
class Dog : public Animal {
public:
    // 派生类构造函数:必须初始化基类
    Dog(string n) : Animal(n) {
        cout << "Dog构造函数被调用" << endl;
    }
    
    // 派生类新增成员:叫
    void bark() {
        cout << name << "在汪汪叫" << endl; // 访问基类保护成员
    }
};

// 派生类:猫(公有继承Animal)
class Cat : public Animal {
public:
    Cat(string n) : Animal(n) {
        cout << "Cat构造函数被调用" << endl;
    }
    
    // 派生类新增成员:喵
    void meow() {
        cout << name << "在喵喵叫" << endl;
    }
};

// 主函数:程序入口
int main() {
    // 创建Dog对象
    Dog dog("大黄");
    dog.eat();    // 调用基类方法
    dog.sleep();  // 调用基类方法
    dog.bark();   // 调用派生类方法
    
    cout << endl; // 换行分隔
    
    // 创建Cat对象
    Cat cat("小白");
    cat.eat();    // 调用基类方法
    cat.sleep();  // 调用基类方法
    cat.meow();   // 调用派生类方法
    
    return 0;
}
7.1.3 派生类生成过程

派生类的生成包括三个步骤:

  1. 吸收基类成员:派生类自动包含基类的所有成员(除构造 / 析构函数、重载运算符等特殊成员)
  2. 改造基类成员:通过重定义或访问控制修改基类成员的行为或权限
  3. 添加新成员:增加派生类特有的成员(属性和方法)

派生类生成流程图

7.2 访问控制

        继承方式(公有、私有、保护)决定了基类成员在派生类中的访问权限。基类成员有三种访问级别:public(公有)、private(私有)、protected(保护)。

7.2.1 公有继承(public)
  • 基类public成员 → 派生类public成员(类内、类外均可访问)
  • 基类protected成员 → 派生类protected成员(类内可访问,类外不可)
  • 基类private成员 → 派生类不可直接访问(需通过基类公有方法访问)

示例代码

代码语言:javascript
复制
class Base {
public:
    int pub; // 公有成员
protected:
    int pro; // 保护成员
private:
    int pri; // 私有成员
};

// 公有继承
class PubDerived : public Base {
public:
    void test() {
        pub = 1; // 可访问(public→public)
        pro = 2; // 可访问(protected→protected)
        // pri = 3; // 错误:私有成员不可直接访问
    }
};

int main() {
    PubDerived d;
    d.pub = 10; // 正确:类外可访问public成员
    // d.pro = 20; // 错误:类外不可访问protected成员
    return 0;
}
7.2.2 私有继承(private)
  • 基类public/protected成员 → 派生类private成员(仅派生类内可访问)
  • 基类private成员 → 派生类不可直接访问

示例代码

代码语言:javascript
复制
// 私有继承
class PriDerived : private Base {
public:
    void test() {
        pub = 1; // 可访问(public→private)
        pro = 2; // 可访问(protected→private)
        // pri = 3; // 错误
    }
};

int main() {
    PriDerived d;
    // d.pub = 10; // 错误:public继承后变为private,类外不可访问
    return 0;
}
7.2.3 保护继承(protected)
  • 基类public成员 → 派生类protected成员
  • 基类protected成员 → 派生类protected成员
  • 基类private成员 → 派生类不可直接访问

访问控制对比表

基类成员

公有继承

私有继承

保护继承

public

public

private

protected

protected

protected

private

protected

private

不可访问

不可访问

不可访问

7.3 类型兼容规则

类型兼容规则是指在需要基类对象的地方,可以使用派生类对象替代,包括:

  1. 派生类对象可直接赋值给基类对象
  2. 派生类对象地址可赋值给基类指针
  3. 派生类对象可作为基类引用的初始值

示例代码

代码语言:javascript
复制
// 基类
class Base {
public:
    void print() { cout << "Base::print()" << endl; }
};

// 派生类
class Derived : public Base {
public:
    void print() { cout << "Derived::print()" << endl; } // 重定义
};

int main() {
    Derived d;
    Base b;

    // 1. 派生类对象赋值给基类对象
    b = d; // 仅复制基类部分成员
    b.print(); // 输出:Base::print()

    // 2. 派生类地址赋值给基类指针
    Base* pb = &d;
    pb->print(); // 输出:Base::print()(未用虚函数,静态绑定)

    // 3. 派生类对象作为基类引用
    Base& rb = d;
    rb.print(); // 输出:Base::print()

    return 0;
}

类型兼容规则示意图

7.4 派生类的构造和析构函数

        派生类不能继承基类的构造 / 析构函数,需自行定义,但需负责初始化基类成员。

7.4.1 构造函数

派生类构造函数语法:

代码语言:javascript
复制
派生类名(参数列表) : 基类名(基类参数), 成员变量(参数) {
  // 派生类构造逻辑
}

构造函数调用顺序:基类构造函数 → 派生类成员构造函数 → 派生类构造函数

示例代码

代码语言:javascript
复制
class Base {
public:
    Base(int x) : x(x) {
        cout << "Base构造函数:x=" << x << endl;
    }
private:
    int x;
};

class Derived : public Base {
public:
    // 派生类构造函数必须初始化基类
    Derived(int x, int y) : Base(x), y(y) {
        cout << "Derived构造函数:y=" << y << endl;
    }
private:
    int y;
};

int main() {
    Derived d(10, 20); 
    // 输出顺序:
    // Base构造函数:x=10
    // Derived构造函数:y=20
    return 0;
}
7.4.2 复制构造函数

派生类复制构造函数需显式调用基类复制构造函数:

代码语言:javascript
复制
class Base {
public:
    Base(int x) : x(x) {}
    // 基类复制构造函数
    Base(const Base& other) : x(other.x) {
        cout << "Base复制构造函数" << endl;
    }
private:
    int x;
};

class Derived : public Base {
public:
    Derived(int x, int y) : Base(x), y(y) {}
    // 派生类复制构造函数:需调用基类复制构造
    Derived(const Derived& other) : Base(other), y(other.y) {
        cout << "Derived复制构造函数" << endl;
    }
private:
    int y;
};

int main() {
    Derived d1(10, 20);
    Derived d2 = d1; // 调用复制构造函数
    // 输出:
    // Base复制构造函数
    // Derived复制构造函数
    return 0;
}
7.4.3 析构函数

    析构函数调用顺序与构造函数相反:派生类析构函数 → 派生类成员析构函数 → 基类析构函数

示例代码

代码语言:javascript
复制
class Base {
public:
    ~Base() {
        cout << "Base析构函数" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        cout << "Derived析构函数" << endl;
    }
};

int main() {
    Derived d;
    // 输出顺序:
    // Derived析构函数
    // Base析构函数
    return 0;
}

构造 / 析构调用顺序流程图

7.4.4 删除 delete 构造函数

    使用delete关键字禁用特定构造函数(如复制构造):

代码语言:javascript
复制
class NoCopy {
public:
    NoCopy() = default; // 默认构造函数
    NoCopy(const NoCopy&) = delete; // 禁用复制构造函数
    NoCopy& operator=(const NoCopy&) = delete; // 禁用赋值运算符
};

int main() {
    NoCopy a;
    // NoCopy b = a; // 错误:复制构造函数已删除
    return 0;
}

7.5 派生类成员的标识与访问

   当派生类与基类有同名成员时,需通过特殊方式访问基类成员。

7.5.1 作用域分辨符(::)

使用基类名::成员名访问基类同名成员:

代码语言:javascript
复制
class Base {
public:
    void print() { cout << "Base::print()" << endl; }
};

class Derived : public Base {
public:
    void print() { 
        cout << "Derived::print()" << endl;
        Base::print(); // 访问基类print()
    }
};

int main() {
    Derived d;
    d.print(); // 调用派生类print()
    d.Base::print(); // 显式调用基类print()
    return 0;
}
7.5.2 虚基类

   解决菱形继承(多继承中同一基类被多次继承)的二义性问题,使用virtual声明虚基类:

代码语言:javascript
复制
#include<iostream>
using namespace std;

// 虚基类
class A {
public:
    int x;
    A(int x) : x(x) {
        cout << "A的构造函数被调用,x=" << x << endl;
    }
};

// 中间派生类声明A为虚基类
class B : virtual public A {
public:
    B(int x) : A(x) {
        cout << "B的构造函数被调用" << endl;
    }
};

class C : virtual public A {
public:
    C(int x) : A(x) {
        cout << "C的构造函数被调用" << endl;
    }
};

// 最终派生类D只继承A一次
class D : public B, public C {
public:
    // 虚基类由最终派生类初始化
    D(int x) : A(x), B(x+1), C(x+2) {
        cout << "D的构造函数被调用" << endl;
    }
    void print() {
        cout << "x=" << x << endl; // 无歧义,仅一个x
    }
};

// 主函数,程序入口
int main() {
    // 创建D类对象,测试虚基类功能
    D d(10);
    d.print(); // 输出x的值,验证没有二义性
    
    // 验证x的唯一性
    cout << "通过D对象访问x: " << d.x << endl;
    cout << "通过B继承路径访问x: " << d.B::x << endl;
    cout << "通过C继承路径访问x: " << d.C::x << endl;
    
    return 0;
}

菱形继承问题与虚基类解决方案

代码语言:javascript
复制
@startuml
' 菱形继承问题
class A
class B {
  + x: int  // 继承自A
}
class C {
  + x: int  // 继承自A(重复)
}
class D {
  + x: int  // 二义性:B::x还是C::x?
}

A <|-- B
A <|-- C
B <|-- D
C <|-- D

note right of D: 问题:D中有两个x副本,访问时二义性
@enduml
代码语言:javascript
复制
@startuml
' 虚基类解决方案
class A
class B <<virtual>> {
  + x: int  // 虚继承自A
}
class C <<virtual>> {
  + x: int  // 虚继承自A
}
class D {
  + x: int  // 仅一个x副本(来自A)
}

A <|-- B
A <|-- C
B <|-- D
C <|-- D

note right of D: 解决:虚基类A在D中只保留一个副本
@enduml
7.5.3 虚基类及其派生类构造函数

   虚基类的构造函数由最终派生类负责初始化,中间派生类的虚基类构造函数调用被忽略:

代码语言:javascript
复制
class A {
public:
    A(int x) : x(x) {
        cout << "A构造函数:x=" << x << endl;
    }
    int x;
};

class B : virtual public A {
public:
    B(int x) : A(x) { // 中间派生类的虚基类构造会被忽略
        cout << "B构造函数" << endl;
    }
};

class C : virtual public A {
public:
    C(int x) : A(x) { // 中间派生类的虚基类构造会被忽略
        cout << "C构造函数" << endl;
    }
};

class D : public B, public C {
public:
    // 最终派生类必须初始化虚基类A
    D(int x) : A(x), B(x+1), C(x+2) {
        cout << "D构造函数" << endl;
    }
};

int main() {
    D d(10); // 输出A构造函数:x=10(仅一次)
    return 0;
}

7.6 程序实例 —— 用高斯消去法解线性方程组

7.6.1 算法基本原理

高斯消去法通过消元(将方程组化为上三角矩阵)和回代(求解未知数)解线性方程组:

  1. 消元:对矩阵做行变换,使对角线以下元素为 0
  2. 回代:从最后一行开始,依次求解每个未知数
7.6.2 程序设计分析

设计Matrix类封装矩阵操作,GaussianElimination类继承Matrix实现高斯消去算法:

  • 核心方法:eliminate()(消元)、backSubstitute()(回代)
7.6.3 源程序及说明
代码语言:javascript
复制
#include <iostream>
#include <vector>
#include <cmath>
#include <iomanip>

using namespace std;

// 矩阵基类
class Matrix {
protected:
    vector<vector<double>> data; // 矩阵数据
    int rows; // 行数
    int cols; // 列数

public:
    // 构造函数:初始化rows×cols矩阵
    Matrix(int rows, int cols) : rows(rows), cols(cols) {
        data.resize(rows, vector<double>(cols, 0.0));
    }

    // 设置矩阵元素
    void set(int i, int j, double val) {
        if (i >= 0 && i < rows && j >= 0 && j < cols) {
            data[i][j] = val;
        }
    }

    // 获取矩阵元素
    double get(int i, int j) const {
        if (i >= 0 && i < rows && j >= 0 && j < cols) {
            return data[i][j];
        }
        return 0.0;
    }

    // 获取行数
    int getRows() const { return rows; }

    // 获取列数
    int getCols() const { return cols; }

    // 打印矩阵
    void print() const {
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                cout << setw(8) << fixed << setprecision(2) << data[i][j];
            }
            cout << endl;
        }
    }
};

// 高斯消去法类(继承Matrix)
class GaussianElimination : public Matrix {
private:
    vector<double> solution; // 解向量

public:
    // 构造函数:n元方程组(增广矩阵n×(n+1))
    GaussianElimination(int n) : Matrix(n, n + 1), solution(n, 0.0) {}

    // 消元过程:化为上三角矩阵
    bool eliminate() {
        int n = rows;
        for (int k = 0; k < n - 1; ++k) { // 第k步消元
            // 找主元(最大值)
            int maxRow = k;
            for (int i = k; i < n; ++i) {
                if (fabs(get(i, k)) > fabs(get(maxRow, k))) {
                    maxRow = i;
                }
            }
            // 交换行
            if (maxRow != k) {
                swap(data[k], data[maxRow]);
            }
            // 检查主元是否为0(奇异矩阵)
            if (fabs(get(k, k)) < 1e-10) {
                return false; // 无解或无穷多解
            }
            // 消元计算
            for (int i = k + 1; i < n; ++i) {
                double factor = get(i, k) / get(k, k);
                for (int j = k; j <= n; ++j) {
                    data[i][j] -= factor * data[k][j];
                }
            }
        }
        return true;
    }

    // 回代过程:求解未知数
    void backSubstitute() {
        int n = rows;
        // 最后一行直接求解
        solution[n - 1] = get(n - 1, n) / get(n - 1, n - 1);
        // 从倒数第二行向上回代
        for (int i = n - 2; i >= 0; --i) {
            double sum = 0.0;
            for (int j = i + 1; j < n; ++j) {
                sum += get(i, j) * solution[j];
            }
            solution[i] = (get(i, n) - sum) / get(i, i);
        }
    }

    // 打印解
    void printSolution() const {
        cout << "方程组的解:" << endl;
        for (int i = 0; i < solution.size(); ++i) {
            cout << "x" << i + 1 << " = " << fixed << setprecision(4) << solution[i] << endl;
        }
    }
};

int main() {
    // 解方程组:
    // 2x1 + x2 - x3 = 8
    // -3x1 - x2 + 2x3 = -11
    // -2x1 + x2 + 2x3 = -3
    GaussianElimination ge(3); // 3元方程组
    // 设置增广矩阵
    ge.set(0, 0, 2);  ge.set(0, 1, 1);  ge.set(0, 2, -1); ge.set(0, 3, 8);
    ge.set(1, 0, -3); ge.set(1, 1, -1); ge.set(1, 2, 2);  ge.set(1, 3, -11);
    ge.set(2, 0, -2); ge.set(2, 1, 1);  ge.set(2, 2, 2);  ge.set(2, 3, -3);

    cout << "原始增广矩阵:" << endl;
    ge.print();

    if (ge.eliminate()) {
        cout << "\n消元后的上三角矩阵:" << endl;
        ge.print();
        ge.backSubstitute();
        ge.printSolution();
    } else {
        cout << "\n方程组无解或有无穷多解" << endl;
    }

    return 0;
}
7.6.4 运行结果与分析

运行结果:

   分析:程序通过高斯消去法正确求解线性方程组,消元过程将矩阵化为上三角,回代过程依次求解每个未知数,验证了继承的实用性(GaussianElimination复用Matrix的矩阵操作)。

7.7 综合实例 —— 个人银行账户管理程序

7.7.1 问题的提出

   设计银行账户管理系统,支持不同类型账户(储蓄账户、信用卡账户)的存款、取款、查询等操作,通过继承实现代码复用。

7.7.2 类设计
  • 基类Account:封装账户基本属性(账号、余额)和方法(存款、取款、查询)
  • 派生类SavingsAccount:新增利率属性和计息方法
  • 派生类CreditAccount:新增信用额度和透支利息计算
代码语言:javascript
复制
@startuml
class Account {
  - accountId: string
  - balance: double
  + Account(id: string, balance: double)
  + deposit(amount: double): bool
  + withdraw(amount: double): bool
  + getBalance(): double
  + getAccountId(): string
  + printInfo(): void
}

class SavingsAccount {
  - interestRate: double
  + SavingsAccount(id: string, balance: double, rate: double)
  + calculateInterest(): double
  + addInterest(): void
  + printInfo(): void
}

class CreditAccount {
  - creditLimit: double
  - overdrawRate: double
  + CreditAccount(id: string, balance: double, limit: double, rate: double)
  + withdraw(amount: double): bool
  + calculateOverdrawInterest(): double
  + printInfo(): void
}

Account <|-- SavingsAccount
Account <|-- CreditAccount
@enduml
7.7.3 源程序及说明
代码语言:javascript
复制
#include <iostream>
#include <string>
#include <iomanip>

using namespace std;

// 银行账户基类
class Account {
protected:
    string accountId; // 账号
    double balance;   // 余额

public:
    // 构造函数
    Account(const string& id, double initialBalance) 
        : accountId(id), balance(initialBalance) {
        // 确保初始余额非负
        if (balance < 0) {
            cout << "警告:初始余额不能为负,已重置为0" << endl;
            balance = 0;
        }
    }

    // 析构函数
    virtual ~Account() {}

    // 存款
    bool deposit(double amount) {
        if (amount <= 0) {
            cout << "存款金额必须为正数!" << endl;
            return false;
        }
        balance += amount;
        return true;
    }

    // 取款(基类默认实现:不允许透支)
    virtual bool withdraw(double amount) {
        if (amount <= 0) {
            cout << "取款金额必须为正数!" << endl;
            return false;
        }
        if (amount > balance) {
            cout << "余额不足,取款失败!" << endl;
            return false;
        }
        balance -= amount;
        return true;
    }

    // 获取余额
    double getBalance() const {
        return balance;
    }

    // 获取账号
    string getAccountId() const {
        return accountId;
    }

    // 打印账户信息(虚函数,允许派生类重写)
    virtual void printInfo() const {
        cout << "账号:" << accountId << endl;
        cout << "当前余额:" << fixed << setprecision(2) << balance << "元" << endl;
    }
};

// 储蓄账户(派生类)
class SavingsAccount : public Account {
private:
    double interestRate; // 年利率

public:
    // 构造函数
    SavingsAccount(const string& id, double initialBalance, double rate)
        : Account(id, initialBalance), interestRate(rate) {
        // 确保利率合法
        if (interestRate < 0) {
            cout << "警告:利率不能为负,已重置为0" << endl;
            interestRate = 0;
        }
    }

    // 计算利息(年利息)
    double calculateInterest() const {
        return balance * interestRate;
    }

    // 增加利息到余额
    void addInterest() {
        double interest = calculateInterest();
        deposit(interest); // 复用基类存款方法
        cout << "已结算年利息:" << fixed << setprecision(2) << interest << "元" << endl;
    }

    // 重写打印信息
    void printInfo() const override {
        Account::printInfo(); // 调用基类方法
        cout << "年利率:" << fixed << setprecision(2) << (interestRate * 100) << "%" << endl;
        cout << "年利息:" << fixed << setprecision(2) << calculateInterest() << "元" << endl;
    }
};

// 信用卡账户(派生类)
class CreditAccount : public Account {
private:
    double creditLimit;    // 信用额度
    double overdrawRate;   // 透支利率(月利率)

public:
    // 构造函数
    CreditAccount(const string& id, double initialBalance, double limit, double rate)
        : Account(id, initialBalance), creditLimit(limit), overdrawRate(rate) {
        // 确保参数合法
        if (creditLimit < 0) {
            cout << "警告:信用额度不能为负,已重置为0" << endl;
            creditLimit = 0;
        }
        if (overdrawRate < 0) {
            cout << "警告:透支利率不能为负,已重置为0" << endl;
            overdrawRate = 0;
        }
    }

    // 重写取款方法(允许透支,但不超过信用额度)
    bool withdraw(double amount) override {
        if (amount <= 0) {
            cout << "取款金额必须为正数!" << endl;
            return false;
        }
        // 可取款上限:余额 + 信用额度
        double maxWithdraw = balance + creditLimit;
        if (amount > maxWithdraw) {
            cout << "超过信用额度,取款失败!最大可取款:" 
                 << fixed << setprecision(2) << maxWithdraw << "元" << endl;
            return false;
        }
        balance -= amount;
        return true;
    }

    // 计算透支利息(月利息)
    double calculateOverdrawInterest() const {
        if (balance >= 0) return 0; // 未透支,无利息
        return (-balance) * overdrawRate; // 透支金额×月利率
    }

    // 重写打印信息
    void printInfo() const override {
        Account::printInfo(); // 调用基类方法
        cout << "信用额度:" << fixed << setprecision(2) << creditLimit << "元" << endl;
        cout << "透支月利率:" << fixed << setprecision(2) << (overdrawRate * 100) << "%" << endl;
        if (balance < 0) {
            cout << "当前透支金额:" << fixed << setprecision(2) << (-balance) << "元" << endl;
            cout << "月透支利息:" << fixed << setprecision(2) << calculateOverdrawInterest() << "元" << endl;
        }
    }
};

// 测试函数
void testAccounts() {
    // 创建储蓄账户
    SavingsAccount sa("SA123456", 10000, 0.0275); // 年利率2.75%
    cout << "\n===== 储蓄账户初始信息 =====" << endl;
    sa.printInfo();

    sa.deposit(5000); // 存款5000
    cout << "\n===== 存款5000后 =====" << endl;
    sa.printInfo();

    sa.withdraw(3000); // 取款3000
    cout << "\n===== 取款3000后 =====" << endl;
    sa.printInfo();

    sa.addInterest(); // 结算利息
    cout << "\n===== 结算利息后 =====" << endl;
    sa.printInfo();

    // 创建信用卡账户
    CreditAccount ca("CA654321", 5000, 10000, 0.0005); // 信用额度1万,月透支利率0.05%
    cout << "\n===== 信用卡账户初始信息 =====" << endl;
    ca.printInfo();

    ca.withdraw(8000); // 取款8000(余额5000,可透支)
    cout << "\n===== 取款8000后 =====" << endl;
    ca.printInfo();

    ca.withdraw(7000); // 再取款7000(测试透支上限)
    cout << "\n===== 再取款7000后 =====" << endl;
    ca.printInfo();

    ca.deposit(10000); // 存款10000
    cout << "\n===== 存款10000后 =====" << endl;
    ca.printInfo();
}

int main() {
    cout << "===== 个人银行账户管理系统 =====" << endl;
    testAccounts();
    return 0;
}
7.7.4 运行结果与分析

运行结果(部分):

代码语言:javascript
复制
===== 个人银行账户管理系统 =====

===== 储蓄账户初始信息 =====
账号:SA123456
当前余额:10000.00元
年利率:2.75%
年利息:275.00元

===== 存款5000后 =====
账号:SA123456
当前余额:15000.00元
年利率:2.75%
年利息:412.50元

...

===== 信用卡账户初始信息 =====
账号:CA654321
当前余额:5000.00元
信用额度:10000.00元
透支月利率:0.05%

===== 取款8000后 =====
账号:CA654321
当前余额:-3000.00元
信用额度:10000.00元
透支月利率:0.05%
当前透支金额:3000.00元
月透支利息:1.50元

   分析:程序通过继承实现了不同账户类型的管理,基类Account提供基础功能,派生类通过重写方法(如withdrawprintInfo)实现个性化需求,体现了继承的代码复用和扩展能力。

7.8 深度探索

7.8.1 组合与继承
  • 继承(is-a):派生类是基类的一种(如 “狗是动物”)
  • 组合(has-a):类 A 包含类 B 的对象(如 “汽车有发动机”)

示例对比

代码语言:javascript
复制
// 继承示例(is-a)
class Animal { /* ... */ };
class Dog : public Animal { /* 狗是动物 */ };

// 组合示例(has-a)
class Engine { /* 发动机类 */ };
class Car { 
private:
    Engine engine; // 汽车有发动机
public:
    void start() { engine.start(); } // 复用发动机功能
};

选择原则:优先组合(低耦合),需多态时用继承。

7.8.2 派生类对象的内存布局

派生类对象内存包含基类成员和派生类新增成员(简化模型):

代码语言:javascript
复制
class Base {
public:
    int a;
protected:
    int b;
private:
    int c; // 私有成员也在内存中,但派生类不可直接访问
};

class Derived : public Base {
public:
    int d;
};

派生类内存布局示意图

内存布局示意图(32位系统)
代码语言:javascript
复制
+-------------------------+ <-- 对象起始地址
|  vptr (4 bytes)         |  // 指向派生类的虚表
+-------------------------+
|  Base::base_data (4 bytes) 
+-------------------------+
|  Derived::derived_data (4 bytes)
+-------------------------+ <-- 对象结束地址

虚函数表(vtable)布局
代码语言:javascript
复制
Derived类的虚表:
+-------------------------+
|  &Derived::vfunc1       |  // 覆盖的虚函数
+-------------------------+
|  &Base::vfunc2          |  // 继承的基类虚函数
+-------------------------+
|  &Derived::vfunc3       |  // 派生类新增虚函数
+-------------------------+

关键说明
  1. 基类子对象优先 基类的成员变量(base_data)位于派生类对象起始位置(紧随vptr之后)。
  2. 虚表指针(vptr)
    • 位于对象最开头(地址最低处)
    • 派生类会覆盖基类的vptr,指向自己的虚表
    • 虚表中包含所有虚函数的地址
  3. 虚函数表规则
    • 第1项:覆盖的虚函数(vfunc1
    • 第2项:继承的基类虚函数(vfunc2
    • 后续:派生类新增虚函数(vfunc3
  4. 内存对齐 示例中未显示填充字节,实际可能存在对齐填充(如64位系统中vptr为8字节)。
7.8.3 基类向派生类的转换及其安全性
  • 派生类→基类:隐式转换(安全,类型兼容规则)
  • 基类→派生类:需显式强制转换(不安全,可能访问不存在的成员)
代码语言:javascript
复制
Base* b = new Derived(); // 安全:派生类→基类指针
Derived* d1 = b; // 错误:基类→派生类无隐式转换
Derived* d2 = static_cast<Derived*>(b); // 显式转换(需确保b指向Derived对象,否则危险)

风险示例

代码语言:javascript
复制
Base b;
Derived* d = static_cast<Derived*>(&b); // 危险!b不是Derived对象
d->d = 10; // 访问不存在的成员,导致未定义行为

7.9 小结

本章重点:

  1. 继承的核心是代码复用和建立类层次,通过基类和派生类实现
  2. 三种继承方式(公有、私有、保护)控制基类成员访问权限
  3. 派生类构造 / 析构函数需遵循特定调用顺序,虚基类解决菱形继承二义性
  4. 作用域分辨符::用于访问基类同名成员
  5. 类型兼容规则允许派生类对象替代基类对象,但基类向派生类转换需谨慎
  6. 实战中需结合继承与组合,根据场景选择合适的设计方式

    通过高斯消去法和银行账户实例,我们掌握了继承在实际开发中的应用,合理使用继承能显著提高代码质量和开发效率。

结语

        类的继承是 C++ 面向对象编程的核心技术,掌握它能帮你设计更灵活、可扩展的系统。建议多动手实践本章代码,深入理解继承的细节和最佳实践。如有问题欢迎在评论区交流!

#C++ #面向对象 #类的继承 #编程学习

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 本章知识思维导图
  • 7.1 基类与派生类
    • 7.1.1 继承关系举例
    • 7.1.2 派生类的定义
    • 7.1.3 派生类生成过程
  • 7.2 访问控制
    • 7.2.1 公有继承(public)
    • 7.2.2 私有继承(private)
    • 7.2.3 保护继承(protected)
  • 7.3 类型兼容规则
  • 7.4 派生类的构造和析构函数
    • 7.4.1 构造函数
    • 7.4.2 复制构造函数
    • 7.4.3 析构函数
    • 7.4.4 删除 delete 构造函数
  • 7.5 派生类成员的标识与访问
    • 7.5.1 作用域分辨符(::)
    • 7.5.2 虚基类
    • 7.5.3 虚基类及其派生类构造函数
  • 7.6 程序实例 —— 用高斯消去法解线性方程组
    • 7.6.1 算法基本原理
    • 7.6.2 程序设计分析
    • 7.6.3 源程序及说明
    • 7.6.4 运行结果与分析
  • 7.7 综合实例 —— 个人银行账户管理程序
    • 7.7.1 问题的提出
    • 7.7.2 类设计
    • 7.7.3 源程序及说明
    • 7.7.4 运行结果与分析
  • 7.8 深度探索
    • 7.8.1 组合与继承
    • 7.8.2 派生类对象的内存布局
    • 内存布局示意图(32位系统)
    • 虚函数表(vtable)布局
    • 关键说明
    • 7.8.3 基类向派生类的转换及其安全性
  • 7.9 小结
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档