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

【C++心愿便利店】No.12---C++之探索string底层实现

文章目录

  • 前言
  • 一、写实拷贝(了解)
  • 二、string类常用接口实现
    • 2.1 成员变量
    • 2.2 默认构造函数
    • 2.3 拷贝构造函数
    • 2.4 operator==
    • 2.5 operator[]
    • 2.6 c_str
    • 2.7 size()
    • 2.8 capacity()
  • 三、迭代器的实现
    • 3.1 begin()和end()
    • 3.2 范围for
  • 四、string类增删查改
    • 4.1 reserve():增容函数
    • 4.2 push_back():尾插字符
    • 4.3 append():追加字符串
    • 4.4 operator+=
    • 4.5 insert
    • 4.6 erase
    • 4.7 resize
    • 4.8 find
    • 4.9 substr
  • 五、string类运算符重载
    • 5.1 operator< == <= > >= !=
    • 5.2 operator<<
    • 5.3 operator>>


前言

在这里插入图片描述

👧个人主页:@小沈YO.
😚小编介绍:欢迎来到我的乱七八糟小星球🌝
📋专栏:C++ 心愿便利店
🔑本章内容:探索string底层实现
记得 评论📝 +点赞👍 +收藏😽 +关注💞哦~


提示:以下是本篇文章正文内容,下面案例可供参考

一、写实拷贝(了解)

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。
引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

二、string类常用接口实现

2.1 成员变量

class string
{
public:private:char* _str;size_t _size;size_t _capacity;const static size_t npos;——————————————————————————const static size_t npos=-1;//静态的成员变量是不可以给缺省值,必须在类外面进行初始化但是const静态的整形可以(特例)
};
const size_t string::npos = -1;//支持在类外面初始化

在这里插入图片描述

2.2 默认构造函数

string():_str (new char[1]{'\0'}),_size(strlen(0)),_capacity(0)
{}
//常量字符串规定后面默认就有\0
//strlen 计算的是字符串中有效字符的个数,不算 '\0',而常量字符串的结尾默认有一个 '\0',用 new开辟空间的时候需要多开一个用来存储结尾的 \0
string(const char* str=""):_size(strlen(str))//0,_capacity(_size)//0
{_str = new char[_capacity + 1];//1strcpy(_str, str);
}

对于上述代码中形参上必须加 const 修饰,这样才能用 C 语言中的常量字符串来初始化 string 类对象,上面两种初始化的方式都可以一个是无参的一个是有缺省值的,形参的的缺省值直接给一个空字符串即可如上述,要注意初始化列表是按照声明的顺序来初始化的。_capacity表示的是可以存储有效字符的容量,而字符串结尾默认的 ‘\0’ 并不算作有效字符,因此最初的 _capacity 就是形参 str 的长度。
🌟对于为什么不可以用 ‘\0’ “\0” 和nullptr当缺省值?
答案:

  • 首先对于’\0’:str是一个char*类型,而’\0’是一个char类型的 类型不匹配
  • 其次对于给缺省值nullptr:strlen是不会去检查空的,它是一直找到 \0为止的,也就相当于直接对这个字符串进行解引用了,这里的字符串又是空,所以会引发空指针问题。
  • 最后对于"\0":它表示该字符串有一个字符 ‘\0’ ,它的结尾还有一个默认的 ‘\0’,因此有两个 ‘\0’

2.3 拷贝构造函数

//传统写法
string(const string& s)//拷贝构造
{_str = new char[s._size+1];strcpy(_str,s._str);_size = s._size;_capacity = s._capacity;
}

上述我们称为传统写法下面这种我们称为现代写法,对于现代写法不需要亲自去申请空间初始化,而是调用构造函数去完成。最后再将初始化好的 tmp 交换过来
还要注意:一定要通过初始化列表对 *this 进行初始化,不然交换给 tmp 后,里面都是随机值(不同编译器有的会处理有的不会),最终出了作用域 tmp 去销毁的时候就会出问题。

