当前位置: 首页 > news >正文

c++中类的继承


前言

一、继承的基本概念

继承是面向对象编程的三大特性之一(封装、继承、多态),它允许我们根据一个类来定义另一个类,使得代码可以重用,同时也便于创建更抽象的关系模型。

1.1 为什么需要继承

  • 代码重用:可以复用基类的代码,减少重复编写

  • 层次化分类:可以创建从通用到特殊的类层次结构

  • 可扩展性:可以在不修改基类的情况下扩展功能

1.2 基本语法

class 派生类名 : 访问修饰符 基类名 {// 派生类成员
};

二、继承的访问控制

C++中有三种继承方式,它们决定了基类成员在派生类中的访问权限:

2.1 public继承(最常用)

class Base {
public:int publicVar;
protected:int protectedVar;
private:int privateVar;
};class Derived : public Base {// publicVar仍然是public// protectedVar仍然是protected// privateVar不可访问
};

2.2 protected继承

class Derived : protected Base {// publicVar变为protected// protectedVar仍然是protected// privateVar不可访问
};

2.3 private继承(默认继承方式)

class Derived : private Base {// publicVar变为private// protectedVar变为private// privateVar不可访问
};// 等价于
class Derived : Base { ... };

2.4 访问规则总结表

基类成员访问权限继承方式在派生类中的访问权限
publicpublicpublic
publicprotectedprotected
publicprivateprivate
protectedpublicprotected
protectedprotectedprotected
protectedprivateprivate
private任何方式不可访问

三、派生类的构造与析构

3.1 构造函数调用顺序

  1. 基类构造函数

  2. 成员对象构造函数(按声明顺序)

  3. 派生类构造函数

class Base {
public:Base() { cout << "Base constructor" << endl; }
};class Member {
public:Member() { cout << "Member constructor" << endl; }
};class Derived : public Base {Member m;
public:Derived() { cout << "Derived constructor" << endl; }
};// 输出顺序:
// Base constructor
// Member constructor
// Derived constructor

这里要注意的是要构造derived的话必须先构造base类

3.2 析构函数调用顺序

与构造函数相反:

  1. 派生类析构函数

  2. 成员对象析构函数

  3. 基类析构函数

3.3 显式调用基类构造函数

class Base {int x;
public:Base(int i) : x(i) {}
};class Derived : public Base {int y;
public:Derived(int i, int j) : Base(i), y(j) {}
};

使用初始化列表初始化列表直接调用基类的公有方法给私有成员赋值

四、函数重写与隐藏

4.1 函数重写(覆盖)

发生在虚函数中,是实现多态的基础:

class Base {
public:virtual void show() { cout << "Base show" << endl; }
};class Derived : public Base {
public:void show() override { cout << "Derived show" << endl; }
};Base* b = new Derived();
b->show(); // 输出 "Derived show" - 多态

将base类的成员方法虚写了这里只是虚方法不是纯虚函数所以派生类可以重写也可以不重写

如果重写后调用的时候调用的是派生类的方法

4.2 函数隐藏

如果基类和派生类有同名函数但不是虚函数,会发生隐藏:

class Base {
public:void display() { cout << "Base display" << endl; }
};class Derived : public Base {
public:void display(int x) { cout << "Derived display" << endl; }
};Derived d;
d.display(1); // OK
d.display();  // 错误!基类display被隐藏
d.Base::display(); // 正确,显式调用基类方法

当派生类定义了与基类同名的函数(不论参数是否相同),基类的同名函数在派生类作用域中会被隐藏。

五、多重继承与虚继承

5.1 多重继承

一个类可以继承多个基类:

class A { /*...*/ };
class B { /*...*/ };
class C : public A, public B { /*...*/ };

5.2 菱形继承问题

    Base/    \
Der1   Der2\    /Final

这样的情况的话会导致Final类中有两份Base的成员,造成二义性。

class Base {
public:int data;void func() { cout << "Base::func()" << endl; }
};class Der1 : public Base { /* 可能添加特有成员 */ };
class Der2 : public Base { /* 可能添加特有成员 */ };class Final : public Der1, public Der2 { /* ... */ };

​​​​​Final类会包含两份Base的成员(通过Der1Der2各继承一份)对于int dataFinal对象中将有两份拷贝

六、继承中的类型转换

6.1 向上转型(Upcasting)

派生类指针/引用转换为基类指针/引用,是安全的隐式转换

Derived d;
Base* pb = &d;  // 向上转型
Base& rb = d;   // 向上转型

6.2 向下转型(Downcasting)

基类指针/引用转换为派生类指针/引用,需要使用dynamic_cast(需要多态类型):

Base* pb = new Derived();
Derived* pd = dynamic_cast<Derived*>(pb);
if (pd) { /* 转换成功 */ }

七、实际应用示例

员工管理系统

class Employee {
protected:string name;double salary;
public:Employee(string n, double s) : name(n), salary(s) {}virtual void printInfo() {cout << "Name: " << name << ", Salary: " << salary << endl;}
};class Manager : public Employee {string department;
public:Manager(string n, double s, string d) : Employee(n, s), department(d) {}void printInfo() override {cout << "Manager - Department: " << department << ", ";Employee::printInfo();}
};

九、常见问题与陷阱

  1. 切片问题:将派生类对象赋值给基类对象时,派生类特有部分会被"切掉"

    Derived d;
    Base b = d; // 切片发生
  2. 私有继承误用:私有继承表示"implemented-in-terms-of"关系,不是"is-a"关系

  3. 默认继承访问权限:class默认private继承,struct默认public继承

  4. 虚析构函数:当基类指针指向派生类对象时,基类必须有虚析构函数才能正确调用派生类析构函数


总结

继承是C++面向对象编程的核心概念之一,正确使用继承可以创建出灵活、可维护的代码结构。理解各种继承方式的区别、构造/析构顺序、虚函数机制等关键点,对于设计良好的类层次结构至关重要。同时,也要注意继承的滥用问题,在适当的时候考虑使用组合而非继承

相关文章:

  • 0610_特性和反射_加密和解密_单例模式
  • Playwright 与 Selenium:自动化测试的两大主流工具对比
  • Kubernetes 从入门到精通-pod基础管理
  • 饿一饿对肝脏好
  • ETL中图表统计分析模版组件使用
  • Java设计模式基础问答
  • 设计模式和设计原则回顾
  • C#设计模式
  • QMC5883L的驱动
  • 深入解析 GitHub Token 与 NPM Token:自动化发布的完整指南
  • pnpm install 和 npm install 的区别
  • Java如何权衡是使用无序的数组还是有序的数组
  • oracle 安全基线配置
  • MySQL主从复制实现指南
  • [2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
  • 2025蓝奏云软件库合集分享链接汇总:极刻云搜 - 一站式获取海量资源
  • 人脸识别技术应用备案材料揭秘
  • 24-Oracle 23 ai ​Lock-Free Reservations​(无锁列值保留)
  • 恶意流量异同
  • python打卡day50@浙大疏锦行
  • 电脑怎么建网站详细步骤/新闻头条免费下载安装
  • 建一个个人网站/网络营销推广价格
  • 一级a做愛网站体验区/站长工具查询
  • 地方信息网站源码/武汉软件测试培训机构排名
  • 网站建设包含哪些方面/河北seo推广
  • 哪里有做杂志的免费模板下载网站/搜索引擎技术优化