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

C++ STL学习之【string类的模拟实现】

✨个人主页: Yohifo
🎉所属专栏: C++修行之路
🎊每篇一句: 图片来源

  • The key is to keep company only with people who uplift you, whose presence calls forth your best.

    • 关键是只与那些提升你的人在一起,他们的存在唤起了你最好的一面。

    配图


文章目录

  • 🪁前言
  • 🥏正文
    • 1、结构
    • 2、默认成员函数
      • 2.1、构造与析构
      • 2.2、拷贝与赋值
    • 3、访问数据
      • 3.1、下标访问
      • 3.2、迭代器访问
    • 4、修改数据
      • 4.1、尾插
      • 4.2、附加
      • 4.3、重载
      • 4.4、任意位置插入、删除
    • 5、查看容量
      • 5.1、大小、容量、判空
      • 5.2、扩充容量
      • 5.3、调整大小
    • 6、运算符重载
      • 6.1、字符串相加
      • 6.2、逻辑判断
    • 7、其他
      • 7.1、查找
      • 7.2、清理、交换
      • 7.3、获取原生指针
    • 8、读取与写入
      • 8.1、流插入
      • 8.2、流提取
      • 8.3、获取整行串
    • 9、整体代码
  • 🎯总结


🪁前言

string 本质上就是一个专注于存储字符的顺序表,使用起来很方便;但在模拟实现 string 时,有许多值得注意的点,下面就来看看 string 类是如何诞生的吧

注意: string 接口众多,本文模拟实现的只是部分常用接口

string区别


🥏正文

跟着官方文档走,string 分割成了几个部分进行实现

《string官方文档》

1、结构

string 本质上就是一个顺序表,因此它也是由 指针大小容量 这几部分组成

namespace Yohifo	//命名空间
{//定义一个 string 类class string{private:char* _str;	//数据指针size_t _size;	//大小size_t _capacity;	//容量};
}

大小容量 设为 size_t,上限值为 42亿多,即无符号整型 -1,这个值常常被用来当作循环结束判断条件和查找时未找到的标志,因此有一个专门用来表示 size_t -1 的变量 npos

public:static const size_t npos = -1;	//定义能全局使用的 npos 值(public)

npos 需要设置为公开,否则类外就无法使用了

注意:

  • npos 类型为 static const size_t
  • static 修饰后,npos 只能被初始化一次
  • 而加了 const 后,允许在类中赋予缺省值进行初始化,如果不加 const,则必需到类外手动初始化静态成员
  • const 修饰的静态变量,只允许整型家族在类中设置缺省值
static const char c = 1;	//合法,为整型家族static const double d = 3.14;	//非法,只允许整型家族操作

2、默认成员函数

string 中的四大默认成员函数需要自己设计,因为涉及空间申请与释放,以及后续的深拷贝问题
其他的两个默认成员函数没有必要自己设计,库中的就已经够用了
成员函数
注意: 此时的默认成员函数均在类中直接实现,成为内联函数

2.1、构造与析构

构造函数
使用缺省参数,当用户未传递字符串时,将 string 对象初始化为空串;此时 构造函数 可以利用初始化列表进行初始化

//default	默认成员函数
string(const char* str = ""):_size(strlen(str))
{_capacity = _size;_str = new char[_capacity + 1];	//预留 '\0' 的位置strcpy(_str, str);
}

注意:

  • 为了确保程序的正确性,在初始化列表中只初始化 大小,再将 大小 赋值给 容量,避免出现赋值为随机值的情况(初始化列表初始化顺序只与类中的声明顺序有关
  • 开辟空间时,需要多开一个空间,存储 ‘\0’

析构函数
析构函数 中在释放内存时,统一为 delete[] 的形式,因此其他地方在申请内存时,即使只申请一个 char,也要写成 new char[1] 的形式,目的就是与销毁对应

~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}

2.2、拷贝与赋值

拷贝构造
涉及空间开辟,此处为深拷贝,即先开辟一块等量空间,再拷贝数据至新空间,完成拷贝

string(const string& s)
{//将 s 对象拷贝给 *this_str = new char[s._capacity + 1];	//开空间、赋值,深拷贝_size = s._size;_capacity = s._capacity;strcpy(_str, s._str);
}

