4 - C++ 进阶知识

1、并行和并发

定义

  • 并行 :指同时执行多个任务,通常需要多个处理器或核心。例如,在多核 CPU 上,多个线程可以同时在不同核心上执行。
  • 并发 :指在同一时间段内处理多个任务,但不一定同时执行。可以通过时间片轮转(如单核CPU 上的线程切换)来实现。

资源使用

  • 并行 :通常利用多个核心或处理器来提高性能。
  • 并发 :可以在单核心或多核心系统中实现,侧重于任务的切换和管理。

实现方式

  • 并行 :常通过多线程、并行计算框架等来实现。
  • 并发 :通过线程管理、事件驱动等方式来实现。

2、epoll

epollLinux 下的一种高效的 I/O 事件通知机制,主要用于处理大量并发连接的网络编程。它提供了一种非阻塞的方式来监视多个文件描述符(如 sockets)的状态变化,常用于高性能的服务器程序。

3、多路复用

多路复用是一种技术,允许多个信号或数据流通过共享的传输介质进行同时传输。在计算机网络和操作系统中,多路复用通常用于提高资源利用率,减少延迟和提升性能。

基本概念

  • 共享资源 :多个数据流或信号可以通过同一物理通道(如网络连接、文件描述符)进行传输。

  • 提高效率 :通过同时处理多个信号,避免了资源的浪费,提升了系统的吞吐量。

  • 事件驱动 :在某些情况下,多路复用允许程序在等待事件发生时进行其他操作,从而提高了应用的响应性。

