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

【C++知识点总结全系列 (08)】:面向对象编程OOP

这里写目录标题

  • 1、OOP概述
    • (1)面向对象四大特征
      • A.抽象
      • B.封装
      • C.继承
      • D.多态
    • (2)构造函数
      • A.What(什么是构造函数)
      • B.Why(构造函数的作用)
      • C. Which(有哪些构造函数)
    • (3)析构函数
      • A.What(什么是析构函数)
      • B.Why(析构函数的作用)
    • (4) =default 和 =delete
      • A.Why
      • B.How
  • 2、继承
    • (1)What(什么是继承)
    • (2)Why(继承的作用)
    • (3)How(如何使用继承)
    • (4)虚函数
      • A.What
      • B.Why(虚函数的作用)
      • C.使用虚函数的注意事项
    • (5)虚析构函数
      • A.Why(虚析构函数的作用)
      • B.What(什么是虚析构函数)
    • (6)抽象基类
      • A.What(纯虚函数&抽象基类)
      • B.抽象基类的特点
    • (7)继承关系中的访问控制
      • A.类中成员的访问权限
      • B.类继承中的访问权限
      • C.派生类向基类转换的权限问题(向上转型)
      • D.友元在继承中的访问权限
    • (8)多重继承
      • A.横向多重继承:
      • B.纵向多重继承:
      • C.联合多重继承:
    • (9)虚继承
      • A.What(什么是虚继承、虚基类)
      • B.Why(虚继承的作用)
      • C.How
  • 3、多态(编译时多态)-重载
    • (1)输入输出运算符重载
    • (2)算术运算符:+、-、*、/
    • (3)关系运算符:>、>=、==、<=、!=
    • (4)赋值运算符
    • (5)下标运算符
    • (6)递增递减运算符
    • (7)解引用运算符和箭头运算符
    • (8)重载new和delete
      • A.Why(为什么要重载new和delete运算符)
      • B.How(如何重载)
    • (9)函数调用运算符
      • A.什么是函数对象
      • B.函数对象lamda

1、OOP概述

(1)面向对象四大特征

A.抽象

把一类东西的共同属性和方法提取到一个类中,而不关注具体如何实现

B.封装

对象属性对外接是隐藏的,只能通过对象的方法进行访问和修改

通过类实现对现实情况的抽象,使用private权限实现对类的封装

class Caculater{ 
private:int *Id; // 对数据进行封装,只有类能访问,外部智能通过提供的函数访问 
public:Caculater() { // 默认构造函数std::srand(std::time(nullptr)); int randomNumber = std::rand(); Id = new int;*Id = randomNumber; }Caculater(Caculater &caculater) { // 拷贝构造函数Id = caculater.getId();}~Caculater() { delete Id; } void showId(){if(Id) cout << "Id = " << *Id << endl; else cout << "nullptr" << endl;}template <class T, class U> auto add(const T &t, const U &u) const {return t + u; }int *getId() {return Id; }
};
template <typename T> 
void printf(const T &&t) { cout << t << endl; } 
int main(){// 隐式调用默认构造函数 Caculater caculater01; // 调用成员函数printf(caculater01.add(20, 10.1)); // 打印:30.1return 1;
}

C.继承

派生类可以继承基类的非私有属性和方法,而无需自己重新定义

D.多态

静态多态:编译时多态

函数重载或模板重载

动态多态:运行时多态

父类型可以指向其子类型的实例,使子类型对同一方法作出不同的回应,也就是所谓的动态绑定

动态绑定

通过基类指针或引用调用虚函数时,会根据实际对象的类型来确定要调用的函数版本 基类的引用或指针调用虚函数 virtual 时发生动态绑定

(2)构造函数

A.What(什么是构造函数)

一种特殊的成员函数:
一方面它没有返回值
另一方面它和类名相同

B.Why(构造函数的作用)

主要作用是创建初始化类对象:为对象的成员赋初始值、执行一些必要的初始化操作等

C. Which(有哪些构造函数)

无参构造函数

如果没有显式定义任何构造函数,编译器将自动生成一个默认构造函数(合成默认构 造函数)。它不带任何参数,并对类的成员进行默认初始化

带参构造函数

参数列表不为空的构造函数

Student(string strStuName, int iAge )
{	m_strStuName = strStuName;m_iAge = iAge;
}
Student(string strStuName, int iAge = 24)
{m_strStuName = strStuName;m_iAge = iAge;
}

拷贝构造函数

将参数中的对象深拷贝给当前对象,如果存在指针数据,一定要重新开辟空间,然后赋值

Student(const Student &stuObj)
{	this->strStuName = stuObj.getName();this->iAge = stuObj.getAge();this->ptrScore = new float(strObj.getScore());
}

移动构造函数

实现了数据的转移,相当于“鸠占鹊巢,还得把鹊赶尽杀绝”,移动赋值运算符同理

Student( Student &stuObj)
{this->strStuName = stuObj.getName();stuObj.setName("");this->iAge = stuObj.getAge();stuObj.setAge(0);this->ptrScore = new float(strObj.getScore());stuObj.score = nullptr;
}

转换构造函数

本质是带一个参数的构造函数,在需要时可以将其他类型的对象隐式转换为当前类的对象

(3)析构函数

A.What(什么是析构函数)

一个特殊的成员函数:
一方面,对象被销毁时自动调用,它不能是delete的
另一方面,和构造函数一样没有返回值

~Student(){delete this->ptrScore;ptrScore = nullptr;
}

B.Why(析构函数的作用)

