C++ 多态学习笔记

C++ 的多态性是面向对象程序设计的三大特性之一(封装、继承、多态),它允许将子类对象赋值给父类对象,从而实现基类指针指向子类对象,实现基类指针调用子类对象的成员函数。 C++ 的多态性主要有两种实现方式:静态多态和动态多态。 静态多态:通过函数重载和模板实现。 动态多态:通过虚函数实现。 静态多态函数重载函数重载是指在同一个作用域内,可以定义 多个名称相同 但 参数列表不同 的函数。注意,不能用 返回值类型 来区分重载函数。 int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; } double add(double a, double b, double c) { return a + b + c; } 笔记 编译过程 ...

2024年06月27日 · 2 分钟 · Cassius0924

HTTP 协议版本变化学习笔记

互联网发展至今,HTTP 协议已经发展了多个版本,分别为 HTTP/1.0、HTTP/1.1、HTTP/2.0、HTTP/3.0,本文将对这几个版本的变化进行学习笔记。 HTTP/1.0HTTP/1.0 是最早的 HTTP 协议版本,它的特点是: 每次请求都会建立一个新的 TCP 连接,请求结束后立即断开连接。 每个请求都会包含完整的请求头和请求体。 不支持持久连接,每次请求都需要重新建立连接。 不支持管道化,即同一个连接中不能同时发送多个请求。 HTTP/1.1HTTP/1.1 是对 HTTP/1.0 的改进,它的特点是: 支持持久连接(Keep-Alive),即同一个连接可以发送多个请求。 支持管道化,即同一个连接中可以同时发送多个请求。但是,由于 HTTP/1.1 中的管道化存在队头阻塞问题,所以很少被使用。默认为关闭状态,并且大多数浏览器也不支持。 所以我们认为 HTTP/1.1 不支持管道化 。 HTTP/1.1 的缺点: 队头阻塞问题:如果一个请求响应时间过长,那么后面的请求就会被阻塞。 明文传输:HTTP/1.1 的数据传输是纯文本且未加密的,容易被窃听。比如状态码 200 会被分为 2、0、0 三个字节传输。这点会在 HTTP/2.0 中得到改进。 头部冗余:每次请求都需要携带完整的请求头,头部信息冗余。 HTTPS在讲述 HTTP/2.0 之前,我们先来了解一下 HTTPS。 HTTPS 是在 HTTP 的基础上加入了 SSL/TLS 加密层,使得数据传输更加安全。HTTPS 的特点是: 数据加密:HTTPS 使用 SSL/TLS 加密传输数据,保证数据传输的安全性。 身份认证:HTTPS 使用证书机制对服务器和客户端进行身份认证,防止中间人攻击。 数据完整性:HTTPS 使用数字签名对数据进行完整性校验,防止数据被篡改。 加密方式:对称加密、非对称加密、数字签名。 通信前使用非对称加密协商对称加密的密钥,通信过程使用对称加密传输数据,保证数据传输的安全性。 HTTP/2.0HTTP/2.0 是对 HTTP/1.1 的重大升级,它的特点是: 头部压缩:HTTP/2.0 使用 HPACK 算法对头部进行压缩,如果请求头中包含相同的字段,只需要发送一次,后续请求只需要发送索引,接着从静态表或动态表中获取对应的值。解决了 HTTP/1.1 中头部冗余的问题。 ...

2024年06月27日 · 1 分钟 · Cassius0924

TCP 的常见拥塞控制算法学习笔记

TCP 的拥塞控制算法有几种: Tahoe Reno NewReno SACK BIC CUBIC BBR 笔记 MSS: Maximum Segment Size,最大分段大小。 ...

2024年06月27日 · 1 分钟 · Cassius0924

C++ 智能指针学习笔记

智能指针简介智能指针是一种 RAII(Resource Acquisition Is Initialization)技术,用于管理动态分配的内存。智能指针的优点是可以自动释放内存,避免内存泄漏。 C++11 标准引入了三种智能指针:std::unique_ptr、std::shared_ptr 和 std::weak_ptr。 它们都定义在头文件 <memory> 中。 unique_ptrunique_ptr 是一种独占所有权的智能指针,它保证同一时间只有一个指针可以指向对象。 unique_ptr 的创建#include <iostream> #include <memory> int main() { // 使用 new 创建 unique_ptr std::unique_ptr<int> up1(new int(10)); std::cout << *up1 << std::endl; // 使用裸指针创建 unique_ptr int count = 20; std::unique_ptr<int> up2(&count); std::cout << *up2 << std::endl; // 使用 make_unique 创建 unique_ptr auto up2 = std::make_unique<int>(20); std::cout << *up2 << std::endl; return 0; } unique_ptr 的拷贝和赋值unique_ptr 不能拷贝,但可以移动。 #include <iostream> int main() { std::unique_ptr<int> up1(new int(10)); std::unique_ptr<int> up2 = std::move(up1); std::cout << *up2 << std::endl; return 0; } unique_ptr 的释放unique_ptr 会在离开作用域时自动释放内存。 #include <iostream> int main() { std::unique_ptr<int> up1(new int(10)); std::cout << *up1 << std::endl; return 0; } unique_ptr 的自定义删除器unique_ptr 支持自定义删除器,可以用于释放动态分配的内存。 ...