//现代写法:下面代码是两种不同的现代写法
不过要注意如果 string 对象中有 '\0',只会把 '\0' 前面的字符拷贝过去
void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}
string(const string& s):_str(nullptr)//拷贝构造,_size(0),_capacity(0)//不处理tmp里面是随机值析构就会发合适呢个错误
{string tmp(s._str);//构造swap(tmp);
}
___________________________________________________________________________________________
void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}
string(const string& s)//拷贝构造
{string tmp(s._str);swap(_str, tmp._str);swap(_size, tmp._size);swap(_capacity, tmp._capacity);
}

2.4 operator==

//传统写法
string& operator=(const string& s)
{if (this != &s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;
}

对比于上述代码写法,下述这种写法通过调用拷贝构造来申请空间,在利用局部对象出了作用就会被销毁的特点,将需要释放的资源通过 swap 交换给这个局部变量,让这个局部变量帮我们销毁。

//现代写法:这里不能直接用 swap 交换两个 string 类对象,会导致栈溢出
void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}
string& operator=(const string& s)
{if (this != &s){//string tmp(s);//调用拷贝构造string tmp(s._str);//调用构造swap(tmp);//这里s2换给了tmp本来s2要析构现在tmp出了作用域调用析构也就意味着原始的s2的空间释放了}return *this;
}

下述这种写法不用我们去调用构造或者拷贝构造,直接通过形参去调用,传值传参会调用拷贝构造,tmp是它的实参调用拷贝构造构造的一个一摸一样的空间。

//现代写法的优化版本
void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}
string& operator=(string tmp)
{swap(tmp);return *this;
}

2.5 operator[]

这两个运算符重载函数构成函数重载,对象在调用的时候会找最匹配的,非const对象会调用非const版本,const 对象会调用const版本。

//可读可写版本
char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}
//只可以读版本
const char& operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}

2.6 c_str

返回的是一个const char*的数组指针,只读不写,这个数组包含的字符序列与string对象的值相同,另外还包含一个以空字符(‘\0’)结尾的字符串,加上 const,这样普通的 string 类对象可以调用,const 类型的 string 类对象也可以调用,普通对象来调用就是权限的缩小

const char* c_str()
{return _str;
}

2.7 size()

size_t size()const
{return _size;
}

2.8 capacity()

size_t capacity()const
{return _capacity;
}

三、迭代器的实现

iterator 是 string 类的内嵌类型,也可以说是在 string 类里面定义的类型,在一个类里面定义类型有两种方法,typedef 和 内部类。

3.1 begin()和end()

//非const调用
typedef char* iterator;//string 类的 iterator 是通过typedef来实现的
iterator begin()
{return _str;
}
iterator end()
{return _str+_size;
}
——————————————————————————————————————————————————————————
//const调用
typedef const char* const_iterator;
const_iterator begin()const
{return _str;
}
const_iterator end()const
{return _str + _size;
}

3.2 范围for

支持范围for写法,范围for的底层是迭代器实现的,但是范围for不是万能的,范围for遇上const类型的对象,会报错,因此要提供const迭代器:typedef const char* const_iterator;

string s1("hello world");
for (auto ch : s1)
{ch++;cout << ch << " ";
}
cout << endl;

四、string类增删查改

4.1 reserve():增容函数

reserve 函数不会进行缩容,因此在扩容前要先进程判断,只有当形参 n 大于当前容量的时候才扩容。

void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];//开n+1个是因为n个有效字符,另一个是'\0'strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}

4.2 push_back():尾插字符

对于尾插首先检查是否需要增容,如果需要就调用我们上面实现的 reserve 函数来进行扩容(选择2倍扩容),
扩容后将ch加到str上,然后 _size++ 最后手动添加一个新的 \0 。

