C++【string类,模拟实现string类】
🌟个人主页:落叶
🌟当前专栏: C++专栏
目录
为什么学习string类
C语言中的字符串
标准库中的string类
auto和范围for
auto关键字
迭代器
范围for
string类的常用接口说明和使用
1. string类对象的常见构造
2.string类对象的容量操作
3.string类对象的访问及遍历操作
4. string类对象的修改操作
【push_back】在字符串后尾插字符c
【append】在字符串后追加一个字符串
【find + npos(重 点)】从字符串pos位置开始往后找字符c,返回该字符在字符串中的 位置
【rfind】从字符串pos位置开始往前找字符c,返回该字符在字符串中的 位置。
【erase】从字符串中删除字符
5.string类非成员函数
【operator+】尽量少用,因为传值返回,导致深拷贝效率低
【operator>> (重点)】输入运算符重载
【operator<< (重点)】输出运算符重载
【getline (重点)】获取一行字符串
【relational operators (重点)】大小比较
string类的模拟实现
经典的string类问题
浅拷贝
深拷贝
浅拷贝(Shallow Copy)
深拷贝(Deep Copy)
写时拷贝(了解)
为什么学习string类
C语言中的字符串
C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列 的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户 自己管理,稍不留神可能还会越界访问。
标准库中的string类
在使用string类时,必须包含#include头文件以及using namespace std;
auto和范围for
auto关键字
在这里补充2个C++11的小语法,方便我们后面的学习。
- 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个 不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型 指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期 推导而得。
- 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
- 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
- auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
- auto不能直接用来声明数组
auto声明的变量编译器会推导而得类型。
这个typeid是获取变量的真实的类型,
我们可以看到类型是对得上的。

不能没有值
// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项auto e;
auto不能作为函数的参数,可以做返回值,但是建议谨慎使用

auto不能用来声明数组

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

迭代器
https://legacy.cplusplus.com/reference/string/string/begin/
迭代器类似于指针,它指向容器中的一个元素,并且可以通过递增或递减来移动到下一个或前一个元素。迭代器提供了一组操作,如解引用(*),递增(++),递减(--),比较(==, !=)等。
add.begin()是指向第一个元素,
add.end()是指向最后一个元素,
通过i遍历每个字符

在这个例子中,add.begin
()返回一个指向vec第一个元素的迭代器,add.end()返回一个指向add最后一个元素之后位置的迭代器。循环中,i被用来遍历add中的元素,*i用来访问当前元素的值。
迭代器使得算法可以与容器解耦,提高了代码的复用性和灵活性。
范围for
- 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围 内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
- 范围for可以作用到数组和容器对象上进行遍历
- 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。
范围for底层就是迭代器,范围for可以作用到数组和容器对象上进行遍历。

为什么要在auto后面加引用呢?
通过引用遍历可以避免不必要的拷贝构造函数的调用,从而提高性能。

这样做有几个优点:
性能优化:如果容器中的元素很大,或者元素是复杂类型(如类对象),通过引用遍历可以避免不必要的拷贝构造函数的调用,从而提高性能。
修改元素:如果需要在循环中修改容器中的元素,必须通过引用来遍历它们。如果不使用引用,循环变量将是容器中元素的一个副本,对
副本的修改不会影响到原始容器中的元素。
举个例子,auto& 告诉编译器,num 是一个引用 add容器中的实际元素,而不是一个副本。因此,如果对 num 的修改会直接 add中的元素。
string类的常用接口说明和使用
在C++中,std::string 类是标准库的一部分,它提供了一个方便的接口来处理字符串。std::string 位于 <string> 头文件中,并且是 std 命名空间的一部分。
#include<string>
1. string类对象的常见构造
https://legacy.cplusplus.com/reference/string/string/string/
| (constructor)函数名称 | 功能说明 |
| string() (重点) | 构造空的string类对象,即空字符串 |
| string(const char* s) (重点) | 用C-string来构造string类对象 |
| string(size_t n, char c) | string类对象中包含n个字符c |
| string(const string&s) (重点) | 拷贝构造函数 |

#include<iostream>
using namespace std;
#include<string>int main()
{string s1; //构造空的string对象string s2("hello");//用字符串构造string对象string s3(10, 'w');//用10个w构造string对象string s4(s2); //拷贝构造函数return 0;
}
2.string类对象的容量操作
| 函数名称 | 功能说明 |
| size(重点) | 返回字符串有效字符长度 |
| capacity | 返回空间总大小 |
| empty (重点) | 检测字符串释放为空串,是返回true,否则返回false |
| clear (重点) | 清空有效字符 |
| reserve (重点) | 为字符串预留空间** |
| resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
size我们可以看到字符串有效字符长度是5
https://legacy.cplusplus.com/reference/string/string/size/

