【C++核心篇】—— C++面向对象编程:继承、多态的相关语法和使用的详解,回顾学习看这一篇就足够了!!!

本文主要是介绍【C++核心篇】—— C++面向对象编程:继承、多态的相关语法和使用的详解,回顾学习看这一篇就足够了!!!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、继承
    • 1.继承的基本语法
    • 2. 继承的使用
    • 3. 继承中的同名成员的处理
    • 4. 多继承语法
    • 5. 菱形继承
  • 二、多态
    • 1. 多态的使用
    • 2. 多态的原理剖析
    • 3.纯虚函数和抽象类
    • 4.虚析构和纯虚析构


前言

在本篇文章中,主要是对C++的基础语法进行回顾学习,回顾学习C++的基本语法规则、数据类型、控制结构以及面向对象编程的基本思想。

一、继承

继承是面向对象三大特性之一,有些类与类之间存在特殊的关系,例如下图中:
在这里插入图片描述
定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。这个时候我们就可以考虑利用继承的技术,减少重复代码。

1.继承的基本语法

(1) 基本语法

继承的语法:class 子类 : 继承方式 父类
示例:

class Person{
public:void func(){}
public:int m_age;
};
class goodPerson : public Person{
public:void func(){}
public:string m_name;
};

总结:goodPerson 类同时拥有自身的m_name和父类中的m_age属性

2. 继承的使用

