news 2026/4/3 15:51:32

C++继承详解:从概念到实战应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++继承详解:从概念到实战应用

C++继承详解:从概念到实战应用

1. 继承的基本概念

1.1 什么是继承?

继承是面向对象程序设计中最核心的代码复用机制。它允许我们在保持原有类特性的基础上进行扩展,增加新的方法和属性,从而产生新的类(派生类)。

简单理解:就像儿子继承父亲的某些特征一样,派生类可以"继承"基类的成员。

1.2 继承的优势

在没有继承之前,我们需要重复定义相同的成员:

// 没有继承的冗余设计 class Student { public: string name; // 姓名 string address; // 地址 string phone; // 电话 int age; // 年龄 void identity() { cout << "学生身份验证" << endl; } void study() { cout << "学生学习" << endl; } }; class Teacher { public: string name; // 姓名 string address; // 地址 string phone; // 电话 int age; // 年龄 void identity() { cout << "教师身份验证" << endl; } void teaching() { cout << "教师授课" << endl; } };

使用继承后,代码变得简洁:

// 基类:包含公共成员 class Person { public: string name; // 姓名 string address; // 地址 string phone; // 电话 int age; // 年龄 void identity() { cout << "身份验证" << endl; } }; // 派生类:继承基类 class Student : public Person { public: void study() { cout << "学生学习" << endl; } protected: int stuId; // 学号 }; class Teacher : public Person { public: void teaching() { cout << "教师授课" << endl; } protected: string title; // 职称 }; int main() { Student s; Teacher t; s.identity(); // 继承自Person类 t.identity(); // 继承自Person类 s.study(); // Student特有方法 t.teaching(); // Teacher特有方法 return 0; }

2. 继承的定义和访问控制

2.1 继承的定义格式

class 派生类名 : 继承方式 基类名 { // 派生类新增成员 };

示例

class Person { public: string name; protected: int age; private: string idCard; }; // public继承 class Student : public Person { public: void printInfo() { name = "张三"; // OK: public成员在派生类中仍是public age = 20; // OK: protected成员在派生类中仍是protected // idCard = "123"; // ERROR: private成员在派生类中不可见 } };

2.2 继承方式与访问控制

基类成员/继承方式public继承protected继承private继承
public成员publicprotectedprivate
protected成员protectedprotectedprivate
private成员不可见不可见不可见

重要规则