capacity返回空间总大小
https://legacy.cplusplus.com/reference/string/string/capacity/
明明只有4个字符,空间大小怎么会是15呢?
因为底层有个16大小的数组存放数据,

当我们给满16个字符,会自动扩容到32大小的空间。

empty(重点)检测字符串释放为空串,是返回true,否则返回false
https://legacy.cplusplus.com/reference/string/string/empty/
是空字符串返回true就是1,不是空字符串返回false就是0

clear (重点)清空有效字符
https://legacy.cplusplus.com/reference/string/string/clear/
我们可以看到清空了有效字符,但是空间大小不变。

reserve (重点)为字符串预留空间**
https://legacy.cplusplus.com/reference/string/string/reserve/
reserve 也就是扩容空间的意思。
数据很多的话,我们可以提前扩容空间避免多次2倍扩容。

resize (重点)将有效字符的个数改成n个,多出的空间用\0填充
https://legacy.cplusplus.com/reference/string/string/resize/
字符不变,多出来的空间都用\0填充。

这里是3,abc保留,cdef被清除。

注意: 1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接 口保持一致,一般情况下基本都是用size()。
2. clear()只是将string中有效字符清空,不改变底层空间大小。
3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不 同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数 增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参 数小于string的底层空间总大小时,reserver不会改变容量大小。
3.string类对象的访问及遍历操作
| 函数名称 | 功能说明 |
| operator[] (重 点) | 返回pos位置的字符,const string类对象调用 |
| begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器 |
| rbegin + rend | begin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器 |
| 范围for | C++11支持更简洁的范围for的新遍历方式 |
operator[] (重 点)返回pos位置的字符,const string类对象调用】
https://legacy.cplusplus.com/reference/string/string/operator[]/

rbegin + rend begin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器。
https://legacy.cplusplus.com/reference/string/string/rbegin/
正向迭代器是iterator,反向迭代器是reverse_iterator。
reverse_iterator是标准库提供的一种迭代器适配器,它允许我们以逆序的方式遍历容器。reverse_iterator的设计是为了简化反向遍历容器的过程,使得我们可以像使用普通迭代器一样使用它,但方向相反。

以下是使用 reverse_iterator 的几个主要原因:
1.简化代码:使用
reverse_iterator可以避免手动计算容器的结束位置和开始位置之间的偏移量,从而简化代码。2.一致性:它提供了一种与普通迭代器使用方式一致的方法来反向遍历容器,使得代码更加直观和易于理解。
3.与算法兼容:许多标准库算法都设计为与迭代器一起工作。通过使用
reverse_iterator,我们可以将这些算法应用于容器的反向遍历,而无需修改算法本身。4.安全性:使用
reverse_iterator可以减少由于手动计算迭代器位置而可能引入的错误。
4. string类对象的修改操作
| 函数名称 | |
| push_back | 在字符串后尾插字符c |
| append | 在字符串后追加一个字符串 |
| operator+= (重 点) | 在字符串后追加字符串str |
| c_str(重点) | 返回C格式字符串 |
| find + npos(重 点) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的 位置 |
| rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的 位置 |
| substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
| erase | 从字符串中删除字符 |
【push_back】在字符串后尾插字符c
https://legacy.cplusplus.com/reference/string/string/push_back/

【append】在字符串后追加一个字符串
https://legacy.cplusplus.com/reference/string/string/append/
abcdefg插入了1111的后面

【operator+= (重 点)】 在字符串后追加字符串str
https://legacy.cplusplus.com/reference/string/string/operator+=/
可以+=字符串和字符

也可以+=一个对象

【c_str(重点)】返回C格式字符串
https://legacy.cplusplus.com/reference/string/string/c_str/
可以返回字符串

【find + npos(重 点)】从字符串pos位置开始往后找字符c,返回该字符在字符串中的 位置
https://legacy.cplusplus.com/reference/string/string/find/
在字符串中查找内容
下面我们可以看到,从0下标位置开始查找9这个字符,找到了返回9的下标,没有找到返回-1

没有找到交换返回-1

【rfind】从字符串pos位置开始往前找字符c,返回该字符在字符串中的 位置。
https://legacy.cplusplus.com/reference/string/string/rfind/
我们不写下标,默认从最后一个下标开始往前查找。
找到了返回下标,没有找到返回-1。