(1) 继承方式
继承方式一共有三种:公共继承保护继承私有继承,子类继承父类的方式和继承的权限如下图所示:
在这里插入图片描述
(2) 继承中的对象模型
问题:从父类继承过来的成员,哪些属于子类对象中?
回答:父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后访问不到(子类的内存大小 = 子类的非静态属性的内存 + 父类的非静态属性的内存大小

(3) 继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数
问题:父类和子类的构造和析构顺序是谁先谁后?
回答:继承中先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

3. 继承中的同名成员的处理

(1) 继承同名成员处理方式
问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
回答
a. 访问子类同名成员直接访问即可.
b. 访问父类同名成员需要加作用域
以上述为《继承的基本语法》的例示:

goodPerson  s;           //实例化对象
s.func();                //调用goodPerson 类中的fun()函数
s.Person::func();        //调用Person 类中的fun()函数

(2) 继承同名静态成员处理方式
问题:继承中同名的静态成员在子类对象上如何进行访问?
回答:静态成员和非静态成员出现同名,处理方式一致
对于静态的成员属性,要类内申明,类外定义

class Person{
public:static void func(){}static int m_age;
};
int Person :: m_age = 100;
class goodPerson : public Person{
public:static void func(){}string m_name;
};

对于静态成员函数,两种访问的方式(通过对象和通过类名)

goodPerson s;
s.func();   
s.Person::func();     //通过对象访问
goodPerson::func();                //通过类名访问
goodPerson::Person::func();     

4. 多继承语法

C++中允许一个类继承多个类
语法:class子类:继承方式父类1,继承方式父类2…

class goodPerson : public Person1, public Person1 {}

示例:goodPerson 继承了Person1,Person2中的m_age属性

goodPerson s
//加作用域才能
cout<<s.Person1::m_age<<endl;
cout<<s.Person2::m_age<<endl;

注意:多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议用多继承

5. 菱形继承

菱形继承概念
两个派生类继承同一个基类,又有某个类同时继承者两个派生类,这种继承被称为菱形继承,或者钻石继承 。典型的菱形继承案例:


示例
菱形继承带来的问题

a. 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
解决:加作用域(同上子类访问父类同名成员的方法

b. 草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
解决:利用虚继承来解决(在继承前加上vritual的关键字)

class Animal {
public:int m_Age;
};
//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal{};
class Tuo : virtual public Animal{};
class SheepTuo : public Sheep , public Tuo{};
//使用时
SheepTuo st;
st.Sheep::m_Age=100;
st.Tuo::m_Age=200;
cout << st.Sheep::m_Age << endl;  //输出是100
cout << st.Tuo::m_Age << endl;    //输出是200
cout << st.m_Age << endl;   //虚继承的作用,此时st的年龄为200

总结
菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义利用,虚继承可以解决菱形继承问题

二、多态

多态是C++面向对象三大特性之一,多态的优点
a. 代码组织结构清晰
b. 可读性强
c. 利于前期和后期的扩展以及维护

1. 多态的使用

(1) 多态分为两类
a. 静态多态: 函数重载和运算符重载属于静态多态,复用函数名
b. 动态多态: 派生类和虚函数实现运行时多态
(2) 两者的区别
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址

(3) 多态满足条件
a. 有继承关系
b. 子类重写父类中的虚函数
重写:函数返回值类型函数名参数列表完全一致称为重写

(4) 多态使用条件:父类指针或引用指向子类对象
虚函数的语法:函数前面加上virtual关键字,变成虚函数

class Animal {
public:virtual void Speak() {cout << "动物在讲话" << endl; }
}class Cat : public Animal {     //1. 有继承关系
public:virtual void Speak() {       //2. 子类重写父类的虚函数cout << "小猫在讲话" << endl; }
}//3. 父类指针或引用指向子类对象
void DoSpeak(Animal & animal){   animal.speak();
}
int main(){Cat cat;DoSpeak(cat);   //输出的是:小猫在讲话,因为传入的是猫对象,多态的作用就会调用猫对象的Speak成员函数函数system("pause");return 0;
}

2. 多态的原理剖析

3.纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数

纯虚函数语法:virtual返回值类型函数名(参数列表)=0; 当类中有了纯虚函数,这个类也称为抽象类

抽象类特点
a. 无法实例化对象(不能使用该类创建对象)
b. 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
示例:

class Animal {
public:virtual void Speak() = 0;
}class Cat : public Animal {     //1. 有继承关系
public:virtual void Speak() {       //2. 子类重写父类的虚函数cout << "小猫在讲话" << endl; }
}class Dog : public Animal {     //1. 有继承关系
public:virtual void Speak() {       //2. 子类重写父类的虚函数cout << "小狗在讲话" << endl; }
}//使用
void test01(){Animal * animal = NULL;//animal = new Animal ;//错误,抽象类无法实例化对象animal = new Cat ;  //在堆区开辟内存animal ->Speak();  //输出:小猫在讲话delete animal ;//记得销毁
}
int	main(){test01();system("pause");return0;
}

4.虚析构和纯虚析构

问题:多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决:将父类中的析构函数改为虚析构或者纯虚析构

(1) 虚析构

析构语法:virtual ~类名() {}

示例

class Animal {
public:Animal(){cout << "Animal构造函数调用!" << endl;}virtual void Speak(){cout << "动物在讲话" << endl; }virtual ~Animal(){      //父类的虚析构cout << "Animal虚析构函数调用!" << endl;}
}class Cat : public Animal {     //1. 有继承关系
public:Cat(stringname){cout<<"Cat构造函数调用!"<<endl;m_Name = new string(name);}virtual void Speak(){     cout<< *m_Name << "小猫在说话!" <<endl;}~Cat(){cout<<"Cat析构函数调用!"<<endl;if(this->m_Name != NULL){delete m_Name;m_Name = NULL;}
public:string *m_Name;
}//验证:
void test01(){Animal *animal = new Cat("Tom");//animal = new Animal ;//错误,抽象类无法实例化对象animal->Speak();delete animal;
}
int	main(){test01();system("pause");return0;
}

总结
不使用虚构函数时:通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏/
解决:给基类增加一个虚析构函数,虚析构函数就是用来解决通过父类指针释放子类对象

(2) 纯虚析构

纯虚析构语法:virtual ~类名() = 0;

作用和虚析构一样,使用上面使用存虚函数的语法,示例

class Animal {
public:Animal(){cout << "Animal构造函数调用!" << endl;}virtual void Speak() = 0virtual ~Animal() = 0}class Cat : public Animal {     //1. 有继承关系
public:Cat(stringname){cout<<"Cat构造函数调用!"<<endl;m_Name = new string(name);}virtual void Speak(){     cout<< *m_Name << "小猫在说话!" <<endl;}~Cat(){cout<<"Cat析构函数调用!"<<endl;if(this->m_Name != NULL){delete m_Name;m_Name = NULL;}
public:string *m_Name;
}//验证:
void test01(){Animal *animal = new Cat("Tom");animal->Speak();delete animal;
}
int	main(){test01();system("pause");return0;
}

注意:纯虚析构,该类属于抽象类,无法实例化对象

虚析构和纯虚析构共性
可以解决父类指针释放子类对象
都需要有具体的函数实现
虚析构和纯虚析构区别
如果是纯虚析构,该类属于抽象类,无法实例化对象

总结
1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
2.如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
3.拥有纯虚析构函数的类也属于抽象类

这篇关于【C++核心篇】—— C++面向对象编程:继承、多态的相关语法和使用的详解,回顾学习看这一篇就足够了!!!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1095106

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<