  • 可用于释放动态分配的内存
  • 可用于关闭文件、数据库连接和网络连接资源
  • 解锁互斥量或释放其它同步资源

(4) =default 和 =delete

A.Why

更精确地控制类的成员函数的行为,提高代码的可读性和安全性

B.How

class MyClass { 
public:// 默认构造函数 MyClass() = default; // 默认析构函数~MyClass() = default; // 禁用拷贝构造函数MyClass(const MyClass&) = delete; // 禁用赋值运算符MyClass& operator=(const MyClass&) = delete; // 禁用移动构造函数MyClass(MyClass&&) = delete; // 使用默认移动赋值运算符MyClass& operator=(MyClass&&) = default;
};

2、继承

(1)What(什么是继承)

派生类从基类继承属性和方法

(2)Why(继承的作用)

重用性:派生类可以继承基类的属性和方法,减少重复代码的编写
扩展性:派生类可以在继承基类的基础上添加新的属性和方法,实现更强大的功能
多态性:实现对不同派生类的统一处理

说明:派生类的属性和基类重名时,会自动隐藏基类的成员

(3)How(如何使用继承)

class Derived: public Base{...}

(4)虚函数

A.What

一种特殊的成员函数,在基类中声明并用于被派生类重写的特殊成员函数

B.Why(虚函数的作用)

允许在运行时根据对象的实际类型来调用相应的函数实现,以实现多态性

C.使用虚函数的注意事项

  • override 关键字的使用:只有虚函数才能用 override 修饰(在派生类中使用该关键字)
  • 虚函数与默认实参:如果虚函数使用了默认实参, 则基类和派生类中定义的默认 实参应该一致,基类和派生类的虚函数必须接受相同的形参列表,否则无法实现动 态绑定
  • 回避虚函数机制:通常情况下,只有在成员函数(或友元)中使用作用域运算符来回避虚函数机制
    在这里插入图片描述

(5)虚析构函数

A.Why(虚析构函数的作用)

当我们使用基类指针或引用指向派生类对象,并且在基类指针或引用上删除该对象时, 如果基类的析构函数不是虚函数,则只会调用基类的析构函数,而不会调用派生类的 析构函数。这可能导致资源泄漏和未定义行为

B.What(什么是虚析构函数)

virtual ~Base();

在这里插入图片描述

注意:虚析构函数将阻止合成的移动构造函数和合成的移动赋值运算符,因为默认只 进行浅拷贝,而动态内存分配下的浅拷贝可能导致内存泄漏、悬挂指针等问题

(6)抽象基类

A.What(纯虚函数&抽象基类)

纯虚函数:
在这里插入图片描述
抽象基类:

含有纯虚函数的类叫做抽象基类

B.抽象基类的特点

  • 至少包含一个纯虚函数
  • 不能实例化对象,只能用作其他类的基类
  • 继承抽象基类的派生类必须实现纯虚函数,否则派生类也会称为抽象基类
  • 抽象基类可以包含非纯虚函数,提供默认或共享的功能

(7)继承关系中的访问控制

A.类中成员的访问权限

  • public:类的对象(外部)可以访问,派生类也可以访问
  • protected:类的对象(外部)不能访问,派生类可以访问
  • private:类的对象(外部)不能访问,派生类也不可以访问

B.类继承中的访问权限

  • public继承:public->public, protected->protected

    派生类可以继承基类中的公有成员和受保护成员,并将其作为自己的公有成员和受保护成员

  • protected继承:public&protected->protected

    将基类中的公有成员和受保护成员作为派生类的受保护成员

  • private继承:public&protected->private

    将基类中的公有成员和受保护成员作为派生类的私有成员,使得派生类无法直接访问这些成员

C.派生类向基类转换的权限问题(向上转型)

在这里插入图片描述
在这里插入图片描述
注意:派生类的成员函数和友元函数中,可以进行向上转型

D.友元在继承中的访问权限

