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

Learning C++ No.11【string类实现】

引言:

北京时间:2023/2/19/8:48,昨天更新了有关进程状态的博客,然后在休息的时候,打开了腾讯视屏,然后看到了了一个电视剧,导致上头,从晚上6点看到了10点,把我宝贵的博客时间给搞没了,伤心,现在就让我们开始将功补过,把昨天就应该开始写的博客给补一补!加油,不怂,就是干!今天博客的内容非常简单,就是使用我们以前学过的知识,无论是类和对象、内存管理,还是string类中的函数使用,运用它们写一个较为完整的string类出来,也就是自己实现string类,写库里面的东西,想想还是挺激动的,就让我们带着这份激动,go go go!
在这里插入图片描述

string类的模拟实现

string类基本框架

首先开始学习之前,明白string类的底层就是一个字符数组而已,本质上我们在实现string类的过程中,也就是在补全之前学习中的一些不足,和一些重难点,当然最关键的可以复习之前的各种知识点,并且在复习的过程中更好的理解这些知识点的使用,把一小块一小块的知识给合并成一大块的知识,所以模拟实现string类是非常重要的。

步入正题,实现string类基本框架,如下代码:从代码入手,再做细节方面处理
注意图中小写s应该替换成大写
以上就是一个string类的基本框架,大部分函数功能还没有实现,只是实现了基本的构造函数和析构函数以及一些细节方面的处理。

string类框架之后的地基

搞定了上述有关string类中的构造函数和析构函数,当然最重要的是类和对象的使用,我们可以说是有了一个string类的基本框架,也可以说是我们拥有了一个基本的类框架,所以现在就让我们走进string类,复习并使用更多的知识来实现这个STL中的经典字符类。

拷贝构造

在搞定了构造函数和析构函数,之后实现一个类最重要的莫过于是拷贝构造函数了,虽然拷贝构造函数和构造函数、析构函数一样,都是一个默认成员函数,但是懂的都懂,默认成员函数并不是万能的,准确的来说是编译器并不是万能的,这些默认成员函数大部分都只是对内置类型起作用,而我们自己实现的自定义类型就像是后娘养的,人家是爱答不理,并不能很好的把自定义类型进行相应的初始化,所以对这些后娘养的自定义类型,则需要我们自己来实现初始化,当然经典的初始化场景有在初始化列表初始化、给缺省值(但本质还是在初始化列表初始化)、调用拷贝构造函数初始化等,所以调用拷贝构造函数初始化是自定义类型初始化的一个好地方,我们现在就来复习巩固一下拷贝构造函数。(当然重点就是想要讲深浅拷贝问题)
如下图:
在这里插入图片描述
当然上述的前提是通过成员变量中有一个char* _str的指针,涉及指针就涉及指针指向的空间,就涉及深拷贝问题,涉及深拷贝就涉及析构问题,这些看似无关,却紧密相连,这就是自我实现string类的好处,搞清各个知识点之间的关系和熟练掌握运用,所以以后写拷贝构造函数第一点就是考虑深浅拷贝问题。

const成员函数使用场景和运算符重载

在这里插入图片描述从上图中我们可以发现,我们许多函数在实现的时候,都可以去调用那些已经自己实现好了的函数,或者库函数来实现一些新的功能,并且可以发现,只要使用const来修饰成员函数,只要我们对该函数不做改变数据的操作,这种方法是很好的,可以有效的避免权利放大问题,解决不必要的麻烦,可以让我们使用const对象调用函数的时候,变得更加的放心,程序变得更加的稳定。 并且注意: 我们在进行运算符重载时,进行字符串的比较,使用的是strcmp函数,表明,此时我们比较的是该字符串的ASCII码值,而不是该字符串的大小和容量。

string类中迭代器的实现

在这里插入图片描述
从上图可以看出,范围for的本质就是迭代器,从迭代器可以实现语法糖来分析,足以看出迭代器身为STL六大天王之一不是浪得虚名的。并且此时注意: const修饰的迭代器,该迭代器对象是可以被改变的,只是该对象中指向的内容不可以被修改而已

扩容函数和字符、字符串插入函数

在这里插入图片描述

总了来说,字符和字符串插入删除函数大致上都是差不多的,细节方面处理到位就行,跟数据结构中的顺序表本质上是一样的,这里就不过多介绍。并且此时我们把字符插入这些函数实现之后,string类中的函数,也就完成了地基部分,此时我们完成了地基就可以开始盖房子了,由于时间关系,我们把盖房子部分留到下一篇博客。

具体代码如下: 包括测试部分(注释很全)