void push_back(char ch)
{if (_size == _capacity){//reserve(_capacity * 2);//对于这里可以采用三目来判断_capacity是否为0,若不进行判断空串的_capacity是0,进行扩容0*0=0就会发生越界访问reserve(_capacity==0?4:_capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';
}

4.3 append():追加字符串

void append(const char * str)
{size_t len = strlen(str);if (_size +len> _capacity){reserve(_size+len);//这里就不需要担心_capacity为0的情况}strcpy(_str + _size, str);//strcpy会把'\0'也拷贝过去size += len;
}

4.4 operator+=

+=要有返回值返回*this

//追加一个字符复用push_back()
string& operator+=(char ch)
{push_back(ch);return *this;
}//追加一个字符串复用append()
string& operator+=(const char* str)
{append(str);return *this;
}

4.5 insert

在pos位置插入字符

void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size;while (end >= pos){_str[end] = _str[end-1];--end;}_str[pos] = ch;_size++;
}

注意:上述代码挪动数据时的判断条件中,end 和 pos 都是 sizt_t 类型,例如当 pos = 0 的时候 end >= pos,end–,一直减到end=-1但是end是一个无符号整形,所以循环条件一直成立还可以进入循环, 所以下面有两种修改方式:

为了防止整型提升有以下两种写法:
void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size+1;while (end > pos){_str[end] = _str[end-1];--end;}_str[pos] = ch;_size++;
}
——————————————————————————————————————————————————————————————————————————————————
void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}int end = _size;换成有符号也会报错因为操作符的两边两个数据的类型不同时会发生类型提升,end变成有符号类型也会被提升到无符号类型因为pos是无符号类型,所以可以强转pos->(int)poswhile (end >=(int) pos){_str[end] = _str[end-1];--end;}_str[pos] = ch;_size++;
}

在pos位置插入字符串

void insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size+len;while (end >= pos+len){_str[end] = _str[end-len];--end;}strncpy(_str + pos, str,len);_size += len;}
}

4.6 erase

void erase(size_t pos, size_t len=npos)
{assert(pos < _size);if (len == npos||pos+len>=_size){_str[pos] = '\0';_size = pos;}else{size_t begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];begin++;}_size -= len;}
}

4.7 resize

size_t find(char ch,size_t pos=0)//这里给了一个半缺省
{for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}

4.8 find

size_t find(const char* str, size_t pos = 0)//半缺省
{const char* p = strstr(_str+pos, str);//strstr找到返回所在位置指针否则返回空if (p){return p - _str;//返回下标}else{return npos;}
}

4.9 substr

这里就表明我们一定要手写一个拷贝构造,编译器默认生成的拷贝构造是一个浅拷贝,会发生调用两次析构的问题所以要手写一个深拷贝

string substr(size_t pos, size_t len = npos)
{string s;size_t end = pos + len;if (len == npos || pos + len >= _size){len = _size - pos;end = _size;}s.reserve(len);for (size_t i = pos; i < pos + len; i++){s += _str[i];}return s;//s是一个浅拷贝出了作用域s销毁
}

五、string类运算符重载

5.1 operator< == <= > >= !=

bool operator<(const string& s)const
{return strcmp(_str, s._str) < 0;
}bool operator==(const string& s)const
{return strcmp(_str, s._str) == 0;
}bool operator<=(const string& s)const
{return *this < s || *this == s;
}bool operator>(const string& s)const
{return !(*this <= s );
}bool operator>=(const string& s)const
{return !(*this < s);
}bool operator!=(const string& s)const
{return !(*this == s);
}

5.2 operator<<

因为类函数有this指针传参数容易发生错误匹配原因,>>和<<运算符重载要写在类外面
无论是形参还是返回值,只要涉及到 ostream 或 istream 都必须要用引用

//有以下两种写法:
ostream& operator<<(ostream& out,const string& s)
{for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;
}
_______________________________________________________________________
范围for(这里使用范围for要调用const迭代器)
ostream& operator<<(ostream& out,const string& s)
{for (auto ch : s)//s是一个const对象要用const迭代器out << ch;return out;
}

5.3 operator>>

空格符 ’ ’ 和换行符 \n不能直接用 istream 对象来读取的,in >> ch 是读不到空格符和换行符。需要借助 get() 成员函数才能读取到空格符和换行符。