注意: 在申请空间后,一定要 记得使用 strcpy 进行数据拷贝,否则就是无效操作

赋值重载
赋值重载 函数在实现时需要注意几种情况:

  • 是否为同一对象的赋值
  • 被赋值对象空间是否足够

前者用一个判断就可以很好解决,而后者在设计时,是 先借助临时变量开辟空间,若空间开辟成功,则将数据拷贝至新空间,释放原空间,改变指针 _str 指向;若空间开辟失败,则抛出异常,同时还确保了原空间数据不被损坏

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;	//需要返回,避免 a = b = c 连续赋值情况
}

3、访问数据

string 离不开数据访问函数,如同顺序表一样,可以直接通过 [] + 下标 访问数据,同时也可以通过 迭代器 访问数据

访问数据
注意: 下标访问 在类中定义,类外实现;迭代器 则是直接在类中定义

3.1、下标访问

类中的数据为私有,无法直接访问,但可以 通过函数间接访问

#include"String.h"using namespace Yohifo;//access	访问相关
char& string::operator[](size_t pos)
{assert(pos < _size);	//下标必须小于 _size//通过下标访问return _str[pos];
}//需要再提供一个 const 版本
const char& string::operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}

注意: 在类外实现的函数,需要先包含命名空间(这里使用的是全局展开),再通过 :: 访问到指定的类,才能正常实现函数

3.2、迭代器访问

迭代器STL 六大组件之一,适用于所有容器,我们这里只是简单模拟实现 迭代器,使用的是获取原生指针的方式

//需要通过 typedef 重命名数据类型typedef char* iterator;	//简易迭代器
typedef const char* const_iterator;	//简易const迭代器//iterator	迭代器
iterator begin()
{return _str;
}
const_iterator cbegin() const
{//需要再新增一个 const 版本return _str;
}iterator end()
{return _str + _size;
}
const_iterator cend() const
{return _str + _size;
}

下面来看看通过 原生指针 实现的 迭代器 效果:

测试


4、修改数据

string 支持对其中的数据进行任意修改:尾插字符、尾插字符串、任意位置插入删除都可以

修改相关

4.1、尾插

尾插即 push_back,这个东西在数据结构实现阶段已经很熟悉,对于顺序表直接在尾部插入数据即可,当然插入前需要先判断是否需要扩容

//modify	修改相关
void string::push_back(char ch)
{//检查容量是否足够if (_size + 1 > _capacity){_capacity == 0 ? _capacity = 1 : 0;reserve(2 * _capacity);	//二倍扩容}//尾插字符_str[_size] = ch;_str[++_size] = '\0';
}

注意: VS 中的 string 扩容机制为 1.5 倍扩容,我们这里直接采用二倍扩容的方式

4.2、附加

append 译为附加,指在 string 尾部附加 n 个字符或 str 字符串

附加字符
附加字符直接调用 npush_back 即可,不过值得注意的是,为了避免二倍扩容而造成的空间浪费,可以提前将空间扩容至 _size + n 节省空间

string& string::append(size_t n, char ch)
{//复用代码,尾插n次//提前开空间if (_size + n > _capacity)reserve(_size + n);while (n--)push_back(ch);return *this;
}

附加字符串
附加字符串的话,同样需要判断空间是否足够,如果不够就扩容,然后再 调用库函数 strcat 即可完成字符串附加

string& string::append(const char* str)
{int len = strlen(str);if (_size + len > _capacity)reserve(_size + len);//调用库函数strcat(_str, str);_size += len;return *this;
}

注意: 附加字符串在完成操作后,需要对 _size 作出改变

4.3、重载

push_backappend 在实际中用的都比较少,一般是直接使用运算符重载 += 实现拼接

+= 实际就是对尾插字符和尾插字符串这两种功能的封装,使用起来更加方便

string& string::operator+=(char ch)
{//复用return append(1, ch);
}string& string::operator+=(const string& s)
{//复用return append(s._str);
}

复用代码可以尽可能的减少错误的出现

4.4、任意位置插入、删除

任意位置的操作,需要对原数据进行挪动

尤其是 pos = 0 处的操作,需要格外注意

任意位置插入
可以分为两步:挪动数据插入数据