类型

  • 时间多路复用(TDM:在不同的时间片中传输不同的数据流。

  • 频率多路复用(FDM:在不同的频率上同时传输多个信号。

  • 代码多路复用(CDM:使用不同的代码对信号进行区分,同时在同一频道上传输。

4、Redis

Redis 是一个开源的、基于内存的 NoSQL 数据库,主要用于实现高效的数据存储与检索。它支持多种数据结构,并且具备高性能和多种应用场景,常用作缓存、消息队列、会话存储等。

实例对比

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL
);

INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com');
INSERT INTO users (username, email) VALUES ('bob', 'bob@example.com');

SELECT email FROM users WHERE username = 'alice';

#输出结果
#alice@example.com
#使用键值对或哈希结构来存储用户信息。

HSET user:alice username "alice" email "alice@example.com"
HSET user:bob username "bob" email "bob@example.com"

HGET user:alice email

#输出结果
#alice@example.com

5、设计模式

设计模式是对常见软件设计问题的解决方案的总结与提炼。是经过验证的、可复用的解决方案,旨在帮助开发者更有效地解决特定场景下的设计问题,提升代码的可维护性、可读性和可扩展性。

  1. 创建型模式 :处理对象的创建过程,旨在将对象的创建过程与对象的使用分离

    • 单例模式(Singleton :确保某一个类只有一个实例,并提供一个全局的访问点。

    • 工厂方法模式(Factory Method :定义一个接口用于创建对象,但让子类决定实例化哪一个类。

    • 抽象工厂模式(Abstract Factory :提供一个接口,创建一系列相关或相互依赖的对象,而不指定具体的类。

    • 建造者模式(Builder :将复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。

    • 原型模式(Prototype :通过复制现有对象来创建新对象,而不是通过类实例化。

  2. 结构型模式 :处理对象组合问题。

    • 适配器模式(Adapter:将一个类的接口转换成客户希望的另一个接口,使得原本因接口不兼容而无法一起工作的类可以协同工作。

    • 桥接模式(Bridge:将抽象部分与它的实现部分分离,使它们都可以独立变化。

    • 装饰器模式(Decorator:动态地给一个对象添加一些额外的职责,比生成子类更灵活。

    • 组合模式(Composite:将对象组合成树形结构以表示“部分-整体”层次结构,使得用户对单个对象和组合对象具有一致的操作。

    • 外观模式(Facade:为子系统中的一组接口提供一个一致的界面,定义一个高层接口,使得子系统更加容易使用。

    • 享元模式(Flyweight:运用共享技术有效地支持大量细粒度对象的复用。

    • 代理模式(Proxy:为其他对象提供一个代理以控制对这个对象的访问。

  3. 行为型模式 :处理对象之间的通信和交互问题。

    • 策略模式(Strategy:定义一系列算法,把它们封装起来,使它们可以互相替换,客户端可以通过使用策略接口来实现不同的算法。

    • 观察者模式(Observer:定义对象间的一种一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知并自动更新。

    • 责任链模式(Chain of Responsibility:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合。

    • 模板方法模式(Template Method:定义一个操作的算法骨架,而将某些步骤延迟到子类中实现。

    • 命令模式(Command:将请求封装成对象,使得可以用不同的请求来参数化其他对象、队列请求、记录日志,以及支持可撤销的操作。

    • 迭代器模式(Iterator:提供一种方法顺序访问一个集合中的各个元素,而不暴露其内部表示。

    • 状态模式(State:允许对象在内部状态改变时改变它的行为,使得对象看起来像是改变了类。

    • 备忘录模式(Memento:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

    • 中介者模式(Mediator:用一个中介对象来封装一系列对象的交互,中介者使各对象不需要显式地互相引用,从而使其耦合松散。

    • 解释器模式(Interpreter:给定一个语言,定义它的文法,并定义一个解释器用来解释语言中的句子。

6、inline(内联)

​在 C/C++ 中,函数的调用过程需要将函数的参数、返回地址、局部变量等信息压入栈中。而这样的操作会使栈内存使用过高,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。

​内联函数的实际意义就是,假如某个函数的函数体较为简单,但是在之后的程序中又频繁调用该函数,综合考虑代码的重复性以及运行效率,就会使用内联函数。内联函数就是将调用该函数的代码语句替换为实际的函数体,这一过程发生在函数的编译阶段。这样做的好处是,同一代码不需要自己手动重复编写,编译器在识别到inline关键字的时候,会在编译过程中将对应的函数调用语句替换。

​如果内联函数发生改变就需要重新进行编译。

7、volatile

volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

volatile 指出 它所声明的变量 是随时可能发生变化的,每次使用它的时候必须从 该变量 的地址中读取,因而编译器生成的汇编代码会重新从 该变量 的地址读取数据。

volatile 用在如下的几个地方:

  • 中断服务程序中修改的供其它程序检测的变量需要加 volatile
  • 多任务环境下各任务间共享的标志应该加 volatile
  • 存储器映射的硬件寄存器通常也要加 volatile 说明,因为每次对它的读写都可能由不同意义。

8、内存对齐

内存对齐 是一种在内存中安排数据的方式,以使数据在 CPU 中更高效地访问。在进行内存对齐时,编译器会为数据在内存中的地址进行调整,以确保变量在其整数倍(如 4 字节的变量需要放在地址为 4 的倍数的位置)的地址上,这样做能够提高数据的访问速度,在一个周期内读取到变量。否则,可能会导致多次内存读取操作,降低效率。

9、linux 常用指令

  • 文件和目录操作

    • ls :列出当前目录下的文件和目录。常用参数:ls -l(长格式列出文件),ls -a(显示隐藏文件)。

    • cd :切换目录。例如,cd /home/user

    • pwd :显示当前所在路径。

    • mkdir :创建目录。例如,mkdir new_folder

    • rm :删除文件或目录。

      • 常用参数 :rm -r(递归删除目录),rm -f(强制删除)。
    • cp :复制文件或目录。

      • 例如,cp file1 file2(复制文件),cp -r dir1 dir2(复制目录)。
    • mv :移动或重命名文件/目录。

  • 文件查看与编辑

    • cat :查看文件内容。例如,cat file.txt

    • lessmore :分页查看文件内容,less 支持向上翻页。

    • head :查看文件开头内容,例如 head -n 10 file.txt

    • tail :查看文件末尾内容,tail -f file.txt 可实时监控文件变化。

    • nanovim :文本编辑器,nano 简单易用,vim 功能强大。

  • 权限管理

    • chmod :修改文件权限。例如,chmod 755 file.sh

    • chown :更改文件的所有者和所属组。例如,chown user:group file.txt

  • 系统管理

    • ps:查看当前运行的进程,ps aux 查看所有进程。

    • top:实时显示系统性能和进程信息。

    • kill:终止进程,例如 kill 1234(通过 PID 终止进程)。

    • df:查看磁盘空间使用情况,例如 df -h

    • du:查看目录或文件大小,例如 du -sh folder

    • free:查看内存使用情况。

  • 查找文件内容

    • find :查找文件,例如 find /home -name "*.txt"

    • grep :搜索文件内容,例如 grep "keyword" file.txt

10、端口

  • 一般端口是与 ip 地址绑定的,用户一般是先通过 ip 地址找到对应的服务器,然后再通过端口找到对应的服务。

  • 端口与协议:端口只识别符合对应协议的数据流,例如,服务器中识别 http 协议数据流的端口号默认为 80https 协议的默认端口号为443

11、静态与动态

“静态”“动态” 通常用于描述不同的特性或行为,主要包括内存分配、类型绑定和生命周期等方面。静态 指在程序运行期间 不易改变 的内容或状态;动态 指在程序运行期间 容易发生变化 的内容或状态。

静态

  • 静态内存分配 :在编译期确定内存大小和位置,整个程序运行过程中不改变。例如,全局变量、静态变量或栈上分配的局部变量都属于静态分配。它们的生命周期通常与程序一致或在函数退出时结束。

  • 静态类型检查 :在编译阶段进行类型检查。静态类型语言(如 C++、Java)在编译时就会检查变量的类型,确保类型安全。这带来了一定的安全性和效率。

  • 静态链接 :在编译期,所有依赖项(如库函数)都被链接到可执行文件中。在运行时不再需要额外的加载,因此程序启动速度较快。

动态

  • 动态内存分配 :在程序运行时才分配内存,大小和位置可以变化。动态内存通常由 new(在C++ 中)或 malloc(在 C 中)等分配函数管理,使用完后需要手动释放。动态分配允许程序更灵活地使用内存,但也增加了内存管理的复杂性。

  • 动态类型检查 :在运行时检查类型(例如 Python、JavaScript)。动态类型语言在运行时才确认类型的正确性,编程时灵活性更强,但缺乏编译期的类型安全性。

  • 动态链接 :在程序运行时加载库和模块。动态链接的程序在执行时加载必要的库,能够减少初始文件大小,适应不同环境的依赖要求。

12、cast(强制类型转换)

cast(类型转换)用于将一种数据类型转换为另一种数据类型。

  • static_cast :静态类型转换,一般用于基本类型间的转换,也可以用于将子类指针转为父类指针

    int a = 10;
    double b = static_cast<double>(a);
    
  • dynamic_cast :动态类型转换,用于在类层次结构中进行安全的向下转换(如基类指针转换为派生类指针),只能用于包含虚函数的多态类。

    Base *base = new Derived();
    Derived *derived = dynamic_cast<Derived*>(base); // 如果转换不安全,将返回 nullptr
    
  • const_cast :用于添加或移除 const 属性。

    const int a = 10;
    int &b = const_cast<int&>(a);
    
  • reinterpret_cast :用于强制将一种数据类型转换为另一种不相关的数据类型,如将一个整数转换为指针。这种转换不安全,需要谨慎使用。

    int a = 100;
    void *ptr = reinterpret_cast<void*>(&a);
    

13、虚函数

​虚函数是在基类中使用关键字 virtual 声明的成员函数。它允许通过基类指针或引用来调用派生类的重写版本。

特点

  1. 多态性 :虚函数使得程序能够在运行时决定调用哪个函数,这就是多态性。通过基类指针或引用调用虚函数时,将调用派生类的重写函数,而不是基类的版本。
  2. 重写 :派生类可以重写基类的虚函数,以提供特定的实现。重写的函数必须与基类的虚函数具有相同的签名(名称、参数类型和顺序)。
  3. 动态绑定 :虚函数通过动态绑定机制来实现多态。在运行时,根据对象的实际类型(而不是指针或引用的类型)来确定调用哪个函数。
  4. 虚表(vtable) :每个包含虚函数的类会有一个虚表,虚表是一个指针数组,存储了虚函数的地址。在对象创建时,会为对象分配一个指向虚表的指针,以实现动态绑定。

虚表

​虚表的作用在于 可以使基类指针指向派生类对象时,派生类对象可以正确调用其派生类的虚函数而非基类函数。 使用基类指针可以指向任一派生类对象,但是由于派生类中会重写基类中的虚函数,因此为了使正确调用对应的重写后的虚函数,就需要使用虚表。

​每个含有虚函数的类都会自动产生一个虚表,虚表可以看作是一个数组,其中存储了该类中所有虚函数的地址。而派生类也会生成一个虚表,但是该虚表存储的是该类中虚函数的地址,无论是否对基类中的虚函数进行了重写,都会生成一个虚表存储虚函数的地址,改写后是存放改写后的地址,可以理解为不管是否重写,原先基类中的虚函数到了派生类中依然是一个虚函数。

​每个派生类对象内部会有一个虚指针指向其虚表,当使用基类指针指向该派生类对象的时候,会根据该派生类对象的类型,通过虚指针找到虚表中的该函数的地址,从而正确调用该函数。

​只要一个类含有虚函数,就会生成一个虚表,而这个类在调用虚函数的时候,会严格按照虚表上函数的地址来执行,即要先使用 vptr 找到虚表,再从虚表中的对应虚函数地址来调用该虚函数。

基类与派生类

  • 所有的派生类对象都可以使用基类指针来指向,这样做的好处是,可以清晰的看出这些派生类继承了哪个基类,统一地指针更方便地进行管理,例如 dog,cat 继承了 animal 类,如果全部使用 animal 类指针,那么再有新的 animal 子类的时候,依然可以使用,但是如果使用对应类指针,就会造成代码的混乱。

  • 实现基类指针可以正确调用所属对象的函数就是通过 虚表 来实现的。

  • 派生类不会继承基类的构造与析构函数,而派生类会有自己的默认构造与析构该函数。

  • 派生类对象所拥有的内容并非全部为派生类,其内容是由派生类自己特有的内容+基类继承的内容。

  • 构造函数执行过程:父类构造先执行,子类后执行;析构函数执行过程:子类析构先执行,父类后执行。其中vptr指向虚表是构造的最后一步。

纯虚函数

​纯虚函数是 C++ 中一种特殊类型的虚函数,它在基类中被声明但没有提供具体的代码块实现。它的主要目的是为了定义一个接口,强制派生类实现该函数。

virtual ReturnType FunctionName(Parameters) = 0;

​任何继承了包含纯虚函数的基类的派生类,都必须重写这个纯虚函数,否则该派生类也将变为抽象类,无法实例化。包含纯虚函数的类称为抽象类,抽象类不能被实例化。这意味着你不能直接创建抽象类的对象,只能通过指向派生类的基类指针或引用来使用它。

静态(早)绑定与动态(晚)绑定

  • 静态绑定 是在编译阶段决定调用的函数的地址,这是由指针类型所决定的。

  • 动态绑定 是在运行阶段决定调用的函数的地址,这是由对象类型所决定的。

原理

问题的来源 :由于基类指针可以指向派生类对象,由于静态绑定是由指针类型来决定调用哪个函数,而派生类对象并非实际的基类对象,为了实现多态,就需要动态绑定,即依据实际对象类型来确定调用哪个函数。

问题解决 :将每个类的所有函数的地址存放在一个表中,使用一个指针指向这个表,那么在调用的时候就使用这个表中的函数地址。因此诞生虚函数。一个基类中若含有虚函数,那么这个类会在编译阶段生成一个 虚表,同理派生类也会生成一个虚表,虚表中包含对应类中所有的虚函数地址,每当生成一个对象的时候,对象会自动生成一个指向虚表的指针用以调用虚表中的内容,派生类与基类拥有各自的虚表。

过程 :编译器识别到某个类中含有虚函数,那么在识别到所有由该基类或者其派生类所生成的对象时,就不会再编译阶段进行绑定。而是当运行的时候,依据派生类对象的实际类型来绑定,使用派生类对象中的 vptr 所指向的虚表来调用派生类中的函数。

优点 :可以实现多态,且可以将不同的派生类使用基类指针进行管理,每当创建了新的派生类,均可以使用基类指针来来管理新的派生类对象。而基类指针类型相同就方便存放,例如可以将其存放在一个 vector 中,同时可以正确调用派生类的函数。vtable 对于类是唯一的,但 vptr 是对象特有的,每一个派生类只有一个虚表,而由该类创建的每个对象拥有各自的 vptr,但是这些 vptr 指向同一个虚表。

posted @ 2025-12-31 14:03  Amireux77  阅读(2)  评论(0)    收藏  举报