istream& operator>>(istream& in, string& s)
{s.clear();//清掉原始数据不然就变成尾插了char ch;//in >> ch;//拿不到空格或者换行例如sacnf拿不到空格所以出现了getcharch = in.get();while (ch!=' '&&ch!='\n'){s += ch;ch = in.get();}return in;
}

对于上面这种写法,在输入的字符串很长的情况下会多次调用 reserve 进行扩容,所以可以采用下述优化版本来实现:先开辟一个数组,将输入的字符存储到数组中,然后从数组中拷贝到string对象中,数组出了作用域就会销毁

istream& operator>>(istream& in, string& s)
{s.clear();//清掉数据不然就变成尾插了char buff[128];size_t i = 0;char ch;ch = in.get();while (ch!=' '&&ch!='\n'){buff[i++] = ch;if (i == 128){buff[i] = '\0';s += buff;i = 0;}}if (i != 0){buff[i] = '\0';s += buff;}return in;
}

相关文章:

【C++心愿便利店】No.12---C++之探索string底层实现

文章目录 前言一、写实拷贝&#xff08;了解&#xff09;二、string类常用接口实现2.1 成员变量2.2 默认构造函数2.3 拷贝构造函数2.4 operator2.5 operator[]2.6 c_str2.7 size()2.8 capacity() 三、迭代器的实现3.1 begin()和end()3.2 范围for 四、string类增删查改4.1 reser…...

Android Studio(列表视图ListView)

前言 前面在适配器章节&#xff0c;已经介绍了ListView的作用(干什么的)&#xff0c;这节将主要介绍如何去设计ListView页面视图。 思考 列表视图需要些什么&#xff1f; 1. 列表项容器&#xff08;装载各列表项的容器&#xff09;&#xff1a;<ListView/> 2. 列表项布局…...

让深度神经网络绘画以了解它们是如何工作的

一、说明 深度学习如此有效&#xff0c;这真是一个谜。尽管有一些关于深度神经网络为何如此有效的线索&#xff0c;但事实是没有人完全确定&#xff0c;并且深度学习的理论理解是一个非常活跃的研究领域。 在本教程中&#xff0c;我们将以一种不寻常的方式触及问题的一个小方面…...

https://www.jianshu.com/p/34bf240b85a9

https://www.jianshu.com/p/34bf240b85a9 https://www.eccee.com/soft-platform/991.html...

如何导出PPT画的图为高清图片?插入到world后不压缩图像的设置方法?

期刊投稿的时候&#xff0c;需要图片保持一定的清晰度数&#xff0c;那么我们怎么才能从PPT中导出符合要求的图片呢&#xff1f; 对于矢量图绘图软件所画的图&#xff0c;直接导出即可。 而PPT导出的图片清晰度在60pi&#xff0c;就很模糊。 整体思路&#xff1a; PPT绘图——…...

【Spring】Spring IOC DI

Spring IOC & DI IOC DI入门什么是Spring什么是容器什么是IOC IOC介绍传统程序开发解决方案 DI IOC详解Bean的存储Controller(控制器存储)Service(服务存储)Repository(仓库存储)Component(组件存储)Configuration(配置存储) 为什么需要这么多类注解类注解之间的关系方法注…...

一招解密网络流量瓶颈!

前言 我们曾介绍过观测云提供全面的基础设施监测方案&#xff08;参见《全方位监控基础设施&#xff0c;坚实守护您的业务稳定&#xff01;》&#xff09;&#xff0c;能够高效全面地帮助您实时观测所有的基础设施对象及云产品等&#xff0c;赋能您的业务稳定发展。今天我们将…...

某校帮签到小程序m 加密参数解析