【substr 】 在str中从pos位置开始,截取n个字符,然后将其返回
https://legacy.cplusplus.com/reference/string/string/substr/
从3下标开始截取4个字符,给s2。

我们也可以用find查找a这个下标, 然后通过i下标这个位置截取3个字符。

【erase】从字符串中删除字符
https://legacy.cplusplus.com/reference/string/string/erase/
下面我们可以看到,从下标1删除到下标5,就只剩下a和g了。

5.string类非成员函数
| 函数 | 功能说明 |
| operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
| operator>> (重点) | 输入运算符重载 |
| operator<< (重点) | 输出运算符重载 |
| getline (重点) | 获取一行字符串 |
| relational operators (重点) | 大小比较 |
【operator+】尽量少用,因为传值返回,导致深拷贝效率低
https://legacy.cplusplus.com/reference/string/string/operator+/
我们可以看到s1是您好,s2是落叶,加起来就是您好落叶。

我们也可以加字符串

【operator>> (重点)】输入运算符重载
https://legacy.cplusplus.com/reference/string/string/operator%3E%3E/
可以往s1对象,输入字符串,

但是遇到空格就会停止

【operator<< (重点)】输出运算符重载
https://legacy.cplusplus.com/reference/string/string/operator%3C%3C/

【getline (重点)】获取一行字符串
https://legacy.cplusplus.com/reference/string/string/getline/
这个它遇到空格不会停止,会继续往后读取字符串,也会把空格给读取了。

【relational operators (重点)】大小比较
https://legacy.cplusplus.com/reference/string/string/operators/
计算比较大小,如果大于为真,返回1,为假返回0。

string类的模拟实现
经典的string类问题
上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让 学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析 构函数。大家看下以下string类的实现是否有问题?
// 为了和标准库区分,此处使用String
class String
{
public:/*String():_str(new char[1]){*_str = '\0';}*///String(const char* str = "\0") 错误示范//String(const char* str = nullptr) 错误示范String(const char* str = ""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}~String(){if (_str){delete[] _str;_str = nullptr;}}
private:char* _str;
};
// 测试
void TestString()
{String s1("hello bit!!!");String s2(s1);
}

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认 的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内 存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。
浅拷贝
浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致 多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该 资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一 不想分享就你争我夺,玩具损坏。


可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父 母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。

深拷贝
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给 出。一般情况都是按照深拷贝方式提供。