string& string::insert(size_t pos, size_t n, char ch)
{assert(pos < _size);if (_size + n > _capacity)reserve(_size + n);//挪动size_t end = size() + n;	//错位while (end > pos){_str[end] = _str[end - n];end--;}//赋值//从 pos 位置开始,插入 n 个字符size_t count = n;while (count--){_str[pos++] = ch;}_size += n;return *this;	//有返回值
}

假设存在 string 对象为 "abcde",现需要在 pos = 0 位置插入 "123",具体执行结果如下所示

动图展示
注意: while 循环中,不推荐将条件写为 >= ,因为两者都是 size_t 类型,当 pos = 0 时,可能会出现死循环的情况,因此推荐写为 > 的方式,定义 endsize() + n 处,这样错位处理后可以有效避免死循环问题

string 也支持任意位置插入字符串,此时挪动 len 个字符,再通过 strncpy 函数覆盖字符串即可

string& string::insert(size_t pos, const char* str)
{assert(pos < _size);int len = strlen(str);if (_size + len > _capacity)reserve(_size + len);//挪动size_t end = size() + len;while (end > pos){_str[end] = _str[end - len];end--;}//衔接strncpy(_str + pos, str, len);	//注意:只拷贝 len 个return *this;
}

strncpy 拷贝 len 个字符,避免将字符串 str 中的 '\0' 也拷贝进去

任意位置删除
任意位置删除函数为 全缺省参数

  • 参数1 size_t pos,默认 pos = 0
  • 参数2 size_t len,默认 len = npos

删除元素分为两种情况

  • 元素不够删或 len == npos,此时需要全部删除,即 _size = 0
  • 元素足够删,将 pos + len 处的字符串覆盖至 pos