2024年06月26日 · 3 分钟 · Cassius0924

Redis 为什么采用单线程,为什么性能好

Redis 采用单线程的原因是因为在 内存中 进行读写操作,CPU不是Redis的性能瓶颈,而是内存和带宽,所以采用单线程可以避免 线程切换和锁的开销。 Redis 是什么?Redis 是一个开源的内存数据库,它可以存储键值对,支持多种数据结构,如字符串(string)、哈希(hash)、列表(list)、集合(set)、有序集合(zset)等。 Redis 是不是单线程?实际上,Redis 是多线程的,其内部有以下几个线程: redis-server:主线程,负责接收客户端的连接,读取请求,发送响应。 bio-close-file:负责异步关闭大文件。 bio-aof-fsync:负责将 AOF 文件异步刷到磁盘。 bio-lasy-free:负责异步释放大内存。 jemalloc-bg-thread:负责内存碎片整理。 io-thread:IO 线程,负责 read/write,decode/encode。 笔记 AOF(Append Only File) 是 Redis 的一种持久化方式,将所有写操作追加到文件末尾,重启时重新执行 AOF 文件中的命令即可恢复数据。实时硬盘操作,不会丢失数据,但是会影响性能。 ...

2024年06月26日 · 1 分钟 · Cassius0924

堆排序学习笔记

在学习堆排序之前,我们先来了解一下堆这种数据结构。 堆的概念堆是一种特殊的树形数据结构,它满足以下性质: 堆必须是一个 完全二叉树 。 堆序性:堆中任意节点的值总是不大于或不小于其子节点的值。 根据堆序性,我们可以将堆分为两种类型: 大顶堆:每个节点的值都大于或等于其子节点的值 小顶堆:每个节点的值都小于或等于其子节点的值 所以,如果一个完全二叉树的一个节点即大于其父节点,又大于其子节点,那么这个树就不是一个堆。小于同理。 笔记 完全二叉树的性质 ...

2024年06月26日 · 2 分钟 · Cassius0924

C++ 类的运算符为什么要使用引用返回