浅拷贝(Shallow Copy)
浅拷贝通常指的是对象之间的简单位拷贝(bitwise copy),这意味着新对象和原对象将共享相同的资源。在C++中,如果你没有显式地定义拷贝构造函数和赋值运算符,编译器会为你生成默认的版本,这些默认版本执行的是浅拷贝。
对于
std::string来说,浅拷贝是不存在的,因为std::string内部管理着自己的动态内存。当你进行拷贝构造或赋值操作时,std::string会执行深拷贝。这意味着新创建的字符串对象拥有自己独立的内存空间,与原字符串对象不共享任何资源。深拷贝(Deep Copy)
深拷贝指的是创建一个新对象,并且递归地复制原对象中的所有元素,使得新对象和原对象完全独立。在
std::string的情况下,每次你进行拷贝构造或赋值操作时,都会执行深拷贝
stringl类模拟实现【代码】
string.h
#define _CRT_SECURE_NO_WARNINGS 1#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace bit
{class string{public://迭代器typedef char* iterator;static const int npos;string(const char* str = "");string(const string& s);~string();///扩容void reserve(size_t n);//尾插字符void push_back(char ch);//尾插字符串void append(const char* str);string& operator+=(char ch);string& operator+=(const char* str);//传统string& operator=(const string& s);//现代string& operator=(string s);//清除字符void clear(){_str[0] = '\0';_size = 0;}//首字符iterator begin(){return _str;}//最后的iterator end(){return _str + _size;}//返回_strchar* c_str() const{return _str;}//size_t size()const{return _size;}size_t capacity()const{return _capacity;}//指定位置修改字符char& operator[](size_t index){assert(index < _size);return _str[index];}//指定位置修改字符const char& operator[](size_t index)const{assert(index < _size);return _str[index];}////查询字符size_t find(char ch, size_t pos = 0);//查询字符串size_t find(const char* str, size_t pos = 0);//指定位置插入字符string& insert(size_t pos, char c);//指定位置插入字符串string& insert(size_t pos, const char* str);// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len);//调整字符串大小void resize(size_t n, char c = '\0');//判断字符串是不是空bool empty()const;//字符串交换void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}void swap(string& s, string& s1){s.swap(s1);}//流插入friend ostream& operator<<(ostream& _cout, const bit::string& s);//流提取friend istream& operator>>(istream& _cin, bit::string& s);private:char* _str;size_t _size;size_t _capacity;};//判断大于-小于-大于等于 - 小于等于 - 等于 - 不等于bool operator<(const string& lhs, const string& rhs);bool operator>(const string& lhs, const string& rhs);bool operator<=(const string& lhs, const string& rhs);bool operator>=(const string& lhs, const string& rhs);bool operator==(const string& lhs, const string& rhs);bool operator!=(const string& lhs, const string& rhs);
}
string.cpp
#include"string.h"namespace bit
{const int string::npos = -1;//构造string::string(const char* str):_size(strlen(str)){_capacity = _size;_str = new char[_size + 1];strcpy(_str, str);}深拷贝-(传统写法)//string::string(const string& s)//{// _str = new char[s._capacity + 1];// strcpy(_str, s._str);// _size = s._size;// _capacity = s._capacity;//}//深拷贝-(现代写法)string::string(const string& s){string tmp(s._str);swap(tmp);}//析构string::~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}/////扩容void string::reserve(size_t n){cout << "reserve:"<< n << endl;if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}//尾插字符void string::push_back(char ch){if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;}//尾插字符串void string::append(const char* str){size_t len = strlen(str);if ((_size + len) > _capacity){size_t tmp = _capacity * 2;if (tmp < (_size + len)){tmp = (_size + len);}reserve(tmp);}strcpy(_str + _size, str);_size += len;}string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}赋值-(传统写法)//string& string::operator=(const string& s)//{// if (this != &s)// {// delete[] _str;// _str = new char[s._capacity + 1];// strcpy(_str, s._str);// _size = s._size;// _capacity = s._capacity;// }// return *this;//}//赋值-(现代写法) string& string::operator=(string s){swap(s);return *this;}//查询字符size_t string::find(char ch, size_t pos){assert(pos < _size);for (size_t i = pos; i < _size; i++){if (ch == _str[i]){return i;}}return npos;}//查询字符串size_t string::find(const char* str, size_t pos){assert(pos < _size);const char* tmp = strstr(_str + pos, str);if (tmp == __nullptr){return npos;}return tmp - _str;}//在字符串里插入字符string& string::insert(size_t pos, char ch){assert(pos < _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//循环移动字符int i = _size;while ((int)pos <= i){_str[i + 1] = _str[i];i--;}//在pos位置插入字符_str[pos] = ch;_size++;return *this;}//指定位置插入字符串string& string::insert(size_t pos, const char* str){size_t len = strlen(str);if ((_size + len) > _capacity){size_t tmp = _capacity * 2;if (tmp < (_size + len)){tmp = (_size + len);}reserve(tmp);}//循环往后移动字符size_t i = _size + len;while (pos < i){_str[i] = _str[i - len];i--;}//循环把str的字符一个一个,赋值给_str[pos+i]的位置for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size + len;return *this;}//指定位置删除字符串string& string::erase(size_t pos, size_t len){if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{size_t i = pos + len;while (i <= _size){_str[i - len] = _str[i];i++;}_size = _size - len;}return *this;}//判断大于-小于-大于等于 - 小于等于 - 等于 - 不等于bool operator<(const string& lhs, const string& rhs){return strcmp(lhs.c_str(), rhs.c_str()) < 0;}bool operator>(const string& lhs, const string& rhs){return !(lhs <= rhs);}bool operator<=(const string& lhs, const string& rhs){return (lhs < rhs) || (lhs == rhs);}bool operator>=(const string& lhs, const string& rhs){return !(lhs < rhs);}bool operator==(const string& lhs, const string& rhs){return strcmp(lhs.c_str(), rhs.c_str()) == 0;}bool operator!=(const string& lhs, const string& rhs){return !(lhs == rhs);}//调整字符串大小void string::resize(size_t n, char c){_str[n] = c;}//删除pos位置上的元素,并返回该元素的下一个位置bool string::empty()const{if (_size != 0){return false;}else{return true;}}//流插入ostream& operator<<(ostream& _cout, const bit::string& s){for (size_t i = 0; i < s._size; i++){_cout << s._str[i];}return _cout;}//流提取istream& operator>>(istream& _cin, bit::string& str){str.clear();int i = 0;char buff[256];char ch;ch = _cin.get();while (ch != ' ' && ch != '\n'){buff[i++] += ch;if (i == 255){buff[i] = '\0';str += buff;i = 0;}ch = _cin.get();}if (i > 0){buff[i] = '\0';str += buff;}return _cin;}}
test.cpp【测试】
#include"string.h"//int main()
//{
// bit::string n("asdasdf");
// n += 'w';
// cout << n.c_str() << endl;
//}//int main()
//{
// bit::string n("asfsddfgdf");
// //迭代器
// bit::string::iterator i = n.begin();
// while (i != n.end())
// {
// cout << *i << endl;
// i++;
// }
//}//int main()
//{
// bit::string n("qweeqqqw");
// //n.clear();
// //cout << n.c_str() << endl;
// n.insert(0, "11111111");
// cout << n.c_str() << endl;
//}//int main()
//{
// bit::string n("11223344556677");
// /*n.erase(4, 99);*/
// n[2] = 'x';
// cout << n.c_str() << endl;
//}//int main()
//{
// bit::string n("11223344556677");
// bit::string s1(n);
// cout << (n < s1) << endl;
// cout << (n > s1) << endl;
// cout << (n <= s1) << endl;
// cout << (n >= s1) << endl;
// cout << (n == s1) << endl;
// cout << (n != s1) << endl;
//
//}//int main()
//{
// bit::string n("11223344556677");
// /*n.resize(8,'y');
// n.resize(8);
// cout << n.c_str() << endl;*/
//
// cout << n.empty() << endl;
//}//int main()
//{
// string n("1122");
// string s1("qqwee");
// n.swap(s1);
// cout << n << endl;
// cout << s1 << endl;
//}int main()
{bit::string n("112233");bit::string n1 = n;//bit::string s1("qweqwer");//n.swap(s1);//cout << n << endl;//cout << s1 << endl;/*cin >> n;cout << n << endl;*///getline(cin, n);//n = n1;//swap(n, n1);//swap(n,n1);//cout << n << endl;cout << n1 << endl;
}
写时拷贝(了解)

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。
引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该 资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源, 如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有 其他对象在使用该资源。
写时拷贝
写时拷贝在读取是的缺陷
相关文章:
C++【string类,模拟实现string类】
🌟个人主页:落叶 🌟当前专栏: C专栏 目录 为什么学习string类 C语言中的字符串 标准库中的string类 auto和范围for auto关键字 迭代器 范围for string类的常用接口说明和使用 1. string类对象的常见构造 2.string类对象的容量操作 3…...
Jupyter lab 打开时默认使用 Notebook 而不是浏览器
Jupyter lab 打开时默认使用 Notebook 而不是浏览器 正文 正文 今天遇到了一个特别有意思的事情,这里我们以 Windows \textrm{Windows} Windows 系统举例。 我们知道通常我们需要使用如下代码在 Terminal \textrm{Terminal} Terminal 中打开 Jupyter lab \textr…...
【linux】ubunda repo是什么
Ubuntu repo(repository,简称repo)是一个软件仓库,它是存储和分发软件包的服务器或一组服务器。通俗地说,Ubuntu repo就像一个巨大的在线软件商店,用户可以从中下载和安装各种软件。 主要特点 软件集合&a…...
【MySQL】深层理解索引及特性(重点)--下(12)
索引(重点) 1. 索引的作用2. 索引操作2.1 主键索引2.1.1 主键索引的特点2.1.2 创建主键索引 2.2 唯一键索引2.2.1 唯一键索引的特点2.2.2 唯一索引的创建 2.3 普通索引2.3.1 普通索引的特点2.3.2 普通索引的创建 2.4 全文索引2.4.1 全文索引的作用2.4.2 …...
无人机声学侦测算法详解!
一、算法原理 无人机在飞行过程中,其电机工作、旋翼震动以及气流扰动等都会产生一定程度的噪声。这些噪声具有独特的声学特征,如频率范围、时域和频域特性等,可以用于无人机的检测与识别。声学侦测算法利用这些特征,通过一系列步…...
git 提交仓库
创建 git 仓库: mkdir pySoundImage cd pySoundImage git init touch README.md git add README.md git commit -m “first commit” git remote add origin https://gitee.com/hunan-co-changsha-branch/pytest.git git push -u origin master 已有仓库ÿ…...
基于大语言模型(LLM)自主Agent 智能体综述
近年来,LLM(Large Language Model)取得了显著成功,并显示出了达到人类智能的巨大潜力。基于这种能力,使用LLM作为中央控制器来构建自助Agent,以获得类人决策能力。 Autonomous agents 又被称为智能体、Agent。指能够通过感知周围环境、进行规划以及执行动作来完成既定任务。…...
使用命令行管理 Windows 环境变量
1. 使用命令提示符 (CMD) 1.1. 设置环境变量 添加或修改临时环境变量(当前会话有效) set MY_VARvalue添加或修改用户环境变量 setx MY_VAR "value"添加或修改系统环境变量(需要管理员权限): setx /M MY…...
AUTODL配置百度网盘数据传输
AUTODL使用 1.配置百度网盘开放平台 2.接入并创建应用 3.创建应用 4.添加授权...
LeetCode46. 全排列(2024秋季每日一题 57)
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1: 输入:nums [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例 2: 输入:nums …...
SpringBoot新闻稿件管理系统:架构与实现
3系统分析 3.1可行性分析 通过对本新闻稿件管理系统实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本新闻稿件管理系统采用SSM框架,JAVA作为开发语…...
MinIO讲解和java应用案例示范
一、MinIO 基本概念 1.1 什么是 MinIO? MinIO 是一个高性能的对象存储服务器,专为云原生应用设计。它支持 Amazon S3 API,因此可以与现有的 S3 客户端和工具集成。MinIO 主要用于存储非结构化数据,如图片、视频、备份文件和日志…...
区块链技术与应用 【全国职业院校技能大赛国赛题目解析】第1套 区块链系统设计与运维部分
模块一:区块链产品方案设计及系统运维(35分) 选手完成本模块的任务后,将任务中设计结果、运行代码、运行结果等截图粘贴至客户端桌面【区块链技术应用赛\重命名为工位号\模块一提交结果.docx】中对应的任务序号下。 前述: 我们收到答案后,将针对比赛的答案和样题进行解…...
yaml文件编写
Kubernetes 支持YAML和JSON格式管理资源 JSON 格式:主要用于 api 接口之间消息的传递 YAML 格式;用于配置和管理,YAML是一种简洁的非标记性语言,内容格式人性化容易读懂 一,yaml语法格式 1.1 基本语法规则 使用空格进行缩进(不使用制表符࿰…...
TOEIC 词汇专题:娱乐休闲篇
TOEIC 词汇专题:娱乐休闲篇 在娱乐和休闲活动中,我们会接触到许多特定的词汇。这些词汇涉及到活动入场、观众互动、评论等各个方面,帮助你在相关场景中更加自如。 1. 入场和观众 一些常用词汇帮助你轻松应对观众与入场管理相关的场景&#…...
驱动TFT-1.44寸屏(ST7735)显示器
目录 一、驱动芯片介绍 二、驱动方式 三、主函数main运行 四、完整代码下载 TFT1.44寸屏,搭配ST7735驱动芯片,是一种专为小型电子设备设计的彩色液晶显示解决方案。该屏幕采用薄膜晶体管(TFT)技术,能够实现高亮度、…...
鸿蒙HarmonyOS NEXT一多适配技术方案
鸿蒙一多是什么 HarmonyOS 系统面向多终端提供了“一次开发,多端部署”(后文中简称为“一多”)的能力,让开发者可以基于一种设计,高效构建多端可运行的应用。 一套代码工程,一次开发上架,多端按…...
golang 中map使用的一些坑
golang 中map使用的一些坑 1、使用map[string]interface{},类型断言[]int失败 接收下游的数据是用json转为map[string]any go a : "{\"a\":\"1\",\"b\":[123]}" var marshal map[string]any json.Unmarshal([]byte(a), &…...
cordova 离线打包Android -Linux
背景 已有 cordova 运行环境的docker镜像; 需要在离线环境下执行 cordova 从创建项目到构建安装包一系列命令,最终生成 apk 文件。 方案 先在有网环境(最好与离线环境的OS一致)走一遍 cordova 创建打包工程、添加插件、添加平…...
【python】OpenCV—findContours(4.3)
文章目录 1、功能描述2、代码实现3、完整代码4、结果展示5、涉及到的库函数5.1、cv2.Canny5.2 cv2.boxPoints 6、参考 1、功能描述 找出图片中的轮廓,拟合轮廓外接椭圆和外接矩阵 2、代码实现 导入必要的库,固定好随机种子 import cv2 as cv import …...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