小程序解密清参考我以前的文章 VX小程序逆向 js版本 function n(e, a) {var t (65535 & e) (65535 & a);return (e >> 16) (a >> 16) (t >> 16) << 16 | 65535 & t };function i(e, a, t, n, r, i, s) {return o(a & n | t &…...

Node.js |(六)express框架 | 尚硅谷2023版Node.js零基础视频教程

学习视频&#xff1a;尚硅谷2023版Node.js零基础视频教程&#xff0c;nodejs新手到高手 文章目录 &#x1f4da;express使用&#x1f407;初体验&#x1f407;express路由⭐️路由的使用⭐️获取请求参数⭐️获取路由参数&#x1f525;练习&#xff1a;根据路由参数响应歌手信息…...

包教包会:Mysql主从复制搭建

笑小枫的专属目录 一、无聊的理论知识1. 主从复制原理2. 主从复制的工作过程3. MySQL四种同步方式 二、docker下安装、启动mysql1. 安装主库2. 安装从库 三、配置Master(主)四、配置Slave(从)五、链接Master(主)和Slave(从)六、主从复制排错1. 错误&#xff1a;error connectin…...

Subset Selection

白话解释&#xff1a;https://www.geeksforgeeks.org/feature-subset-selection-process/ 貌似有一种比较常见的方法&#xff0c;称为多元逐步回归有3种筛选自变量的方法 &#xff08;1&#xff09;向前法&#xff1a;n个因变量情况&#xff0c;慢慢增加因变量到方程中&#x…...

【测开求职】面试题:计算机网络 精简版整理

本篇文章整理的是在秋招过程中遇到的计算机网络高频面试题,应付部分中小厂的测试开发工程师面试完全没有问题,如果时间充足的话,建议再看一下笔者的另外一篇文章:【测开求职】面试题:计算机网络 详细版整理,会让你对整个计算机网络有足够全面深刻的理解,亲测应付各个大厂…...

设计模式-代理模式(delegate)

什么是代理&#xff1f; 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方…...

MongoDB 安装与配置

MongoDB 安装与配置 MongoDB 是一个高性能、开源的 NoSQL 数据库&#xff0c;它提供了丰富的查询功能和高可用性。本文将详细讲解 MongoDB 的安装与配置过程。 1. MongoDB 安装 1.1 Windows 平台安装 下载 MongoDB 安装包 访问 MongoDB 官方下载页面&#xff08;https://w…...

rabbitMq创建交换机,以及路由键绑定队列教程

创建交换机&#xff1a; 创建队列&#xff1a; 创建路由&#xff0c;绑定到交换机&#xff1a;...

odoo16前端框架源码阅读——ormService.js

odoo16前端框架源码阅读——ormService.js 路径&#xff1a;addons\web\static\src\core\orm_service.js 简单翻译一下代码中的注释&#xff1a; ORM服务是js代码和python的ORM层通信的标准方法。 然后讲了One2many and Many2many特使的指令格式&#xff0c;每个指令都是3元…...

详谈滑动窗口算法与KMP算法区别以及二者在什么场景下使用

什么是滑动窗口算法 滑动窗口算法是一种用于解决数组&#xff08;或字符串&#xff09;中子数组&#xff08;或子字符串&#xff09;问题的算法。该算法通过维护一个固定大小的窗口&#xff08;通常是两个指针&#xff09;&#xff0c;该窗口在数组上滑动&#xff0c;以寻找符…...

k8s、数据存储

数据存储的概念 容器磁盘上的文件的生命周期是短暂的&#xff0c;这就使得在容器中运行重要应用时会出现一些问题。首先&#xff0c;当容器崩溃时&#xff0c;kubelet 会重启它&#xff0c;但是容器中的文件将丢失——容器以干净的状态&#xff08;镜像最初的状态&#xff09;…...

Vue生命周期全解析:从工厂岗位到任务执行,一览无遗!

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、生…...

常见产品结构四大类型 优劣势比较

一般&#xff0c;我们通过产品架构来构建用户体验&#xff0c;这样可以提供更清晰的导航和组织、优化用户流程和交互、增强产品的可扩展性和可维护性&#xff0c;提升用户的满意度和忠诚度。如果没有明确的产品结构&#xff0c;可能会导致功能冗余或功能缺失、交互流程混乱等问…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...