代码class AClass { private: int _count; public: AClass() : _count(0) { std::cout << "Default constructor called\n"; } // 赋值运算符,返回引用 AClass &operator=(int cnt) { _count = cnt;k return *this; } // 后置自增运算符,返回引用 int &operator++(int) { ++_count; return *this; } }; 如果不使用引用返回,其实也是可以运行的,只不过会在返回时调用拷贝构造函数,生成临时对象,然后再调用析构函数释放临时对象,这样会多出一次拷贝构造和析构的开销。而使用引用返回,可以直接返回对象的引用,避免了这个开销。 需要注意的是,如果我们返回值类型,我们是不能直接修改返回值的: class AClass { private: int _count; public: // 省略构造函数 // 赋值运算符,返回引用 AClass operator=(int cnt) { _count = cnt; return *this; } // 后置自增运算符,返回引用 int operator++(int) { ++_count; return *this; } void print() const { std::cout << "AClass: count = " << _count << '\n'; } }; int main() { AClass a; a.print(); // 输出 AClass: count = 0 (a++) = 10; a.print(); // 输出 AClass: count = 1 (a++)++; a.print(); // 输出 AClass: count = 2 } 可以看到,如果我们返回值类型,我们是不能直接修改返回值的。虽然 a++ 已经修改了 a 的值,但是 a++ 返回的是一个修改后的 a 对象的拷贝,所以 (a++) = 10; 或者 (a++)++; 修改的是这个拷贝对象,而不是原对象 a。 ...

2024年06月25日 · 1 分钟 · Cassius0924

C++ 类的流插入和流提取运算符为什么要声明为友元函数

友元函数版代码class AClass { private: int _count; std::string _str; std::vector<int> _vec; public: friend std::ostream &operator<<(std::ostream &os, const AClass &a) { os << "AClass: count = " << a._count << ", str = " << a._str << ", vec size = " << a._vec.size(); return os; } }; 为什么要声明为友元函数先理解一下友元函数,它实际上是一个普通函数,不属于类成员,但它又是一个特殊的普通函数,因为它可以访问类的私有成员。因此 operator<< 和 operator>> 声明为友元函数的目的很明显,就是为了能够访问类的私有成员。 实际上,如果它们不声明为友元函数,也是可以实现的,例如下面代码: class AClass { private: int _count; std::string _str; std::vector<int> _vec; public: std::ostream &operator<<(std::ostream &os) { os << "AClass: count = " << _count << ", str = " << _str << ", vec size = " << _vec.size(); return os; } }; 但是这样就需要特殊的方法来调用这个 operator<< 函数,因为它不再是一个普通函数,而是一个类成员函数: int main() { AClass a; a << std::cout; // 错误,不能这样调用 a.operator<<(std::cout); // 正确 return 0; } 这样显然不够直观,不是一个正常人类写的代码:) 所以,为了代码的可读性和可维护性,我们将 operator<< 和 operator>> 声明为友元函数,这样就可以直接使用 << 和 >> 运算符来操作类的对象了。 ...

2024年06月25日 · 1 分钟 · Cassius0924

Socket 编程之 IO 多路复用学习笔记

什么是 IO 多路复用?阻塞 IO 与 非阻塞 IO我们先了解一下阻塞 IO,阻塞 IO 是指应用程序在读写数据时,如果没有数据可读或者写,应用程序会一直 阻塞在那里 ,直到有数据可读或者写。 与它相反的是非阻塞 IO,是指应用程序在读写数据时,无论是否有数据可读写,都 立即返回 ,若没有数据可读写将会返回一个错误码,通过不断轮询来检查是否有数据可读或者写。 IO 多路复用由于 Scoket 默认是阻塞 IO,所以很多初学者在处理多个连接时,会为每个连接创建一个线程来处理,但这样做会引起 CPU 的上下文切换,降低系统的性能。 有一种更“优雅”的方式,那就是 IO 多路复用,也称为事件驱动模型(Event-driven IO)。IO 多路复用是指内核一旦发现进程指定的一个或者多个 IO 事件已经就绪,就通知该进程。IO 多路复用模型中,只需要一个线程就可以同时处理多个连接。 通俗易懂的说,IO 多路复用就是将多个 IO 事件交给内核,内核帮我们监听这些 IO 事件,当有 IO 事件就绪时,内核会通知我们,我们只需要处理就绪的 IO 事件即可。 IO 多路复用的优点 一个线程可以同时处理多个连接,减少线程的创建和销毁 降低了系统开销,提高了系统的并发性能 IO 多路复用的实现方式 select poll epoll (Linux) kqueue (FreeBSD) IOCP(Windows) 其中 epoll 是 Linux 下的 IO 多路复用机制,kqueue 是 FreeBSD(macOS 就属于 FreeBSD)下的 IO 多路复用机制。 下面的伪代码是 IO 多路复用的最基本实现方式: ...

2024年06月24日 · 7 分钟 · Cassius0924

搭建自动化部署的 GitHub.io 个人静态博客指南

本文章主要介绍如何使用 GitHub Actions 实现基于 MkDocs 的 GitHub.io 个人静态博客的自动化部署。 本指南主要分为以下几个部分: 创建 GitHub 仓库 创建 MkDocs 项目 配置 GitHub Actions 部署到 GitHub Pages 创建 GitHub 仓库由于我们的博客是托管在 GitHub io 上的,所以我们需要一个 GitHub 仓库来存放我们的 MkDocs 博客。 创建仓库首先,我们需要在 GitHub 上创建一个新的仓库,仓库名可以是 <username>.github.io,其中 <username> 是你的 GitHub 用户名。 克隆仓库然后,我们需要将这个仓库克隆到本地: git clone <repository-url> cd <repository-name> 创建 MkDocs 项目安装 MkDocsMkDocs 是一个 Python 项目使用 pip 安装: pip install mkdocs 我们需要创建一个 MkDocs 项目,可以使用 MkDocs 官方提供的模板来创建。直接在仓库根目录执行以下命令即可: mkdocs new . 然后,我们需要安装 MkDocs Material 主题。当然我们也可以选择其他主题,可以在 MkDocs 的 WiKi 查看更多主题。 ...

2024年06月17日 · 3 分钟 · Cassius0924