  • 友元不能被继承:友元函数和友元类类似于基类的私有成员
  • 派生类的友元不可直接访问基类成员(包括公有成员)
    在这里插入图片描述在这里插入图片描述

(8)多重继承

A.横向多重继承:

在这里插入图片描述

B.纵向多重继承:

在这里插入图片描述

C.联合多重继承:

在这里插入图片描述

因为 single 和 waiter 都继承了一个 worker 组件,因此 SingingWaiter 将包含两个 worker 组件,那么将派生类对象的地址赋给基类指针将出现二义性

那么如何解决二义性问题呢?我们知道程序的执行一定是具有确定性的,在上述情况下,我们能想到的是进行强制转换,如下所示:
在这里插入图片描述
很显然,上述这种强制转换确实能够解决因联合继承带来的二义性问题,但是每次都进行这样的强制转换过于繁琐,那么有没有简单的办法解决二义性问题呢?答案就是:虚继承,所谓的虚继承,就是让共享一个祖父类

(9)虚继承

A.What(什么是虚继承、虚基类)

  • 虚继承:

class Derived: public virtual Base, 如下例所示,展示了虚继承的形式

在这里插入图片描述

  • 虚基类

被声明为虚继承的基类被称为虚基类

B.Why(虚继承的作用)

  • 解决二义性冲突:当基类的指针指向孙子类的指针或引用时,会出现二义性,因为 孙子类对象包含多个祖父类对象,而虚继承只保留一个共享的祖父类
  • 减少内存消耗:因为孙子类只包含一个祖父类对象
  • 减少代码冗余:虚基类的成员只需在最终的派生类中定义一次

C.How

在这里插入图片描述

3、多态(编译时多态)-重载

(1)输入输出运算符重载

以友元的形式进行重载:

在这里插入图片描述

(2)算术运算符:+、-、*、/

以友元的形式重载:

在这里插入图片描述

(3)关系运算符:>、>=、==、<=、!=

以友元的形式重载:

在这里插入图片描述

(4)赋值运算符

在这里插入图片描述

(5)下标运算符

在这里插入图片描述

(6)递增递减运算符

前置版本:
在这里插入图片描述
后缀版本:
在这里插入图片描述

注意:显式调用后置版本(默认调用前置版本)

在这里插入图片描述

(7)解引用运算符和箭头运算符

箭头运算符和解引用运算符必须是类的成员函数

在这里插入图片描述

(8)重载new和delete

A.Why(为什么要重载new和delete运算符)

重载 new 运算符和 delete 运算符是为了对内存分配和释放过程进行自定义操作,以 满足特定的需求。通过重载这些运算符,我们可以提供自定义的内存管理方法,例如 使用自定义的内存池,跟踪内存分配和释放情况,或进行性能优化

B.How(如何重载)

在这里插入图片描述
在这里插入图片描述

(9)函数调用运算符

函数调用运算符必须是成员函数

A.什么是函数对象

如果类定义了调用运算符,则该类的对象称为函数对象

在这里插入图片描述

B.函数对象lamda

在这里插入图片描述

lamda的引用捕获:

在这里插入图片描述

注意:使用[&]可以引用捕获作用域内所有变量

lambda的值捕获:

在这里插入图片描述

注意:使用[=]可以值捕获作用域内所有变量

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 新火种AI|OpenAI的CEO又有新动作?这次他成立了AI健康公司
  • 如何利用java依赖jave-all-deps实现视频格式转换
  • 中英双语介绍美国的州:阿肯色州(Arkansas)
  • 在make类构建系统配置文件中定义函数宏
  • Laravel5+mycat 报错 “Packets out of order”
  • CTF-PWN-kernel-栈溢出(retuser rop pt_regs ret2dir)
  • EUC 2024 I. Disks
  • golang 项目打包部署环境变量设置
  • FPGA原型验证(七):如何选择、搭建原型验证平台?
  • Java-关键字(static,final)
  • redis数据库
  • ER模型理论和三范式
  • Infinitar链游新发展新机遇
  • 探索Qt的QVariant:灵活的数据交换机制
  • 无法下载 https://mirrors./ubuntu/dists/bionic/main/binary-arm64/Packages
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • 30天自制操作系统-2
  • 77. Combinations
  • git 常用命令
  • happypack两次报错的问题
  • JavaScript-Array类型
  • Java深入 - 深入理解Java集合
  • MobX
  • mysql innodb 索引使用指南
  • 阿里研究院入选中国企业智库系统影响力榜
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 解析带emoji和链接的聊天系统消息
  • 力扣(LeetCode)56
  • 聊聊springcloud的EurekaClientAutoConfiguration
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 学习笔记:对象,原型和继承(1)
  • 你对linux中grep命令知道多少?
  • # 达梦数据库知识点
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • %@ page import=%的用法
  • (2024)docker-compose实战 (9)部署多项目环境(LAMP+react+vue+redis+mysql+nginx)
  • (4) PIVOT 和 UPIVOT 的使用
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (k8s)Kubernetes本地存储接入
  • (独孤九剑)--文件系统
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (十七)Flink 容错机制
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (四)进入MySQL 【事务】
  • (转)socket Aio demo
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .NET C# 操作Neo4j图数据库
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .net core docker部署教程和细节问题
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .NET 反射 Reflect
  • .NET 中让 Task 支持带超时的异步等待