  1. 基类private成员在派生类中不可见(但仍被继承)
  2. 访问权限 = Min(基类中的访问权限, 继承方式)
  3. class默认private继承,struct默认public继承

2.3 实际应用建议

推荐使用public继承,其他继承方式在实际开发中很少使用:

// 推荐:public继承 class Student : public Person { // ... }; // 不推荐:protected/private继承 class Student : protected Person { // 尽量避免 // ... };

3. 继承中的特殊问题

3.1 隐藏规则(Name Hiding)

当派生类与基类有同名成员时,派生类成员会隐藏基类成员:

class Person { protected: string name = "小李子"; int num = 111; // 身份证号 }; class Student : public Person { public: void print() { cout << "姓名:" << name << endl; cout << "身份证号:" << Person::num << endl; // 需要指定基类作用域 cout << "学号:" << num << endl; // 访问的是Student的num } protected: int num = 999; // 学号,隐藏了基类的num }; int main() { Student s; s.print(); return 0; }

输出结果

姓名:小李子 身份证号:111 学号:999

3.2 派生类的默认成员函数

派生类的特殊成员函数需要正确处理基类部分:

class Person { public: Person(const char* name = "peter") : name(name) { cout << "Person()" << endl; } Person(const Person& p) : name(p.name) { cout << "Person(const Person& p)" << endl; } Person& operator=(const Person& p) { cout << "Person operator=()" << endl; if (this != &p) { name = p.name; } return *this; } ~Person() { cout << "~Person()" << endl; } protected: string name; }; class Student : public Person { public: Student(const char* name, int num) : Person(name) // 必须调用基类构造函数 , num(num) { cout << "Student()" << endl; } Student(const Student& s) : Person(s) // 调用基类拷贝构造 , num(s.num) { cout << "Student(const Student& s)" << endl; } Student& operator=(const Student& s) { cout << "Student operator=()" << endl; if (this != &s) { Person::operator=(s); // 调用基类operator= num = s.num; } return *this; } ~Student() { // 析构函数会自动调用基类析构函数 cout << "~Student()" << endl; } protected: int num; }; int main() { Student s1("张三", 1001); Student s2 = s1; // 调用拷贝构造 s2 = s1; // 调用赋值运算符 return 0; }

输出结果

Person() Student() Person(const Person& p) Student(const Student& s) Student operator=() Person operator=() ~Student() ~Person() ~Student() ~Person()

4. 继承中的高级特性

4.1 友元关系不能继承

class Student; class Person { public: friend void display(const Person& p, const Student& s); protected: string name; }; class Student : public Person { protected: int stuNum; }; void display(const Person& p, const Student& s) { cout << p.name << endl; // OK: Person的友元 // cout << s.stuNum << endl; // ERROR: 不能访问Student的protected成员 } // 解决方案:让display也成为Student的友元 class Student : public Person { friend void display(const Person& p, const Student& s); protected: int stuNum; };

4.2 静态成员的继承

基类的静态成员在整个继承体系中只有一份:

class Person { public: string name; static int count; // 静态成员 }; int Person::count = 0; class Student : public Person { protected: int stuNum; }; int main() { Person p; Student s; cout << &p.count << endl; // 相同地址 cout << &s.count << endl; // 相同地址 cout << Person::count << endl; // 通过基类访问 cout << Student::count << endl; // 通过派生类访问 return 0; }

5. 多继承和菱形继承问题

5.1 多继承的基本用法

class Printable { public: virtual void print() = 0; }; class Drawable { public: virtual void draw() = 0; }; // 多继承 class Shape : public Printable, public Drawable { public: void print() override { cout << "打印形状" << endl; } void draw() override { cout << "绘制形状" << endl; } };

5.2 菱形继承问题

菱形继承会导致数据冗余和二义性:

class Person { public: string name; }; class Student : public Person { protected: int stuNum; }; class Teacher : public Person { protected: int teacherId; }; class Assistant : public Student, public Teacher { protected: string majorCourse; }; int main() { Assistant a; // a.name = "张三"; // ERROR: 二义性,不知道是Student::name还是Teacher::name a.Student::name = "张三"; // 需要明确指定 a.Teacher::name = "李四"; return 0; }

5.3 虚继承解决方案

使用虚继承解决菱形继承问题:

class Person { public: string name; }; class Student : virtual public Person { // 虚继承 protected: int stuNum; }; class Teacher : virtual public Person { // 虚继承 protected: int teacherId; }; class Assistant : public Student, public Teacher { protected: string majorCourse; }; int main() { Assistant a; a.name = "张三"; // OK: 现在只有一份name return 0; }

6. 继承 vs 组合

6.1 继承(is-a关系)

// Car和BMW是is-a关系 class Car { public: void drive() { cout << "驾驶汽车" << endl; } }; class BMW : public Car { public: void specialFeature() { cout << "宝马特色功能" << endl; } };

6.2 组合(has-a关系)

// Car和Tire是has-a关系 class Tire { public: void rotate() { cout << "轮胎旋转" << endl; } }; class Car { private: Tire tires[4]; // 组合:Car有4个Tire public: void drive() { for (auto& tire : tires) { tire.rotate(); } } };

6.3 选择原则

优先使用组合,组合的耦合度更低,维护性更好:

// 推荐:使用组合 class Stack { private: vector<int> data; // 组合 public: void push(int value) { data.push_back(value); } void pop() { data.pop_back(); } int top() { return data.back(); } }; // 不推荐:使用继承(除非确实需要is-a关系) class Stack : public vector<int> { // 不推荐 public: void push(int value) { push_back(value); } void pop() { pop_back(); } int top() { return back(); } };

7. 实际应用示例

7.1 实现一个不能被继承的类

方法1:构造函数私有化

class NonInheritable { private: NonInheritable() {} // 私有构造函数 public: static NonInheritable* create() { return new NonInheritable(); } }; // class Derived : public NonInheritable {}; // ERROR: 无法访问私有构造函数

方法2:使用C++11的final关键字

class NonInheritable final { // 使用final关键字 // ... }; // class Derived : public NonInheritable {}; // ERROR: 不能继承final类

7.2 继承类模板示例

template<typename T> class Stack : private vector<T> { // 私有继承 public: void push(const T& x) { vector<T>::push_back(x); // 需要指定基类作用域 } void pop() { vector<T>::pop_back(); } const T& top() { return vector<T>::back(); } bool empty() { return vector<T>::empty(); } }; int main() { Stack<int> st; st.push(1); st.push(2); st.push(3); while (!st.empty()) { cout << st.top() << " "; st.pop(); } return 0; }

总结

继承是C++面向对象编程的核心特性,正确使用继承可以大大提高代码的复用性和可维护性。关键要点:

  1. 优先使用public继承,避免protected/private继承
  2. 注意隐藏规则,避免同名成员引起的混淆
  3. 正确处理派生类的特殊成员函数,确保基类部分正确初始化
  4. 避免菱形继承,如必须使用则采用虚继承
  5. 优先选择组合而非继承,降低耦合度
  6. 使用final关键字防止不希望的继承

通过合理运用继承机制,可以构建出层次清晰、易于维护的面向对象程序。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/3 10:57:12

SGMICRO圣邦微 SGM2017-3.3XN5/TR SOT23-5 线性稳压器(LDO)

特性 工作输入电压范围:2.5V至5.5V 固定输出电压为2.8V和3.3V 输出电流:300mA 电流:77微A(TYR) 低压差:在300mA时为300mV(典型值)低噪声:30uVrms(典型值)(10Hz至100kHz)高PSRR:在1kHz时典型值为73dB 电流限制与热保护 使用小型封装陶瓷电容实现稳定运行关断供电电流:0.01uA(典型…

作者头像 李华
网站建设 2026/4/3 5:20:40

SGMICRO圣邦微 SGM2019-1.3YN5G/TR SOT-153 线性稳压器(LDO)

特性 工作输入电压范围:2.5V至5.5V 固定输出电压: 1.2V,1.5V,1.8V,2.5V,2.6V,2.8V,2.85V,3.0V,3.3V可调输出电压范围:1.2V至5.0V输出电压精度:25C时士2.5% 低输出噪声:30pVRMS(典型值) 低压差电压:在300mA时为270mV(典型值) 高PSRR:在1kHz时典型值为74dB 关断电流:0.01uA(典型值…

作者头像 李华
网站建设 2026/4/3 9:52:16

SGMICRO圣邦微 SGM2019-1.5YC5G/TR SC70-5 线性稳压器(LDO)

特性工作输入电压范围&#xff1a;2.5V至5.5V固定输出电压&#xff1a;1.2V、1.5V、1.8V、2.5V、2.6V、2.8V、2.85V、3.0V、3.3V可调输出电压范围&#xff1a;1.2V至5.0V输出电压精度&#xff1a;25C时为2.5%低输出噪声&#xff1a;30μV_RMS&#xff08;典型值&#xff09;低压…

作者头像 李华
网站建设 2026/4/2 22:40:42

Python 爬虫实战:User-Agent 随机切换防封禁

前言 在网络爬虫的开发与应用过程中&#xff0c;反爬机制是绕不开的核心问题。其中&#xff0c;基于请求头中 User-Agent 字段的校验是网站最基础也是最常用的反爬手段之一。固定的 User-Agent 会被服务器快速识别为爬虫程序&#xff0c;进而触发 IP 封禁、请求限制等反爬措施…

作者头像 李华
网站建设 2026/3/31 7:04:09

一、地理探测器:是什么?

Geo Detector是 用Excel编制的地理探测器软件, 可从以下网址免费下载:http://www.geodetector.org/。地理探测器方法简介地理探测器&#xff08;Geographical Detector&#xff09;是一种用于识别空间分异特征及其驱动因素的统计分析方法&#xff0c;最早由王劲峰等学者提出&am…

作者头像 李华