#define  _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;namespace wwx
{class String{public:typedef char* iterator;//普通类型迭代器typedef const char* const_iterator;//const类型迭代器(注意此时自己是可以修改的,只是解引用后的值是不可以修改的)iterator begin(){return _str;//此时因为String本质上就是一个字符数组,所以_str就是首元素地址,就是第一个字符}iterator end(){return _str + _size;//这个就是最后一个字符(前提是要知道第一个字符的位置)}const_iterator begin()const{return _str;}const_iterator end()const{return _str + _size;}String(const char* str = " ")//或者写成"\0",反正这个位置只要可以让strlen算出一个0来就行了(全缺省构造函数): _size(strlen(str)){_capacity = _size == 0 ? 3 : _size;//_capacity为0第一种解决方法_str = new char[_capacity + 1];strcpy(_str, str);}String(const String& s)//注意:拷贝构造也是有初始化列表的(并且要回想起以前有关this指针的知识,此时*this就是s3,str就是s2):_size(s._size),_capacity(s._capacity){//深拷贝(因为使用了指针,或者说因为有自己实现析构函数)_str = new char[s._capacity + 1];strcpy(_str, s._str);}//并且此时要记住,此时的拷贝构造除了利用*this指针以外,还有一个是使用赋值运算符(=)String& operator=(const String& s)//区分赋值和拷贝构造,赋值是两个已经存在的对象,而拷贝构造是一个已经初始化的对象去初始化另一个要创建的对象{if (this != &s)//防止自己给自己赋值{//_size = s._size;//_capacity = s._capacity;//delete[]_str;//这种赋值方法,可以很好的避免被赋值空间太大或太小的问题,只是伴随着开空间的消耗而已//_str = new char[s._capacity + 1];//strcpy(_str, s._str);//为了防止空间开辟失败把原来的空间中的数据给破坏,下面的写法就更好char* tmp = new char[s._capacity + 1];//解决原理:先开空间,再销毁,再给给strcpy(tmp, s._str);delete[]_str;_str = tmp;//此时因为指针指向的空间,本质是内置类型,所以会自己去调用拷贝构造函数,不需要调用我们自己实现的拷贝构造函数_size = s._size;_capacity = s._capacity;}return *this;}~String(){delete[]_str;_str = nullptr;_size = _capacity = 0;}const char* c_str(){return _str;}char& operator[](size_t pos)//普通类型使用,下面的是给特殊的const类型函数使用{assert(pos < _size);return _str[pos];}const char& operator[](size_t pos)const//后面位置上给了const前面就一定也要给一个const,因为此时加了const导致返回值的类型也变成了const类型{assert(pos < _size);return _str[pos];}size_t size()const{return _size;}//运算符重载// 所以得出结论,只要是函数内部不进行数据修改的,我们就把const给加上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;//return *this > s || s == *this;//此时就是简单的把赋值顺序调换一下,该代码就是有问题的,因为此时的s是const类型的,妥妥的权利放大}                                  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);}void resize(size_t n, char ch = '\0'){int len = strlen(_str);if (_capacity < n){reserve(2 * n);}if (n < _size){_size = n;_str[_size] = '\0';return;}_size = n;for (int i = len; i < _size; i++){_str[i] = ch;}_str[_size] = '\0';}void reserve(size_t n)//此时的这个n参数表示的就是n需要扩n个空间{char* tmp = new char[n + 1];//此时为了像上述一样,防止开辟失败,所以先开辟,再赋值(注意:capacity和字符个数的区别,capacity少1)strcpy(tmp, _str);//此时就是注意:只要是在类里面的函数都是自带一个this指针的类对象delete[]_str;_str = tmp;_capacity = n;//开空间跟_size是没有关系的,只有插入数据的时候才跟_size有关系}void push_back(char ch)//注意:有一个this指针,此时就是为了在这个this指针后面插入字符{if (_size == _capacity)//这种判断==的是需要多少扩多少(所以可以二倍二倍的扩){//注意此时要配套使用,不可以使用realloc扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;//string的本质就是一个字符数组++_size;_str[_size] = '\0';//此时就是因为插入字符之后,把原来的\0给搞没了,所以要重新给一个\0,不然就会导致无法计算strlen之类的问题}void append(const char* str)//注意此时这个函数是用来插入字符串的,不是上述用来插入字符的{size_t len = strlen(str);if (_size + len > _capacity)//此时这个位置表示的就是插入len个字符(插入len个字符,刚好等于capacity可以,但是不可以超过){//此时这种直接插入多个,就不可以2倍2倍的扩,需要一次性扩大一点reserve(_size + len);}strcpy(_str + _size, str);//该拷贝,是因为可以直接把原来字符串中的\0给覆盖掉//strcat(_str, str);//但是最好不要使用strcat,追加,目的是因为防止原字符串过长,\0不好找,因为strcat只有找到了\0才会进行追加_size += len;//字符串是不需要处理\0的,因为strcpy会拷贝\0}String& operator+=(char ch){push_back(ch);return *this;}String& operator+=(const char* str){append(str);return *this;}//某个位置插入、某个位置删除void insert(size_t pos, char ch)//某个位置插入字符{assert(pos <= _size);//防止传参的时候越界if (_size + 1 > _capacity)//还是因为等于的时候是刚刚好满了,所以不怕,只有大于的时候才需要扩{reserve(2 * _capacity);}size_t end = _size;while (end >= pos){_str[end + 1] = _str[end];--end;}_str[pos] = ch;++_size;}void insert(size_t pos, const char* str)//某个位置插入字符串{assert(pos <= _size);size_t len = strlen(str);if (len == 0){return;}if (_size + len > _capacity){reserve(_capacity + len);}//扩容完之后就是插入数据size_t end = _size + len;for (int i = end; i >= pos + len; --i){_str[i] = _str[i - len - 1];}char c = _str[pos + len];strcpy(_str + pos, str);_str[pos + _size] = c;_size += len;_str[_size] = '\0';}void erase(size_t pos, size_t len = npos){assert(pos>=0 && pos <= _size);size_t end = _size;while (end > pos){_str[end - 1] = _str[end];--end;}--_size;}private:char* _str;size_t _size;size_t _capacity;static size_t npos;//npos此时给一个静态成员变量,供给大家使用//有一个特例,可以不需要在全局定义static,但是只针对于整形,就是加一个const//static const size_t npos = -1;//static const size_t N = 10;//估计是为了可以这样使用:int _arr[N];};size_t String::npos = -1;void Print(const String& s){for (size_t i = 0; i < s.size(); ++i){cout << s[i] << " ";//此时因为这个函数就是一个const修饰的函数,所以无论在函数内存调用运算符重载,还是别的函数,此时这些函数都需要有const属性,所以导致我们需要实现两个[]运算符重载,一个给普通类型使用,一个给const类型使用}cout << endl;for (auto ch : s)//证明const属性的迭代器(所以需要把迭代器也给弄成两份,一份普通类型,一份const类型){cout << ch << " ";}cout << endl;}void test_string1(){String s1;String s2("hello world");cout << s1.c_str() << endl;cout << s2.c_str() << endl;for (int i = 0; i < 10; i++){s2[i]++;}String s3(s2);//拷贝构造(经典的指针指向同一块空间问题),涉及深拷贝cout << s3.c_str() << endl;cout << s2.c_str() << endl;s3 = s2;//赋值要注意有自己给自己赋值的时候 s2 = s2;cout << s3.c_str() << endl;cout << s2.c_str() << endl;}void test_string2()//验证const修饰的函数需要使用具有const属性的函数{wwx::String s1("hello world");for (size_t i = 0; i < s1.size(); ++i)//注意:此时访问的不是string类中的成员变量,访问的是计算size大小的公有函数{cout << ++s1[i] << " ";}cout << endl;Print(s1);}void test_string3()//验证正向迭代器(反向迭代器先不学){String s1("gdkkn vnqkc");String::iterator it = s1.begin();//普通it类型String::const_iterator it2 = s1.begin();//const类型的itwhile (it != s1.end()){cout << ++(*it) << " ";//指针不仅可以读,而且可以写++it;//虽然被const修饰,但是自己是可以修改的,例:++it是可以的++(*it)就是不可以的(总:自己可以修改,只是指向的内容不可以修改而已)++it2;//cout << ++(*it2) << " ";//const迭代器指向的内容不允许被修改}cout << endl;for (auto ch : s1)//很好的证明了,范围for就是使用迭代器实现的(傻白甜){cout << ch << " ";}cout << endl;}void test_string4()//验证运算符重载{string s1("hello world");string s2("hello world");string s3("xxxxxxxxxxx");//比大小,此时比的是ASCII码值cout << (s1 < s3) << endl;//涉及运算符的优先级,所以要加上()cout << (s1 == s3) << endl;cout << (s1 == s2) << endl;cout << (s1 >= s3) << endl;cout << (s1 > s3) << endl;cout << (s1 != s2) << endl;}void test_string5()//验证字符和字符串追加{string s1("hello world");s1.push_back(' ');//反正就是注意使用this指针就行(因为this指针代表的就是s1对象)cout << s1.c_str() << endl;s1.append("xxxxxxxxxxxxxxxx");cout << s1.c_str() << endl;s1 += "aaaaaaaaaaaaaaaaaa";cout << s1.c_str() << endl;}void test_string6(){String s1("hello world");s1.insert(6, 'm');s1.insert(7, 'y');s1.insert(8, ' ');//搞定了中间插入,此时要防止是在最头上插入等问题cout << s1.c_str() << endl;s1.insert(5, "bit");cout << s1.c_str() << endl;s1.erase(5, 3);cout << s1.c_str() << endl;}
}

以上就是string类地基部分代码,注释很全,注意:测试代码需要放到test.cpp文件中测试

在这里插入图片描述

总结:还是那句话,自己实现string类,可以把以前学的知识得到很好的巩固。

相关文章:

Learning C++ No.11【string类实现】

引言&#xff1a; 北京时间&#xff1a;2023/2/19/8:48&#xff0c;昨天更新了有关进程状态的博客&#xff0c;然后在休息的时候&#xff0c;打开了腾讯视屏&#xff0c;然后看到了了一个电视剧&#xff0c;导致上头&#xff0c;从晚上6点看到了10点&#xff0c;把我宝贵的博客…...

实力见“证”:Tapdata 技术创新与发展潜力广受认可

Tapdata 积极拥抱各种“不确定”&#xff0c;变中求新&#xff0c;只为呈现出更加好用的产品。 而 Tapdata 在专业领域不断深耕&#xff0c;持续打磨产品能力的同时&#xff0c;也收获了诸多来自外界的肯定&#xff0c;从用户到投资人&#xff0c;从生态伙伴到技术媒体以及官方…...

【C++修炼之路】18.map和set

每一个不曾起舞的日子都是对生命的辜负 map和setmap和set一.关联式容器二.set2.1 set的介绍2.2 set的使用1.set的模板参数列表2.set的构造3.set的迭代器4.set修改操作5.bound函数三.multiset四.map3.1 map的介绍3.2 map的使用1.map的模板参数说明2.pair的介绍3.map的[]重载五.m…...

ChatGPT原理与技术演进剖析

—— 要抓住一个风口&#xff0c;你得先了解这个风口的内核究竟是什么。本文作者&#xff1a;黄佳 &#xff08;著有《零基础学机器学习》《数据分析咖哥十话》&#xff09; ChatGPT相关文章已经铺天盖地&#xff0c;剖析&#xff08;现阶段或者只能说揣测&#xff09;其底层原…...

Retrofit+Hilt后端请求小项目1--项目介绍

简介 本项目根据 youtube 对应教程实现而来 将会对对应代码以及依赖&#xff08;如 Hilt、retrofit、coil&#xff09;进行详细的分析与解读&#xff0c;同时缕清项目结构安排 如文章有叙述不清晰的&#xff0c;请直接查看原教程&#xff1a;https://www.youtube.com/watch?…...

实际项目角度优化App性能

前言&#xff1a;前年替公司实现了一个在线检疫App&#xff0c;接下来一年时不时收到该App的需求功能迭代&#xff0c;部分线下问题跟进。随着新冠疫情防控政策放开&#xff0c;该项目也是下线了。 从技术角度来看&#xff0c;有自己的独特技术处理特点。下面我想记录一下该App…...

Structure|Alphafold2在肽结构预测任务上的基准实验

​题目&#xff1a;Benchmarking AlphaFold2 on peptide structureprediction 文献来源&#xff1a;2023, Structure 31, 1–9 代码&#xff1a;基准实验&#xff0c;比较了比较多的模型 1.背景介绍 由2-50个氨基酸构成的聚合物可以称为肽。但是关于肽和蛋白质之间的差异还是…...

Simple XML

简介 官网&#xff1a;https://simple.sourceforge.net/home.php Github&#xff1a;https://github.com/ngallagher/simplexml Simple 是用于 Java 的高性能 XML 序列化和配置框架。它的目标是提供一个 XML 框架&#xff0c;使 XML 配置和通信系统的快速开发成为可能。该框架…...

在代码质量和工作效率的矛盾间如何取舍?

这个问题的答案是&#xff0c;在很短的一段时期&#xff0c;编写高质量代码似乎会拖慢我们的进度。与按照头脑中首先闪现的念头编写代码相比&#xff0c;高质量的代码需要更多的思考和努力。但如果我们编写的不仅仅是运行一次就抛之脑后的小程序&#xff0c;而是更有实质性的软…...

rabbitMq安装(小短文)--未完成

rabbitMq是在activeMq的基础上创造的&#xff0c;有前者的功能&#xff0c;比前者强&#xff0c;属于后来居上。系统环境:windows10首先下载相关软件Erlang&#xff0c;因为他是这个语言写的。https://www.erlang.org/downloads然后安装&#xff0c;并且弄到环境变量里验证是否…...

Python调用MMDetection实现AI抠图去背景

这篇文章的内容是以 《使用MMDetection进行目标检测、实例和全景分割》 为基础&#xff0c;需要安装好 MMDetection 的运行环境&#xff0c;同时完成目标检测、实例分割和全景分割的功能实践&#xff0c;之后再看下面的内容。 想要实现AI抠图去背景的需求&#xff0c;我们需要…...

Java代码使用最小二乘法实现线性回归预测

最小二乘法简介最小二乘法是一种在误差估计、不确定度、系统辨识及预测、预报等数据处理诸多学科领域得到广泛应用的数学工具。它通过最小化误差&#xff08;真实目标对象与拟合目标对象的差&#xff09;的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数…...

linux-rockchip-音频相关

一、查看当前配置声卡状态 cat /proc/asound/cards二、查看当前声卡工作状态 声卡分两种通道&#xff0c;一种是Capture、一种是Playback。Capture是输入通道&#xff0c;Playback是输出通道。例如pcm0p属于声卡输出通道&#xff0c;pcm0c属于声卡输入通道。 ls /proc/asoun…...

Android Handler的内存抖动以及子线程创建Handler

一、介绍 Handler&#xff0c;作为一个在主线程存活的消息分发工具&#xff0c;在App开发过程使用频率很高&#xff0c;也是面试问的比较多的。 面试常见的比如&#xff1a;子线程如何创建&#xff1f;Handler的机制是什么&#xff1f;内存抖动等&#xff0c;接下来我们会针对H…...

机器学习算法原理之k近邻 / KNN

文章目录k近邻 / KNN主要思想模型要素距离度量分类决策规则kd树主要思想kd树的构建kd树的搜索总结归纳k近邻 / KNN 主要思想 假定给定一个训练数据集&#xff0c;其中实例标签已定&#xff0c;当输入新的实例时&#xff0c;可以根据其最近的 kkk 个训练实例的标签&#xff0c…...

【期末复习】例题说明Prim算法与Kruskal算法

点睛Prim与Kruskal算法是用来求图的最小生成树的算法。最小生成树有n个顶点&#xff0c;n-1条边&#xff0c;不能有回路。Prim算法Prim算法的特点是从个体到整体&#xff0c;随机选定一个顶点为起始点出发&#xff0c;然后找它的权值最小的边对应的另一个顶点&#xff0c;这两个…...

AtCoder Beginner Contest 290 A-E F只会n^2

ABC比较简单就不再复述 D - Marking 简要题意 &#xff1a;给你一个长度为nnn的数组,下标为0到n−10 到 n-10到n−1&#xff0c;最初指针位于0,重复执行n-1次操作&#xff0c;每次操作的定义为将当前指针加上ddd&#xff0c;如果该位置为空(未填数),否则我们向右找到第一个为空…...

springMvc源码解析

入口&#xff1a;找到springboot的自动配置&#xff0c;将DispatcherServlet和DispatcherServletRegistrationBean注入spring容器&#xff08;DispatcherServletRegistrationBean间接实现了ServletContextInitializer接口&#xff0c;最终ServletContextInitializer的onStartup…...

采用aar方式将react-native集成到已有安卓APP

关于react-native和android的开发环境搭建、环境变量配置等可以查看官方文档。 官方文档地址 文章中涉及的node、react等版本&#xff1a; node:v16.18.1 react:^18.1.0 react-native:^0.70.6 gradle:gradle-7.2开发工具&#xff1a;VSCode和android studio 关于react-native和…...

Tomcat目录介绍,结构目录有哪些?哪些常用?

bin 启动&#xff0c;关闭和其他脚本。这些 .sh文件&#xff08;对于Unix系统&#xff09;是这些.bat文件的功能副本&#xff08;对于Windows系统&#xff09;。由于Win32命令行缺少某些功能&#xff0c;因此此处包含一些其他文件。 比如说&#xff1a;windows下启动tomcat用的…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

OCR MLLM Evaluation

为什么需要评测体系&#xff1f;——背景与矛盾 ​​ 能干的事&#xff1a;​​ 看清楚发票、身份证上的字&#xff08;准确率>90%&#xff09;&#xff0c;速度飞快&#xff08;眨眼间完成&#xff09;。​​干不了的事&#xff1a;​​ 碰到复杂表格&#xff08;合并单元…...