string& string::erase(size_t pos, size_t len)
{assert(pos < _size);assert(!empty());if (len == npos || _size < pos + len){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}

假设存在 string 对象为 "123abcde",现需要在 pos = 3 位置删除 2个元素,具体执行结果如下所示
任意位置删除

删除并不是真删除,只要合理的调整 '\0' 位置和 _size 值,使访问不到后续元素就行了

erase 还支持通过迭代器区间删除元素,实现很简单,通过 指针 - 指针 获取元素个数(下标),复用上面的代码就行了

string::iterator string::erase(iterator begin, iterator end)
{//复用erase(begin - _str, end - _str);return this->begin();
}

5、查看容量

顺序表支持查看各种数据,如大小、容量,同时动态增长的顺序表还有一个不可缺的功能:扩容,对应到 string 中,扩容由 reserve 完成,而调整大小由 resize 负责

容量相关

5.1、大小、容量、判空

获取这些数据时,因为不需要对 *this 做出修改,这里均使用 const 修饰 *this

// capacity	容量相关
size_t string::size() const
{return _size;
}size_t string::capacity() const
{return _capacity;
}bool string::empty() const
{return _size == 0;
}

这三个函数很简单,不做过多赘述

5.2、扩充容量

reserve 可以扩充 _str 的容量,具体使用时,只需要通过 reserve(size_t capacity) 的方式,即可将 _str 容量改变为 capacity

注意:

  • 传入的 capacity 可能小于或等于 _capacity,此时不需要缩容,什么操作都不需要
  • 传入的 capacity 大于 _capacity,正常扩容,具体逻辑和赋值重载一致
void string::reserve(size_t capacity)
{//Mask:当扩容容量小于 _size 值时,_size 会变成什么样子//Reply: 不变,连 _capacity 也不变if (capacity > _capacity){//开空间+拷贝char* tmp = new char[capacity + 1];strcpy(tmp, _str);//释放空间+改变指向delete[] _str;_str = tmp;_capacity = capacity;}
}

5.3、调整大小

resize 函数为半缺省参数,缺省参数为参数2 char ch = '\0',参数1为 size_t size

调整大小的步骤:

  • 判断 size 是否大于 _capacity,如果大于则需要扩容
  • _size 处开始,填入字符 ch,直到 size 结束
  • 重新赋值 _str[_size] = '\0'
void string::resize(size_t size, char ch)
{//考虑扩容的情况if (size > _capacity)reserve(size);//如果给定的容量大于 _size 就需要植入字符while (_size < size)_str[_size++] = ch;_size = size;	//重新赋值,以防 _size > size的情况_str[_size] = '\0';	//置 '\0'
}

注意: _size = size 这一步不能省略,防止 size 小于 _size 时大小不改变的问题


6、运算符重载

string 中还存在许多重载函数

运算符重载

6.1、字符串相加

"abc" + "123" = "abc123" 这种情况是合法的,当然也存在这个相加函数,无非就是借助临时变量做字符或字符串附加操作

//operator	运算符重载
string string::operator+(char ch) const
{string tmp(*this);	//传值返回,需要借助第三方变量tmp.push_back(ch);return tmp;
}string string::operator+(const string& s) const
{string tmp(*this);tmp.append(s._str);return tmp;
}

注意: 对于操作双方都不能作出修改,因此需要借助临时变量 tmp;返回时,需要使用传值返回,接收时调用拷贝构造,因为 tmp 是局部变量

6.2、逻辑判断

string 对象的大小判断是借助于 ASCII 码值,可以直接使用 strcmp 函数

只需要实现 小于等于 判断,其他逻辑判断可以复用代码

bool string::operator<(const string& s) const
{//直接调用库函数return strcmp(_str, s._str) < 0;
}bool string::operator>(const string& s) const
{//复用逻辑return !(*this <= s);	//不小于等于就是大于
}bool string::operator==(const string& s) const
{return strcmp(_str, s._str) == 0;
}bool string::operator!=(const string& s) const
{return !(*this == s);	//等于取反就是不等于
}bool string::operator<=(const string& s) const
{return (*this < s) || (*this == s);	//小于或等于
}bool string::operator>=(const string& s) const
{return !(*this < s);	//不小于就是大于或等于
}

7、其他

string 中还有其他实用的函数,如查找字符或字符串、清理字符串、交换两个字符串等

其他函数

7.1、查找

查找字符
传入目标字符,遍历一遍字符串,若找到,返回目标下标,没找到返回 npos
默认 size_t pos = 00 处开始向后查找,也支持传入参数从指定位置开始查找

//other	其他
size_t string::find(char c, size_t pos) const
{assert(pos < _size);//从 pos 位置开始,挨个比较while (pos < _size){if (_str[pos] == c)return pos;pos++;}return npos;	//没找到返回 npos
}

查找字符串
在算法界存在一个大名鼎鼎的字符串查找算法:KMP 匹配算法,该算法在子串重复字符较多时比较实用,效率很高,但在实际中,字符串中的重复字符较少,使用 KMP 的查找效率和 strstr 暴力匹配效率相差不大,所以这里直接调用函数 strstr

size_t string::find(const char* s, size_t pos) const
{assert(pos < _size);//此处可以使用 KMP 算法,不过意义不大char* dst = strstr(_str, s);	//调用库函数if (dst == NULL)return npos;return dst - _str;	//返回下标(位置)
}

注意: 指针 - 指针 就是实际找到字符串的下标(位置)

7.2、清理、交换

有时候需要将字符串中的内容一键清空,有时候也需要将两个字符串进行交换

清理
清理的逻辑很简单,令 _size = 0,再令 _str[_size] = '\0' 即可

void string::clear()
{//置空_size = 0;_str[_size] = '\0';
}

交换
可以直接使用库中的 swap 交换函数,但这样做的 效率较低会发生多次拷贝构造操作,而且都是 深拷贝,可以稍微变通下,string 中的三个成员分别 swap,此时是 浅拷贝,效率很高,也能完成交换任务

void string::swap(string& s)
{//直接调用库函数进行三次浅拷贝,避免发生深度拷贝构造行为std::swap(_str, s._str);	//交换指针std::swap(_size, s._size);	//交换大小std::swap(_capacity, s._capacity);	//交换容量
}

7.3、获取原生指针

C++兼容C语言,在部分场景中,需要获取指针字符串的指针,但此时 _str 为私有成员,所以需要通过函数间接获取指针 _str

char* const string::c_str() const
{//返回原生指针,方便与 C语言 接口统一return _str;
}

8、读取与写入

流操作是 string 中少有的类外成员函数,因为此时的左操作数为 ostreamistream

流操作
注意: 这里不需要设为友元函数,因为有很多函数可以辅助我们完成任务

8.1、流插入

string 对象的内容直接输出到屏幕上
通过下标访问的方式输出内存

//流插入
ostream& Yohifo::operator<<(ostream& _cout, const string& s)
{//借助访问函数,输出字符串size_t pos = 0;while (pos < s.size())_cout << s[pos++];return _cout;
}

还可以使用 迭代器原生指针 输出

8.2、流提取

流提取分析:

  1. 在获取字符串前,不知道用户输入的字符串长度,无法提前开辟空间
    • 如果采用默认2倍扩容的方式,势必会造成严重的空间浪费
  2. 读取数据后,若字符串中已存在数据,需要覆盖原数据

解决方案:

  • 借助一个 char buff[128] 数组存储数据,当数组装满时,将 buff 拼接至字符串尾部,buff 重新开始存储数据,这样无论输入多长的字符串,都可以很好的读取,而且避免了空间的浪费
  • 调用 clear() 函数先清理字符串,再进行输入

buff数组

//流提取
istream& Yohifo::operator>>(istream& _cin, string& s)
{s.clear();	//流插入前先清理//此时输入字符串大小未知,需要通过 buff 数组不断装载的方式实现流插入char buff[128] = { 0 };	//大小为128int pos = 0;char ch = _cin.get();	//获取字符while (ch != ' ' && ch != '\n'){buff[pos++] = ch;if (pos == 127){//拼接至 ss += buff;pos = 0;	//重新装载}ch = _cin.get();}if (pos < 127){//此时需要手动置 '\0'buff[pos] = '\0';s += buff;	//链接}return _cin;
}

注意:

  • 逐字符读取,可以使用 cin.get() 函数,类似于 getc() 函数
  • 流提取的结束条件是遇到 空白字符 就结束
  • while 循环结束后,如果 pos < 127,需要置入 '\0',避免插入两个半(或更多) buff 数据的情况

buff 数组是一个 局部变量,不会造成空间浪费

8.3、获取整行串

getline 函数可以读取到空格,实现逻辑95%都和流提取一致,不过在循环结束条件中,getline 只取决于是否读取到 '\n'

//获取一行字符串
istream& Yohifo::getline(istream& _cin, string& s)
{//大体逻辑与流提取一致,不过判断条件减少s.clear();	//先清理char buff[128] = { 0 };	//大小为128int pos = 0;char ch = _cin.get();	//获取字符while (ch != '\n'){buff[pos++] = ch;if (pos == 127){//链接至 ss += buff;pos = 0;	//重新装载}ch = _cin.get();}if (pos < 127){//此时需要手动置 '\0'buff[pos] = '\0';s += buff;	//链接}return _cin;
}

9、整体代码

文中所有代码都存储 Gitee 仓库,可以通过下面的链接直接跳转查看

sting_2_28 代码仓库


🎯总结

以上就是本次关于 string 类模拟实现的全部内容了,string 比较适合尝试自己实现,相信在实现之后,对 string 类的理解和使用能更上一层楼

如果你觉得本文写的还不错的话,可以留下一个小小的赞👍,你的支持是我分享的最大动力!

如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


星辰大海

相关文章推荐

C++ STL 学习之【string】

C++【模板初阶】

C/C++【内存管理】

===============

类和对象实操

类和对象实操之【日期类】

感谢支持

相关文章:

C++ STL学习之【string类的模拟实现】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f38a;每篇一句&#xff1a; 图片来源 The key is to keep company only with people who uplift you, whose presence calls forth your best. 关键是只与那些提升你的人在一起&#xff0c…...

Selenium基于POM的自动化测试实践

什么是Page Object模式 Page Object 见名知意&#xff0c;就是页面对象&#xff0c;并将页面元素定位方法和元素操作进行分离。在实际自动化测试实战过程中&#xff0c;我们一般对脚本的实现分为三层&#xff1a; (1)对象层&#xff1a; 用于存放页面元素定位和控件操作 (2)逻…...

记录每日LeetCode 2373.矩阵中的局部最大值 Java实现

题目描述&#xff1a; 给你一个大小为 n x n 的整数矩阵 grid 。 生成一个大小为 (n - 2) x (n - 2) 的整数矩阵 maxLocal &#xff0c;并满足&#xff1a; maxLocal[i][j] 等于 grid 中以 i 1 行和 j 1 列为中心的 3 x 3 矩阵中的 最大值 。 换句话说&#xff0c;我们希…...

QT中级(6)基于QT的文件传输工具(2)

QT中级&#xff08;6&#xff09;基于QT的文件传输工具&#xff08;2&#xff09;本文实现第一步1 新增功能2 运行效果3 实现思路4 源代码实现这个文件传输工具大概需要那几步&#xff1f;实现多线程对文件的读写实现TCP客户端和服务端实现网络传输 书接上回&#xff1a;QT中级…...

【Linux】工具(3)——gcc/g++

咱们继续进阶&#xff0c;接下来进入到Linux工具中gcc和g的学习在本章博客正式开始介绍之前&#xff0c;我们先要弄清楚程序是怎么翻译的&#xff1a;C语言程序环境一、什么是gcc/g&#x1f4cc;gcc是一个c编译器&#xff0c; g是c编译器。我们根据代码的后缀名来判断用哪个编译…...

Android文件选择器

使用方法:在里层的build.grade的dependency里面加入: implementation com.leon:lfilepickerlibrary:1.8.0 引用https://github.com/leonHua/LFilePicker/blob/master/README_CH.md#lfilepicker LFilePicker 说明:如果发现应用名称被修改,可以参考issues#26 查看解决方案,或…...

《MySql学习》 Select 查询语句慢的非性能原因

一.查询被阻塞 A会话执行 查询操作&#xff0c;长时间没有返回信息,此时我们就可以去排查一下是否是被阻塞了 select * from words 被阻塞的原因有很多&#xff0c;首先列举第一种情况 1.等MDL锁 当我们执行DDL语句时&#xff0c;会自动给表加上MDL写锁。当执行DML和DQL时&…...

Vue组件间通信方式超详细(父传子、父传后代、子传父、后代传父、兄弟组件传值)

一、父传子、父传后代 方式一&#xff1a;子通过props来接收 父组件&#xff1a;父组件引入子组件时&#xff0c;通过<child :parentValue "parentValue"></child>子组件传值。 备注&#xff1a;这种方式父传值很方便&#xff0c;但是传递给后代组件不…...

【ES】Elasticsearch-深入理解索引原理

文章目录Elasticsearch-深入理解索引原理读操作更新操作SHARD不变性动态更新索引删除和更新实时索引更新持久化Segment合并近实时搜索&#xff0c;段数据刷新&#xff0c;数据可见性更新和事务日志更新索引并且将改动提交修改Searcher对象默认的更新时间Elasticsearch-深入理解…...

pdf压缩文件大小的方法是什么?word文件怎么批量转换成pdf格式?

大家在存储文件时&#xff0c;通常会遇到一些较大的文件&#xff0c;这时需要对其进行压缩处理。下面介绍一下如何压缩PDF文件大小以及批量转换Word文件为PDF格式。pdf压缩文件大小的方法是什么?1.打开小圆象PDF转换器&#xff0c;选择“PDF压缩”功能。2.在“PDF压缩”界面中…...

论文阅读——FECANet:应用特征增强的上下文感知小样本语义分割网络

代码&#xff1a;NUST-Machine-Intelligence-Laboratory/FECANET (github.com) 文章地址&#xff1a;地址 文章名称&#xff1a;FECANet: Boosting Few-Shot Semantic Segmentation with Feature-Enhanced Context-Aware Network 摘要 Few-shot semantic segmentation 是学习…...

数组模拟常见数据结构

我们来学习一下用数组模拟常见的数据结构&#xff1a;单链表&#xff0c;双链表&#xff0c;栈&#xff0c;队列。用数组模拟这些常见的数据结构&#xff0c;需要我们对这些数据结构有一定的了解哈。单链表请参考&#xff1a;http://t.csdn.cn/SUv8F 用数组模拟实现比STL要快&a…...

ADC0832的AD模数转换原理及编程

✅作者简介&#xff1a;嵌入式领域优质创作者&#xff0c;博客专家 ✨个人主页&#xff1a;咸鱼弟 &#x1f525;系列专栏&#xff1a;单片机设计专栏 &#x1f4c3;推荐一款求职面试、刷题神器&#x1f449;注册免费刷题 目录 一、描述 二、模数转换原理&#xff1a; 三、…...

【工具插件类教学】UnityPackageManager私人定制资源工具包

目录 一.UnityPackageManager的介绍 二.package包命名 三.包的布局 四.生成清单文件 五.制作package内功能 六.为您的软件包撰写文档 1.信息的结构 2.文档格式 七.提交上传云端仓库 1.生成程序集文件 2.上传至云端仓库 八.下载使用package包 1.获取包的云端路径 …...

【软件测试】2023年了还不会接口测试?老鸟总结接口测试面试谁还敢说我不会......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 测试面试&#xff0…...

类Vuex轻量级状态管理实现

引用自 摸鱼wiki 1. vuex vuex是一个前端广泛流行的状态管理库&#xff0c;主要由以下几大模块组成&#xff1a; state&#xff1a;状态存储getter&#xff1a;属性访问器mutation&#xff1a;可以理解为一个同步的原子性事务&#xff0c;修改state状态action&#xff1a;触发…...

Java 基本数据类型

Java基本数据类型是Java编程语言中最基本的数据类型&#xff0c;包括整型、浮点型、字符型、布尔型和空类型。本文将详细介绍Java基本数据类型的作用和在实际工作中的用途。 整型&#xff08;int、long、short、byte&#xff09; 整型是Java中最常见的基本数据类型&#xff0…...

全网资料最全Java数据结构与算法-----算法分析

算法分析 研究算法的最终目的就是如何花更少的时间&#xff0c;如何占用更少的内存去完成相同的需求&#xff0c;并且也通过案例演示了不同算法之间时间耗费和空间耗费上的差异&#xff0c;但我们并不能将时间占用和空间占用量化&#xff0c;因此&#xff0c;接下来我们要学习…...

【封装xib补充 Objective-C语言】

一、那么首先,咱们就从这个结果来分析 1.就不给大家一步一步分析了,直接分析我们这里怎么想的, 首先,我们看到这样的一个界面,我们想,这些应用数据是不是来源于一个plist文件吧, 所以说,我们首先要,第一步,要懒加载,把这个plist文件中的数据,加载起来, 那么,因…...

linux + jenkins + svn + maven + node 搭建及部署springboot多模块前后端服务

linux搭建jenkins 基础准备 linux配置jdk、maven&#xff0c;配置系统配置文件 vi /etc/profile配置jdk、maven export JAVA_HOME/usr/java/jdk1.8.0_261-amd64 export CLASSPATH.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarexport MAVEN_H…...

VBA之正则表达式(41)-- 快速标记两个星号之后的字符

实例需求&#xff1a;工作表中的数据保存在A列~G列&#xff0c;现需要识别D列中包含超过两个星号的内容&#xff0c;并将第3个星号及其之后的字符设置为红色字体&#xff0c;如图所示。 示例代码如下。 Sub Demo1()Dim objRegExp As ObjectDim objMatch As ObjectDim strMatch…...

VMware16安装MacOS【详细教程】

安装VMware workstation 双击安装包&#xff0c;然后一直下一步就行了。 进行VMware安装&#xff0c;一直 下一步 在输入产品密钥这一步&#xff0c;如果有查找到可用密钥就填进去&#xff0c;没有就跳过&#xff0c;进入软件后也能输入密钥的。 输入密钥。 最后一步&#xff…...

Netty学习(一):Netty概述

一、原生NIO存在的问题 NIO 的类库和API繁杂&#xff0c;使用麻烦:需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。需要具备其他的额外技能:要熟悉Java 多线程编程&#xff0c;因为NIO编程涉及到Reactor 模式&#xff0c;你必须对多线程和网络编程…...

【论文精读】Benchmarking Deep Learning Interpretability in Time Series Predictions

【论文精读】Benchmarking Deep Learning Interpretability in Time Series Predictions Abstract Saliency methods are used extensively to highlight the importance of input features in model predictions. These methods are mostly used in vision and language task…...

自己第一次在虚拟机完整部署ssm项目心得体会

过程使用资源和博文 琳哥发的linux课件文档,阳哥发的linux课件文档(私聊我要) https://www.likecs.com/show-205274015.html https://www.cnblogs.com/aluoluo/articles/15845183.html https://blog.csdn.net/osfipin/article/details/54405445 https://blog.csdn.net/drea…...

操作系统权限提升(二十二)之Linux提权-SUDO滥用提权

系列文章 操作系统权限提升(十八)之Linux提权-内核提权 操作系统权限提升(十九)之Linux提权-SUID提权 操作系统权限提升(二十)之Linux提权-计划任务提权 操作系统权限提升(二十一)之Linux提权-环境变量劫持提权 SUDO滥用提权 SUDO滥用提权原理 sudo是linux系统管理指令&…...

操作系统权限提升(二十四)之Linux提权-明文ROOT密码提权

系列文章 操作系统权限提升(十八)之Linux提权-内核提权 操作系统权限提升(十九)之Linux提权-SUID提权 操作系统权限提升(二十)之Linux提权-计划任务提权 操作系统权限提升(二十一)之Linux提权-环境变量劫持提权 操作系统权限提升(二十二)之Linux提权-SUDO滥用提权 操作系统权限…...

Linux基本命令复习-面试急救版本

1、file 通过探测文件内容判断文件类型&#xff0c;使用权是所有用户&#xff0c; file[options]文件名2、mkdir/rmdir 创建文件目录&#xff08;文件夹&#xff09;/删除文件目录 3、grep 指定文件中搜索的特定内容 4、find 通过文件名搜索文件 find name 文件名 5、ps 查…...

随想录二刷Day09——字符串

文章目录字符串1. 反转字符串2. 反转字符串 II3. 替换空格4. 反转字符串中的单词5. 左旋转字符串字符串 1. 反转字符串 344. 反转字符串 思路&#xff1a; 设置两个指针&#xff0c;分别指向字符串首尾&#xff0c;两指针向中间移动&#xff0c;内容交换。 class Solution { …...

正点原子IMX6ULL开发板-liunx内核移植例程-uboot卡在Starting kernel...问题

环境 虚拟机与Linux版本&#xff1a; VMware 17.0.0 Ubuntu16 NXP提供的U-boot与Linux版本&#xff1a; u-boot:uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2 linux:linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2 开发板&#xff1a; 正点原子-IMX6ULL_EMMC版本&#xff0c;底板版…...

快捷的赣州网站建设/app开发价格表

项目中设计的报表table设计的列数相对过多&#xff0c;当拖动下方的滚动条时无法对应表头所对应的列&#xff0c;因此在网上搜索了好一段日子&#xff0c;最后在网上找到了一些参考资料&#xff0c;然后总结归纳出兼容行列合并的固定表头demo。 多浏览器没有做太多测试&#xf…...

如何做汽车团购网站/怎么查看域名是一级还是二级域名

shell脚本是我们在工作中经常会写的一个东西 一个新手小白或者是不懂linux的初学者可能觉得很难&#xff08;我也是渣渣&#xff09;&#xff0c;编写的过程中总是遇到各种个样的错误 这里举例一个我遇到的问题,在shell中定义变量 #!/bin/bash str “I am string” echo "…...

济南企业网站制作费用/网站排名系统

正文 在angular 2中&#xff0c;回调函数的返回结果&#xff0c;不会自动更新视图层的显示&#xff0c;可以用 ChangeDetectorRef 来驱动angular更新视图。 // 导入 import { Component, OnInit, Input, ChangeDetectorRef } from angular/core; // 注入 constructor(private…...

成都科技网站建设找/网络营销策略存在的问题

时间进入2019年&#xff0c;我们在网页设计中看到旧有风格的重生&#xff0c;也见证了新的设计趋势的诞生。 Web 技术的快速发展&#xff0c;不仅让设计和产品足以满足更多更细致深入的需求&#xff0c;也让设计师能够进行更细腻更独特的表达。现今的网页设计趋势&#xff0c;…...

十大软件公司/googleseo优化

一客户一线程 在一客户一线程&#xff08;thread-per-client&#xff09;的服务器中&#xff0c;为每个连接都创建了一个新的线程来处理。服务器循环执行一些任务&#xff0c;在指定端口上侦听连接&#xff0c;反复接收客户端传入的连接请求&#xff0c;并为每个连接创建一个新…...

一个人可以完成网站建设吗/免费seo公司

那你那转载于:https://www.cnblogs.com/-superman-/p/5714333.html...