《C++ 继承》三大面向对象编程——继承:派生类构造、多继承、菱形虚拟继承概要

🔥个人主页:Cx330🌸

❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》

《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔

🌟心向往之行必能至


🎥Cx330🌸的简介:


目录

前言:

一、派生类的默认成员函数专题

1.1实现一个不可继承类实现

[1.1.1 间接实现:【C++98】构造函数私有的类不能被继承](#1.1.1 间接实现:【C++98】构造函数私有的类不能被继承)

[1.1.2 直接实现:final关键字修改基类](#1.1.2 直接实现:final关键字修改基类)

[1.1.3 代码实现](#1.1.3 代码实现)

[4.4.4 final关键字](#4.4.4 final关键字)

二、继承体系中的友元关系

[2.1 友元与继承的关系特性](#2.1 友元与继承的关系特性)

[2.2 解决方案](#2.2 解决方案)

[2.3 实战](#2.3 实战)

[2.3.1 正确代码演示](#2.3.1 正确代码演示)

[2.3.2 前置声明的必要性](#2.3.2 前置声明的必要性)

[2.3.3 友元关系不能继承](#2.3.3 友元关系不能继承)

三、静态成员在继承中的特性

[3.1 静态成员共享机制:父子共用同一份](#3.1 静态成员共享机制:父子共用同一份)

[3.2 静态与非静态成员对比](#3.2 静态与非静态成员对比)

[3.2.1 非静态成员:实例独立](#3.2.1 非静态成员:实例独立)

[3.2.2 静态成员:类间共享](#3.2.2 静态成员:类间共享)

[3.3 实践出真知:静态成员继承实践案例](#3.3 实践出真知:静态成员继承实践案例)

[四、单继承 vs 多继承(以及菱形继承问题详解)](#四、单继承 vs 多继承(以及菱形继承问题详解))

[4.1 单继承 vs 多继承](#4.1 单继承 vs 多继承)

[4.1.1 概念对比](#4.1.1 概念对比)

[4.1.2 实战](#4.1.2 实战)

[4.2 菱形继承问题详解](#4.2 菱形继承问题详解)

[4.2.1 菱形继承的概念](#4.2.1 菱形继承的概念)

[4.2.2 菱形继承的数据冗余与二义性问题](#4.2.2 菱形继承的数据冗余与二义性问题)

[4.2.3 虚继承解决方案](#4.2.3 虚继承解决方案)

[4.2.4 虚继承机制与virtual关键字](#4.2.4 虚继承机制与virtual关键字)

[4.2.5 菱形继承的问题](#4.2.5 菱形继承的问题)

[4.2.6 实战](#4.2.6 实战)

[4.2.7 可以设计出多继承,不建议设计出菱形继承](#4.2.7 可以设计出多继承,不建议设计出菱形继承)

[4.3 IO库中的菱形虚拟继承](#4.3 IO库中的菱形虚拟继承)

[4.4 多继承中的指针偏移问题](#4.4 多继承中的指针偏移问题)

[4.4.1 题目](#4.4.1 题目)

[4.4.2 答案解析](#4.4.2 答案解析)

五、继承与组合设计模式对比

[5.1 基本概念:is-a vs has-a](#5.1 基本概念:is-a vs has-a)

[5.2 继承与组合关系对比](#5.2 继承与组合关系对比)

[5.3 实践](#5.3 实践)

[5.4 继承 vs 组合](#5.4 继承 vs 组合)

[5.4.1 白盒复用与黑盒复用](#5.4.1 白盒复用与黑盒复用)

[8.4.2 软件设计中的选择策略](#8.4.2 软件设计中的选择策略)

[8.4.3 模块](#8.4.3 模块)

8.4.4继承和组合哪个更好?

完整代码演示

结尾


前言:

在面向对象编程的世界里,"避免重复 " 与 "灵活扩展 " 是开发者始终追求的目标,而 C++ 的继承机制正是实现这两个目标的核心工具。它让我们能够从已有的类(基类)中 "继承" 成熟的成员变量与成员函数,无需重新编写重复代码;同时又能在新类(派生类)中添加专属成员、重写原有函数,让类的功能随需求自然延伸。无论是模拟现实世界中 "动物与猫、狗" 的层级关系,还是开发中 "基础组件与定制组件" 的复用场景,继承都为代码的组织与维护提供了清晰的逻辑框架。理解继承,便是掌握 C++ 面向对象编程的关键一步


一、派生类的默认成员函数专题

1.1实现一个不可继承类实现

1.1.1 间接实现:【C++98】构造函数私有的类不能被继承

基类的构造函数私有,派生类的构成必须调用基类的构造函数,但是基类的构成函数私有化以后,派生类看不见就不能调用了,那么派生类就无法实例化出对象

运行结果:

这里必须调用基类的构造,但是基类这里是私有的,看不见,所以就不能再调用了。

1.1.2 直接实现:final关键字修改基类

C++11新增了一个final关键字,final修改基类,派生类就不能继承了

1.1.3 代码实现
cpp 复制代码
//设计一个不能被继承的类
//class Base
class Base final
{
public:
	void func5() { cout << "Base::func5" << endl; }
protected:
	int a = 1;
private:
	//构造函数私有的类不能被继承
	Base()
	{}
};

class Derive :Base
{

};

int main()
{
	Derive d;

	return 0;
}
4.4.4 final关键字

在本文博主不展开讲,下篇博客,博主会介绍C++进阶中又一个重要的模块------【多态】,在【多态】中,博主会介绍两个涉及到【多态】中的重写相关知识点的关键字:override和final

也就是说,final充当了两个作用

(1)直接实现一个不能被继承的类(【继承】篇涉及知识点);

(2)不让重写基类虚函数(【多态】(下一篇博客)篇即将涉及的知识点)。


二、继承体系中的友元关系

2.1 友元与继承的关系特性

友元关系不能继承。

也就是说 基类友元不能访问派生类私有和保护成员

2.2 解决方案

把派生类也变成基类的友元的友元即可

2.3 实战

2.3.1 正确代码演示
cpp 复制代码
class Student;

class Person
{
public:
	//友元关系不能被子类继承
	friend void Display(const Person& p, const Student& s);
protected:
	string _name; // 姓名
};

class Student : public Person
{
	friend void Display(const Person& p, const Student& s);
protected:
	int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}

int main()
{
	Person p;
	Student s;
	// 编译报错:error C2248: "Student::_stuNum": ⽆法访问 protected 成员
	// 解决⽅案:Display也变成Student 的友元即可
	Display(p, s);

	return 0;
}

运行结果:

这段代码是能顺利运行的,但是,我们看下面这段代码

cpp 复制代码
class Person
{
	// 友元关系不能被子类继承
	friend void Display(const Person& p, const Student& s);
public:
protected:
	string _name; // 姓名
};
 
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
 
void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}
 
int main()
{
	Person p;
	Student s;
	// 编译报错:error C2248:"Student::_stuNum":无法访问 protected 成员
	// 解决方案:Display也变成Student 的友元即可
 
	Display(p, s);
 
	return 0;
}
2.3.2前置声明的必要性

不加前置声明会报下面的错

2.3.3 友元关系不能继承

因为友元关系不能继承,因此我们要给派生类也变成基类友元的友元


三、静态成员在继承中的特性

3.1 静态成员共享机制:父子共用同一份

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个派生类,都只有一个static成员实例

3.2 静态与非静态成员对比

3.2.1 非静态成员:实例独立

非静态成员的继承是父类和子类各一份,地址不一样

3.2.2 静态成员:类间共享

静态成员的继承是父类和子类共用同一份,地址也一样

**3.3 实践出真知:**静态成员继承实践案例

cpp 复制代码
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;
	// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的
	// 说明派⽣类继承下来了,⽗派⽣类对象各有⼀份
	cout << &p._name << endl;
	cout << &s._name << endl;
	// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的
	// 说明派⽣类和基类共⽤同⼀份静态成员
	cout << &p._count << endl;
	cout << &s._count << endl;
	// 公有的情况下,⽗派⽣类指定类域都可以访问静态成员
	cout << Person::_count << endl;
	cout << Student::_count << endl;

	return 0;
}

运行结果:


四、单继承 vs 多继承(以及菱形继承问题详解)

事先说明:多继承是个大坑!!!

4.1 单继承 vs 多继承

4.1.1 概念对比
4.1.2 实战
cpp 复制代码
class Person
{
public:
	string _name; // 姓名
};

class Student : virtual public Person //virtual虚拟继承在腰部
{
protected:
	int _num; //学号
};

class Teacher : virtual public Person
{
protected:
	int _id; // 职⼯编号
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

4.2 菱形继承问题详解

4.2.1 菱形继承的概念
4.2.2 菱形继承的数据冗余与二义性问题

基类数据越多,这两个问题越严重

数据冗余:如下图所示,Person有两个

二义性:访问不明确~> 指定类域勉强解决

4.2.3 虚继承解决方案

菱形继承------多继承延伸的坑

多继承不是问题,多继承实现的菱形继承才是问题

**因此设计了"菱形虚拟继承"来解决,**下面我们会介绍虚继承

4.2.4 虚继承机制与virtual关键字

关键词virtual加在腰部位置,如下图所示

都加上virtual可不可以?------当然不行

换个说法,药能多吃吗?会影响底层的空间模型,能编译通过但底层空间会乱

虚继承太复杂了,无论是使用还是底层,都太复杂

不要玩菱形继承!!!当然,菱形继承也是有应用的,库里面的IO库就是搞成菱形继承的,IO库的使用会专门在IO库讲

4.2.5 菱形继承的问题
4.2.6 实战
cpp 复制代码
class Person
{
public:
	Person(const char* name)
		:_name(name)
	{ }
	string _name; // 姓名
};

class Student : virtual public Person
{
public:
	Student(const char* name, int num)
		:Person(name)
		, _num(num)
	{
	}
protected:
	int _num; //学号
};

class Teacher : virtual public Person
{
public:
	Teacher(const char* name, int id)
		:Person(name)
		, _id(id)
	{
	}
protected:
	int _id; // 职⼯编号
};

// 不要去玩菱形继承
class Assistant : public Student, public Teacher
{
public:
	Assistant(const char* name1, const char* name2, const char* name3)
		:Person(name3)
		,Student(name1, 1)
		,Teacher(name2, 2)
	{
	}
protected:
	string _majorCourse; // 主修课程
};

int main()
{
	// 思考⼀下这⾥a对象中_name是"张三", "李四", "王五"中的哪⼀个?
	Assistant a("张三", "李四", "王五");
	return 0;
}

运行结果:

4.2.7 可以设计出多继承,不建议设计出菱形继承

我们可以设计出多继承,但是不建议设计出菱形继承,因为菱形虚拟继承以后,无论是使用还是底层都会复杂很多。当然有多继承语法支持,就一定存在会设计出菱形继承,像Java是不支持多继承的,就避开了菱形继承

4.3 IO库中的菱形虚拟继承

4.4 多继承中的指针偏移问题

4.4.1 题目
cpp 复制代码
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };
 
int main()
{
	Derive d;
	Base1* p1 = &d;
	Base2* p2 = &d;
	Derive* p3 = &d;
 
	return 0;
}

下面说法正确的是( )

A. p1 == p2 == p3

B. p1 < p2 < p3

C. p1 == p3 != p2

D. p1 != p2 != p3

4.4.2 答案解析

正确答案: C


五、继承与组合设计模式对比

5.1 基本概念:is-a vs has-a

  • public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象
  • 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象
  • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-boxreuse)。术语"白箱"是相对可视性而言:在继承方式中,基类的内部细节对派生类可见。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
  • 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-boxreuse),因为对象的内部细节是不可见的。对象只以"黑箱"的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装
  • 先使用组合,而不是继承。实际尽量多去用组合,组合的耦合度低,代码维护性好。不过也不太那么绝对,类之间的关系就适合继承(is-a)那就用继承,另外要实现多态,也必须要继承。类之间的关系既适合用继承(is-a)也适合组合(has-a),就用组合

5.2 继承与组合关系对比

5.3 实践

cpp 复制代码
// 继承和组合
// Tire(轮胎)和Car(⻋)更符合has-a的关系 
class Tire {
protected:
	string _brand = "Michelin"; // 品牌 
	size_t _size = 17; // 尺寸
};
 
class Car {
protected:
	string _colour = "白色"; // 颜色
	string _num = "陕ABIT00"; // 车牌号 
	Tire _t1; // 轮胎 
	Tire _t2; // 轮胎 
	Tire _t3; // 轮胎 
	Tire _t4; // 轮胎 
};
 
class BMW : public Car {
public:
	void Drive() { cout << "好开-操控" << endl; }
};
 
// Car和BMW/Benz更符合is-a的关系 
class Benz : public Car {
public:
	void Drive() { cout << "好坐-舒适" << endl; }
};
 
template<class T>
class vector
{};
// stack和vector的关系,既符合is-a,也符合has-a 
 
template<class T>
class stack : public vector<T>
{};
 
template<class T>
class stack
{
public:
	vector<T> _v;
};
 
int main()
{
	return 0;
}

is-a用继承,has-a用组合

cpp 复制代码
// 继承和组合
// Tire(轮胎)和Car(⻋)更符合has-a的关系 
// 轮胎类
class Tire {
protected:
    string _brand = "Michelin";
    size_t _size = 17;
};
 
// 汽车基类
class Car {
protected:
    string _colour = "白色";
    string _num = "陕ABIT00";
    Tire _tires[4];  // 使用数组更合适
};
 
// 派生类
class BMW : public Car {
public:
    void Drive() { cout << "好开-操控" << endl; }
};
 
class Benz : public Car {
public:
    void Drive() { cout << "好坐-舒适" << endl; }
};
 
// 正确的stack实现
template<class T>
class Stack {
private:
    vector<T> _v;  // 组合关系
public:
    void push(const T& x) { _v.push_back(x); }
    void pop() {
        if (!_v.empty())
            _v.pop_back();
    }
    T& top() {
        if (!_v.empty())
            return _v.back();
        throw std::out_of_range("Stack is empty");
    }
    bool empty() const { return _v.empty(); }
    size_t size() const { return _v.size(); }
};
 
int main() {
    BMW bmw;
    bmw.Drive();
 
    Stack<int> s;
    s.push(1);
    s.push(2);
    cout << s.top() << endl;  // 输出2
 
    return 0;
}

5.4 继承 vs 组合

5.4.1 白盒复用与黑盒复用

白盒测试:更加难,一般由研发人员写并且测试,看得见、透明------保护、私有都可使用;

黑盒测试:看不见,不透明;

白盒 / 黑盒好坏的依据是从软件设计角度出发的

8.4.2 软件设计中的选择策略

高内聚,低耦合------可维护性(其中一个修改,另一个不受影响)

8.4.3 模块

打成一个个模块,哪个出问题改哪个,不受影响

组件:静态库、动态库------不可执行的二进制文件

1、编译时间降低;

2、看不到源码(二进制编译)

8.4.4继承和组合哪个更好?

实践的角度:优先使用组合;既符合继承也符合组合,我们使用组合; 但是要注意:是"优先使用组合",不是必须使用,但是像多态这些需要继承的地方还是要用继承


完整代码演示

cpp 复制代码
#include<iostream>
using namespace std;

//class Student;
//
//class Person
//{
//public:
//	//友元关系不能被子类继承
//	friend void Display(const Person& p, const Student& s);
//protected:
//	string _name; // 姓名
//};
//
//class Student : public Person
//{
//	friend void Display(const Person& p, const Student& s);
//protected:
//	int _stuNum; // 学号
//};
//void Display(const Person& p, const Student& s)
//{
//	cout << p._name << endl;
//	cout << s._stuNum << endl;
//}
//
//int main()
//{
//	Person p;
//	Student s;
//	// 编译报错:error C2248: "Student::_stuNum": ⽆法访问 protected 成员
//	// 解决⽅案:Display也变成Student 的友元即可
//	Display(p, s);
//
//	return 0;
//}

//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;
//	// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的
//	// 说明派⽣类继承下来了,⽗派⽣类对象各有⼀份
//	cout << &p._name << endl;
//	cout << &s._name << endl;
//	// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的
//	// 说明派⽣类和基类共⽤同⼀份静态成员
//	cout << &p._count << endl;
//	cout << &s._count << endl;
//	// 公有的情况下,⽗派⽣类指定类域都可以访问静态成员
//	cout << Person::_count << endl;
//	cout << Student::_count << endl;
//
//	return 0;
//}

//菱形继承
//菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下⾯的对象成员模型构造,可以
//看出菱形继承有数据冗余和⼆义性的问题,在Assistant的对象中Person成员会有两份。⽀持多继承就
//⼀定会有菱形继承,像Java就直接不⽀持多继承,规避掉了这⾥的问题,所以实践中我们也是不建议
//设计出菱形继承这样的模型的
//class Person
//{
//public:
//	string _name; // 姓名
//};
////
//class Student : virtual public Person //virtual虚拟继承在腰部
//{
//protected:
//	int _num; //学号
//};

//class Teacher : virtual public Person
//{
//protected:
//	int _id; // 职⼯编号
//};
//
//class Assistant : public Student, public Teacher
//{
//protected:
//	string _majorCourse; // 主修课程
//};
//
//int main()
//{
//	// 编译报错:error C2385: 对"_name"的访问不明确
//	Assistant a;
//	a._name = "peter";
//	// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决
//	a.Student::_name = "xxx";
//	a.Teacher::_name = "yyy";
//
//	return 0;
//}

//class Person
//{
//public:
//	Person(const char* name)
//		:_name(name)
//	{ }
//	string _name; // 姓名
//};
//
//class Student : virtual public Person
//{
//public:
//	Student(const char* name, int num)
//		:Person(name)
//		, _num(num)
//	{
//	}
//protected:
//	int _num; //学号
//};
//
//class Teacher : virtual public Person
//{
//public:
//	Teacher(const char* name, int id)
//		:Person(name)
//		, _id(id)
//	{
//	}
//protected:
//	int _id; // 职⼯编号
//};
//
//// 不要去玩菱形继承
//class Assistant : public Student, public Teacher
//{
//public:
//	Assistant(const char* name1, const char* name2, const char* name3)
//		:Person(name3)
//		,Student(name1, 1)
//		,Teacher(name2, 2)
//	{
//	}
//protected:
//	string _majorCourse; // 主修课程
//};
//
//int main()
//{
//	// 思考⼀下这⾥a对象中_name是"张三", "李四", "王五"中的哪⼀个?
//	Assistant a("张三", "李四", "王五");
//	return 0;
//}

// 继承和组合
// Tire(轮胎)和Car(⻋)更符合has-a的关系 
// 轮胎类
//class Tire {
//protected:
//    string _brand = "Michelin";
//    size_t _size = 17;
//};
//
//// 汽车基类
//class Car {
//protected:
//    string _colour = "白色";
//    string _num = "陕ABIT00";
//    Tire _tires[4];  // 使用数组更合适
//};
//
//// 派生类
//class BMW : public Car {
//public:
//    void Drive() { cout << "好开-操控" << endl; }
//};
//
//class Benz : public Car {
//public:
//    void Drive() { cout << "好坐-舒适" << endl; }
//};
//
//// 正确的stack实现
//template<class T>
//class Stack {
//private:
//    vector<T> _v;  // 组合关系
//public:
//    void push(const T& x) { _v.push_back(x); }
//    void pop() {
//        if (!_v.empty())
//            _v.pop_back();
//    }
//    T& top() {
//        if (!_v.empty())
//            return _v.back();
//        throw std::out_of_range("Stack is empty");
//    }
//    bool empty() const { return _v.empty(); }
//    size_t size() const { return _v.size(); }
//};
//
//int main() {
//    BMW bmw;
//    bmw.Drive();
//
//    Stack<int> s;
//    s.push(1);
//    s.push(2);
//    cout << s.top() << endl;  // 输出2
//
//    return 0;
//}

//实现多态的两个重要条件
// 必须是基类的指针或者引⽤调⽤虚函数
// 被调⽤的函数必须是虚函数,并且完成了虚函数重写 / 覆盖。

// 虚函数的重写/覆盖:派⽣类中有⼀个跟基类完全相同的虚函数
// (即派⽣类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),
// 称派⽣类的虚函数重写了基类的虚函数

//class Person
//{
//public:
//	virtual void BuyTicket()//虚函数
//	{
//		cout << "买票-全价" << endl;
//	}
//};
//
//class Student :public Person
//{
//public:
//	virtual void BuyTicket()
//	{
//		cout << "买票-打折" << endl;
//	}
//};
//
//void Func(Person* ptr)
//{
//	// 这⾥可以看到虽然都是Person指针Ptr在调⽤BuyTicket
//	// 但是跟ptr没关系,⽽是由ptr指向的对象决定的。
//	ptr->BuyTicket();
//}

//int main()
//{
//	Person ps;
//	Student st;
//
//	Func(&ps);
//	Func(&st);
//
//	return 0;
//}

//class Animal
//{
//public:
//	virtual void talk() const
//	{ }
//};
//
//class Dog : public Animal
//{
//public:
//	//重写实现,可以不加virtual
//	virtual void talk() const
//	{
//		std::cout << "汪汪" << std::endl;
//	}
//};
//
//class Cat : public Animal
//{
//public:
//	virtual void talk() const
//	{
//		std::cout << "(>^ω^<)喵" << std::endl;
//	}
//};
//
////必须是指针或者引用
//void letsHear(const Animal& animal)
//{
//	animal.talk();
//}
//
//int main()
//{
//	Cat cat;
//	Dog dog;
//
//	letsHear(cat);
//	letsHear(dog);
//
//	return 0;
//}

//class A {};
//class B : public A {};
//
//class Person {
//public:
//	//协变(了解)
//	//	派⽣类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引⽤,
//	//  派⽣类虚函数返回派⽣类对象的指针或者引⽤时,称为协变
//	virtual A* BuyTicket()
//	{
//		cout << "买票-全价" << endl;
//		return nullptr;
//	}
//};
//
//class Student : public Person {
//public:
//	virtual B* BuyTicket()
//	{
//		cout << "买票-打折" << endl;
//		return nullptr;
//	}
//};
//
//void Func(Person* ptr)
//{
//	ptr->BuyTicket();
//}
//
//int main()
//{
//	Person ps;
//	Student st;
//
//	Func(&ps);
//	Func(&st);
//	
//	return 0;
//}

//class A
//{
//public:
//	virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
//	virtual void test() { func(); }
//};
//class B : public A
//{
//public:
//	//多态是:不加virtual重写是重写虚函数的实现部分
//	//相当于是基类的函数声明部分+派生类的函数实现部分
//	//即 virtual void func(int val = 1)+{ std::cout << "B->" << val << std::endl; }
//	void func(int val = 0) { std::cout << "B->" << val << std::endl; }
//};
//int main(int argc, char* argv[])
//{
//	B* p = new B;
//	//多态调用
//	p->test();
//	//普通调用
//	p->func();
//
//	return 0;
//}

//class A
//{
//public:
//	virtual ~A()
//	{
//		cout << "~A()" << endl;
//	}
//};
//
//class B : public A {
//public:
//	//建议加上virtual
//	//virtual ~B()
//	 ~B()
//	{
//		cout << "~B()->delete:" << _p << endl;
//		delete _p;
//	}
//protected:
//	int* _p = new int[10];
//};
//
//// 只有派⽣类Student的析构函数重写了Person的析构函数,下⾯的delete对象调⽤析构函数,才能
////构成多态,才能保证p1和p2指向的对象正确的调⽤析构函数。
//
////基类只要保证析构函数是虚函数,下面这下些场景就不会存在内存泄露
//int main()
//{
//	A* ptr1 = new B;
//	delete ptr1;
//
//	A* ptr2 = new A;
//	delete ptr2;
//
//	return 0;
//}

//override检查虚函数
//class Car {
//public:
//	//virtual void Dirve()//函数名写错、参数写错等导致⽆法构成重写
//	virtual void Drive()
//	{ }
//	//不想让派⽣类重写这个虚函数,那么可以⽤final去修饰
//	virtual void Drive() final
//	{ }
//};
//class Benz :public Car {
//public:
//	virtual void Drive() override { cout << "Benz-舒适" << endl; }
//};
//int main()
//{
//	return 0;
//}

//设计一个不能被继承的类
//class Base
//class Base final
//{
//public:
//	void func5() { cout << "Base::func5" << endl; }
//protected:
//	int a = 1;
//private:
//	//构造函数私有的类不能被继承
//	Base()
//	{}
//};
//
//class Derive :Base
//{
//
//};

//int main()
//{
//	Derive d;
//
//	return 0;
//}

结尾

往期回顾:

《C++ 继承》三大面向对象编程------继承:代码复用与功能扩展的核心机制

结语:继承作为 C++ 面向对象编程的三大特性(封装、继承、多态)之一,是连接 "通用类" 与 "专用类" 的桥梁。它通过代码复用减少重复开发,通过功能扩展满足个性化需求,同时又通过访问控制和继承方式保障代码的安全性与灵活性。掌握继承的概念、关键要素与使用原则,不仅能提升代码效率,更能帮助我们构建逻辑清晰、易于维护的面向对象系统

相关推荐
cookies_s_s3 小时前
项目--缓存系统(C++)
c++·缓存
晨陌y3 小时前
从 “不会” 到 “会写”:Rust 入门基础实战,用一个小项目串完所有核心基础
开发语言·后端·rust
筱砚.3 小时前
【STL——set与multiset容器】
开发语言·c++·stl
Fanfffff7203 小时前
从TSX到JS:深入解析npm run build背后的完整构建流程
开发语言·javascript·npm
Elias不吃糖3 小时前
C++ 中的浅拷贝与深拷贝:概念、规则、示例与最佳实践(笔记)
开发语言·c++·浅拷贝·深拷贝
LEEBELOVED3 小时前
R语言高效数据处理-3个自定义函数笔记
开发语言·笔记·r语言
朝新_3 小时前
【SpringMVC】SpringMVC 请求与响应全解析:从 Cookie/Session 到状态码、Header 配置
java·开发语言·笔记·springmvc·javaee
2501_938782093 小时前
从实例到单例:Objective-C 单例类的线程安全实现方案
开发语言·macos·objective-c
浪裡遊3 小时前
css面试题1
开发语言·前端·javascript·css·vue.js·node.js