STL序列式容器之string的基本用法及实现
1.string类
在使用string类时,必须包含<string>头文件以及using namespace std;
接下来我们看看string
类是如何被声明的:
typedef basic_string<char> string;
可以看到:string类是被类模板basic_string用数据类型char实例化后得到的一个具体的类的别名。
同时规定:
1.string是表示字符串的字符串类;
2.该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作;
3.string在底层实际是:basic_string模板类的别名,typedef basic_string string;
4.不能操作多字节或者变长字符的序列。
1.1 string类的成员函数及其基本用法
1.1.1 默认成员函数
默认成员函数包括,constructor构造函数、destructor ~string析构函数、赋值运算符重载operator=。
1.1.2 constructor--构造函数
string类的构造函数string()被重载了许多形式,我们只需要重点掌握以下三种即可:
string();//构造一个空字符串
string(const string& str);//拷贝构造
string(const char* s);//用一个常量字符串来构造一个string类对象
具体用法:
#include<iostream>
#include<string>
using namespace std;//string类的成员函数及其基本用法
//1、构造函数
void constructor_test()
{string s1;//构造一个空字符串string s2("hello world");//使用一个常量字符串来构造出一个string类对象string s3(s2);//拷贝构造//在string类中已经重载了流插入<< 和流提取>> 运算符,故可以直接使用<<来输出string字符串对象cout << "字符串s1:" << s1 << endl;cout << "字符串s2:" << s2 << endl;cout << "字符串s3:" << s3 << endl;
}int main()
{constructor_test();return 0;
}
output:
字符串s1:
字符串s2:hello world
字符串s3:hello world
1.1.3 operator= ----赋值运算符重载
string& operator=(const string& str);//类类型之间的赋值
string& operator=(const char* s);//利用隐式类型转换,先将字符串s实例化为一个string类型对象,再进行赋值
string& operator=(char c);//和第二种方法类似
具体用法:
//operator= 赋值运算符重载
void test()
{string s1, s2, s3;s1 = "hello world";s2 = 'c';s3 = s1;cout << "s1字符串为:" << s1 << endl;cout << "s2字符串为:" << s2 << endl;cout << "s3字符串为:" << s3 << endl;
}int main()
{test();return 0;
}
output:
s1字符串为:hello world
s2字符串为:c
s3字符串为:hello world
1.1.4 string类对象的容量操作
1.1.4.1 size/length----返回字符串有效字符长度
size_t size() const;
size_t length() const;
这两个函数的效果是一样的,都是返回字符串有效字符的长度,但是更建议使用函数size()。字符串的有效长度是不包括结束字符'\0'的,其结构就和C语言的strlen()函数一样。
size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本上都是用size()。
//3、string类对象的容量操作
//(1)size--返回字符串有效字符的长度
void test_2()
{string s1;string s2("hello world");cout << "size of s1:" << s1.size() << endl;cout << "size of s2:" << s2.size() << endl;
}int main()
{test_2();return 0;
}
output:
size of s1:0
size of s2:11
1.1.4.2 capacity----返回空间总大小
size_t capacity() const;
一般来说,这个最大容量指的是可以存放有效字符的最大容量,也不包括结束符'\0'。需要注意的是,由于不同平台所用的库不同,因此用同一个字符串构造string对象时,分配给其用来存储字符的初始空间也不一定相同(即capacity不一定相同),例如:
//(2)capacity--获取最大容量
void test_3()
{string s1;string s2("hello world hello everyone");cout << "capacity of s1:" << s1.capacity() << endl;cout << "capacity of s2:" << s2.capacity() << endl;
}int main()
{test_3();return 0;
}
在VS下,output:
capacity of s1:15
capacity of s2:31
在Linux下,output:
capacity of s1 is: 0
capacity of s2 is: 16
1.1.4.3 clear/resize
void clear();//清空有效字符
void resize (size_t n);
void resize (size_t n, char c);//将有效字符的个数改成n个,多出的空间用字符c填充
clear()只是将string中有效字符清空,不改变底层空间的大小。
resize(size_t n)与resize(size_t n,char c)都是字符串中有效字符个数改变到n个,不同的是当字符个数增多时,resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
//(4)clear--清空有效字符,
// resize--将有效字符的个数该成n个,多出的空间用字符c填充
void test_4()
{string s("hello world!");cout << "size of s:" << s.size() << endl;cout <<"capacity of s:" << s.capacity() << endl;cout << s << endl;cout << "============" << endl;//将s中的字符串清空,注意清空时只是将size清零,不改变底层空间的大小s.clear();cout << "size of s:" << s.size() << endl;cout << "capacity of s:" << s.capacity() << endl;cout << "============" << endl;//将s中的有效字符个数增加到10个,多出的位置用字符'a'进行填充//"aaaaaaaaaa"s.resize(10, 'a');cout << "size of s:" << s.size() << endl;cout << "capacity of s:" << s.capacity() << endl;cout << s << endl;cout << "============" << endl;//将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充//"aaaaaaaaaa\0\0\0\0\0"//此时s中有效字符个数以及增加到了15个s.resize(15);cout << "size of s:" << s.size() << endl;cout << "capacity of s:" << s.capacity() << endl;cout << s << endl;cout << "============" << endl;//将s中的有效字符个数缩小到5个s.resize(5);cout << "size of s:" << s.size() << endl;cout << "capacity of s:" << s.capacity() << endl;cout << s << endl;
}int main()
{test_4();return 0;
}
output:
size of s:12
capacity of s:15
hello world!
============
size of s:0
capacity of s:15
============
size of s:10
capacity of s:15
aaaaaaaaaa
============
size of s:15
capacity of s:15
aaaaaaaaaa
============
size of s:5
capacity of s:15
aaaaa
1.1.4.4 reserve 为字符串预留空间
void reserve (size_t n = 0);
reserve成员函数会将string的最大容量capacity扩展为n,reserve()只会对capacity做出改变,而不会影响原来的数据(既不会删除也不会创建)。当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小。
需要注意,由于不同平台依赖的库不同,所以reserve最终的效果也会不同,但是无论如何,reserve()绝不会影响到存储的数据。
//4、reserve--为字符串预留空间
void test_5()
{string s1("hello world!");string s2 = s1;cout << "the capacity of s1:" << s1.capacity() << endl;s1.reserve(100);cout << "after reserve(100),the capacity of s1:" << s1.capacity() << endl;s1.reserve(50);cout << "after reserve(50),the capacity of s1:" << s1.capacity() << endl;cout << "the capacity of s2:" << s2.capacity() << endl;s2.reserve(1);cout << "after reserve(1),the capacity of s2:" << s2.capacity() << endl;
}int main()
{test_5();return 0;
}
VS:output
the capacity of s1:15
after reserve(100),the capacity of s1:111
after reserve(50),the capacity of s1:111
the capacity of s2:15
after reserve(1),the capacity of s2:15
Linux:output
the capacity of s1:11
after reserve(100), the capacity of s1:100
after reserve(50), the capacity of s1:50the capacity of s2:11
after reserve(1), the capacity of s2:11
可以总结出两者之间的不同:VS在分配空间时,总会比给定的值多几个空间;而Linux则是给多少开多少空间。在VS下reserve()函数不能缩小空间,只能扩大空间;而Linux操作系统下,reserve()函数可以缩小空间,同时他们有一个共同点:无论给定值再怎么小,reserve()函数都不会影响到原来的数据。
1.1.5 string类对象的访问及遍历操作
1.1.5.1 operator[]/at()--获取指定位置的字符
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;char& at (size_t pos);
const char& at (size_t pos) const;
相同点:和普通字符数组一样,string类类型的对象也可以通过类似[下标]的方式获得指定位置的字符,这个函数被重载成了两份,分别给非const对象和const对象使用。
不同点:如果传入的pos大于size(),那么对于operator[],则会直接报错,而对于at(),则会抛出异常。有了这两个成员函数,我们就可以对string对象存储的数据进行遍历访问了。
//5、operator[] / at()
void test_6()
{string s("hello world");cout << s << endl;for (int i = 0; i < s.size(); ++i)cout << s[i] << ' ';cout << endl;for (int i = 0; i < s.size(); ++i)cout << s.at(i) << ' ';cout << endl;
}int main()
{test_6();return 0;
}
output:
hello world
h e l l o w o r l d
h e l l o w o r l d
1.1.5.2 iterator——迭代器
迭代器是一个用来访问容器数据的对象,其提供了统一的方式来遍历容器中的数据,对于string类,我们可以将迭代器看成一个指针,其指向string对象存储的某个字符。我们可以通过迭代器来访问或者修改容器中的数据。尽管前面的[]运算符可以访问和修改string存储的数据,但是在STL中,对于访问和遍历数据迭代器才是最常用的。
以下是几种获取string类型迭代器的常见方式:
1.1.5.2.1 begin()/end()
iterator begin();
const_iterator begin() const;
###############################
iterator end();
const_iterator end() const;
begin()即返回一个指向字符序列第一个字符的迭代器;end()返回一个指向字符序列最后一个字符(即'\0')的迭代器。begin() const 和 end() const 是针对const对象做出的函数重载。
注意:
由于string类实际上就是存储字符序列的类,因此针对它的迭代器iterator,我们可以将其看成为一个指向char类型的指针char*,而const_iterator对应的是const char*。
但是为什么const_iterator不写成const iterator呢?对于const对象,其返回的迭代器应该具有允许访问(遍历)数据,但不允许修改数据的功能。而const iterator本质上修饰的是迭代器iterator本身,我们可以看作为char* const,这样的效果是不能改变迭代器的指向,但是可以改变迭代器指向数据的内容,着显然是不符合要求的。但是const_iterator本质上修饰的就是迭代器指向的数据,可以看作是const char*,符合要求。
//6、迭代器iterator--begin()/end()
void test_7()
{string s("hello world!");string::iterator it = s.begin();while (it != s.end()){(*it)++;cout << *it << " ";it++;}
}int main()
{test_7();return 0;
}
output:
i f m m p ! x p s m e "
1.1.5.2.2 rbegin()/rend()
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
######################################
reverse_iterator rend();
const_reverse_iterator rend() const;
和begin()/end()类似,只是返回的是反向迭代器,所谓反向迭代器即rbegin()返回一个指向字符序列最后一个有效字符的迭代器;rend()返回一个指向字符序列字符序列第一个字符之前的字符(被认为是反向末端)的迭代器。如果反向迭代器的指向可以修改,那么例如对于rbegin()的返回结果进行+1操作,就会使迭代器指向倒数第二个字符。
例如:
//7、rbegin()/end()
void test_8()
{string s("hello world!");string::reverse_iterator rit = s.rbegin();while (rit != s.rend()){cout << *rit << " ";++rit;}cout << endl;
}int main()
{test_8();return 0;
}
output:
! d l r o w o l l e h
1.1.6 string类对象的修改操作
1.1.6.1 增--插入(拼接):push_back/append/operator+=
void push_back (char c);
########################
string& append (const string& str);
string& append (const string& str, size_t subpos, size_t sublen);
string& append (const char* s);
string& append (const char* s, size_t n);
string& append (size_t n, char c);
##################################################################
string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
例如:
//8、插入(拼接)方式:push_back append operator+=
void test_9()
{string str;str.push_back(' ');//在str后插入空格str.append("hello");//在str后追加一个字符"hello"str += 'w';//在str后追加一个字符'b'str += "orld";//在str后追加一个字符串"it"cout << str << endl;
}int main()
{test_9();return 0;
}
output:
helloworld
1.1.6.2 删 erase
string& erase (size_t pos = 0, size_t len = npos);
即删除从pos位置开始的len个字符。
注意:npos为string里面定义的一个const静态全局变量,const static size_t npos=-1;
1、无符号整形npos的值为-1,因此它的实际值为unsigned int的最大值;
2、如果npos用来表示一个长度,那么它通常用来说明直到字符串的尾;
3、如果npos用来表示一个返回值,那么它通常用来说明没有找到目标。
例如:
//9、erase
void test_10()
{string str("hello world");str.erase(2, 2);cout << str << endl;str.erase(1);cout << str << endl;
}
output:
heo world
h
1.1.6.3 查find/rfind
size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
size_t find (char c, size_t pos = 0) const;size_t rfind (const string& str, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos = npos) const;
size_t rfind (char c, size_t pos = npos) const;
find:从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置。
rfind:从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置。
例如:
//10、正向和反向查找:find/rfind
void test_11()
{//获取file的后缀string file("string.cpp");size_t pos = file.rfind('.');string suffix(file.substr(pos, file.size() - pos));cout << suffix << endl;//取出url中的域名string url("https://legacy.cplusplus.com/reference/string/string/find/");cout << url << endl;size_t start = url.find("://");if (start == string::npos){cout << "invalid url" << endl;return;}start += 3;size_t finish = url.find('/', start);string address = url.substr(start, finish - start);cout << address << endl;//删除url的协议前缀pos = url.find("://");url.erase(0, pos + 3);cout << url << endl;
}int main()
{test_11();return 0;
}
output:
.cpp
https://legacy.cplusplus.com/reference/string/string/find/
legacy.cplusplus.com
legacy.cplusplus.com/reference/string/string/find/
1.1.6.4 改
string类中有专门用于修改字符串的函数replace,但是由于效率的原因并不常用。实际中,一般都是使用[]下标访问和迭代器访问来修改数据。
1.1.6.5 c_str--获得C语言类型的字符串
const char* c_str() const;
例如:
//11、c_str
void test_12()
{string s("hello world");const char* str = s.c_str();cout << str << endl;
}
output:
hello world
1.1.6.4 substr--获得子串
string substr (size_t pos = 0, size_t len = npos) const;
在str中从pos位置开始,截取len个字符,同时将这个子串存储到string类中并进行返回。
例如:
//12、substr
void test_13()
{string s1("hello world");string s2 = s1.substr(0, 5);//hellostring s3 = s1.substr(5);// worldcout << s2 << s3 << endl;
}int main()
{test_13();return 0;
}
output:
hello world
1.2 非成员函数
1.2.1 operator<< / operator>> --流插入/流提取运算符重载
1、有了<<流插入运算符重载,我们就可以利用std::cout向屏幕打印string存储的字符序列;
2、有了>>流提取运算符重载,我们就可以利用std::cin向string类中输入数据。
注意:1、 cin类似于C语言中的scanf,如果碰到空白字符就会停止读取,因此cin只能用于读取不带空格的字符序列。2、原来的数据会被输入端新字符给覆盖。3、如果输入的字符长度大于capacity,那么就会对这个string对象进行扩容,直到可以存储输入的字符序列。
例如:
//13、operator<</operator>>
void test_14()
{string s1, s2;cin >> s1;cin >> s2;cout << "s1 = " << s1 << endl;cout << "s2 = " << s2 << endl;
}
intput:
hello
world hello
output:
s1 = hello
s2 = world
1.2.2 getline--输入字符串
istream& getline (istream& is, string& str);
1、同样是从标准输入流向string对象输入数据;
2、原来的数据会被输入端新字符给覆盖;
3、getline()类似于C语言中的gets(),只有遇到换行才会停止读取。因此可以读取带空格的字符序列;
4、如果输入的字符长度大于capacity,那么就会对这个string对象进行扩容,直到可以存储输入的字符序列。
例如:
//14、getline()
void test_15()
{string s1;getline(cin, s1);cout << s1 << endl;
}
input:
hello world
output:
hello world
1.2.3 relational operators--关系运算符重载
(1)
bool operator== (const string& lhs, const string& rhs);
bool operator== (const char* lhs, const string& rhs);
bool operator== (const string& lhs, const char* rhs);(2)
bool operator!= (const string& lhs, const string& rhs);
bool operator!= (const char* lhs, const string& rhs);
bool operator!= (const string& lhs, const char* rhs);(3)
bool operator< (const string& lhs, const string& rhs);
bool operator< (const char* lhs, const string& rhs);
bool operator< (const string& lhs, const char* rhs);(4)
bool operator<= (const string& lhs, const string& rhs);
bool operator<= (const char* lhs, const string& rhs);
bool operator<= (const string& lhs, const char* rhs);(5)
bool operator> (const string& lhs, const string& rhs);
bool operator> (const char* lhs, const string& rhs);
bool operator> (const string& lhs, const char* rhs);(6)
bool operator>= (const string& lhs, const string& rhs);
bool operator>= (const char* lhs, const string& rhs);
bool operator>= (const string& lhs, const char* rhs);
C++string类关系运算符的逻辑与C语言字符串比较函数strcmp的逻辑类似。
string类的基本使用完整代码可参考:string类的基本使用
2.string类的简单模拟实现--MyString类
模拟实现的string类有4个成员变量:_str指向字符串的指针、_size字符串的大小、_capacity字符串容量、静态成员npos。
char* _str;
size_t _size;
size_t _capacity;
static const size_t npos = -1;
各个接口的实现:
2.1 构造函数
分配空间时,要多分配1个字节的空间用于存放'\0',有参构造和无参构造可以合并成半缺省。
//1、构造函数
MyString(const char*str=""):_size(strlen(str))//先_size后_capacity是为了匹配声明顺序,防止给成随机值
{_capacity = _size == 0 ? 2 : _size;_str = new char[_capacity + 1];//这里多开一个空间给结束符'\0'留空间strcpy(_str, str);
}
2.2 拷贝构造
2.2.1 拷贝构造的传统写法
//2、拷贝构造--string s2(s1)
//1)传统写法
MyString(const MyString& str):_str(new char[strlen(str._str) + 1]), _size(str._size), _capacity(str._capacity)
{strcpy(_str, str._str);
}
2.2.2 拷贝构造的常用写法
(1)先使用str._str作为参数构造一个临时对象;
(2)将临时对象tmp的内容和*this进行交换。
注意:必须要在初始化列表中将_str指针初始化为空指针,因为拷贝构造函数中创建的临时对象tmp出了拷贝构造函数的作用域时,会调用析构函数释放tmp在堆上申请的空间。如果_str没有被初始化为空指针,那么_str就是随机值,当tmp和_str进行交换操作后,tmp对象的成员_str也是随机值,随机值的空间是不能释放的,会导致不可预知的错误;空指针可以被释放,因此_str必须被初始化为空指针。
//2)拷贝构造现在常用写法
MyString(const MyString& str):_str(nullptr),_size(0),_capacity(0)
{MyString tmp(str._str);//使用构造函数创建一个临时对象swap(tmp);
}
2.3 swap
使用库里的swap函数交换*this和s的内容:包括_str字符串内容、_size字符串大小和_capacity字符串容量。
void swap(MyString& s)
{//用::指定调用全局的swap函数即库里的swap函数::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);
}
2.4 赋值运算符重载
2.4.1 写法1
//3、赋值运算符重载 s1=s2
//1)传统写法
MyString& operator=(const MyString& str)
{if (this != &str){char* tmp = new char[strlen(str._str) + 1];strcpy(tmp, str._str);delete[] _str;_str = tmp;_capacity = str._capacity;_size = str._size;}return *this;
}
2.4.2 写法2
使用swap()函数将*this的内容和s进行交换
//2)赋值的现代常用写法s1=s2
MyString operator=(MyString s)
{swap(s);return *this;
}
2.5 析构函数
释放_str在对上申请的空间、将_size和_capacity置0
~MyString()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
2.6 返回字符串的_size和_capacity
//size()
size_t size() const
{return _size;
}size_t capacity() const
{return _capacity;
}
2.7 c_str()
获取c形式的字符串,将const string*类型转化为const char*类型。
//对象以字符串的形式返回
const char* c_str()
{return _str;
}
2.8 operator[]
分为普通operator[](可读可写)和const operator[](只读);如果要对_str中的字符进行修改的话,要返回引用;如果返回的是char,那么return就是传值返回,返回的是_str[i]的拷贝,即临时对象,而临时对象具有常性,不能被修改。
char& operator[](size_t i)
{assert(i < _size);return _str[i];
}//operator[]
const char& operator[](size_t i) const
{assert(i < _size);return _str[i];
}
2.9 push_back--字符串尾插
字符串尾插要考虑是否需要开辟空间。
//在字符串尾部插入一个字符
void push_back(const char ch)
{//首先判断字符串空间是否已满,如果满了则需要扩容if (_size == _capacity){size_t newCapacity = _capacity==0 ? 2 : 2 * _capacity;reserve(newCapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';
}
2.10 append--尾插一个字符串
将字符串str中的内容拷贝到_str+_size(_str的末尾)位置 。
//尾插一个字符串
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(len);}strcpy(_str + _size, str);_size += len;
}
2.11 operator+=()
分为两种情况:
(1)+=1个字符串,使用push_back插入;
(2)+=字符串,使用append追加到字符串末尾。
//s1+='a'
MyString& operator+=(const char ch)
{push_back(ch);return *this;
}//s1+="abcd"
MyString& operator+=(const char* s)
{append(s);return *this;
}
2.12 insert
在指定位置插入字符:(1)判断是否需要增容;(2)将pos位置至字符串末尾的字符都向后挪动一个位置;(3)将要插入的字符插入到pos位;(4)跟新_size。
//在指定位置插入一个字符,并返回新字符串
MyString& insert(size_t pos, const char ch)
{assert(pos <= _size);if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 2 : 2 * _capacity;reserve(newCapacity);}//以下写法是错误的,为什么呢?/*size_t end = _size;while (end >= pos){_str[end + 1] = _str[end];--end;}*///为什么不能这样写呢?//当进行头插时,pos==0,此时end==0,执行到代码最后--end为-1,//由于end为无符号整型,故为-1时,即为size_t类型的最大值,此时会进入死循环。//那能不能把pos换成int类型?也不能,//pos为有符号int类型,end为无符号整型,两者进行比较时,pos会被提升为无符号整型,也进入死循环。size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;return *this;
}//在指定位置插入一个字符串
MyString& 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 (pos+len-1 < end){_str[end] = _str[end-len];--end;}strncpy(_str + pos, str,len);_size += len;return *this;
}
2.13 erase(size_t pos,size_t len=npos)
删除字符,len为要删除的字符的个数,分为两种情况:
(1)从pos到字符串末尾的字符个数小于len(即要删除的字符个数小于len),此时说明字符串从pos往后的长度不够删,此时pos之后的内容全部都被删完了,直接将pos位置赋值为'\0',_size更新为pos即可;
(2)从pos到字符串末尾的字符个数大于或者等于要删除的字符个数,此时剩余的字符需要向前挪动len位。
//erase()删除指定位置往后len个字符,并返回新字符串
MyString& erase(size_t pos, size_t len = npos)
{assert(pos < _size);if (len == npos || pos + len > _size){_str[pos] = '\0';_size = pos;}else{//写法1:/*size_t i = pos + len;while (i<=_size){_str[i-len] = _str[i];++i;}_size -= len;*///写法2:strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}
2.14 reserve
开空间,扩展_capacity,_size不变
(1)申请新空间;(2)拷贝字符串;(3)释放旧空间;(4)指向新空间;(5)更新容量。
//将字符串容量扩展到n
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}
2.15 resize(size_t n,char ch='\0'),
开空间+初始化,扩展_capacity,_size也要修改。
(1)当n<_size,无需扩容,此时_size调整为n;
(2)n>_size时,分两种情况:
①当_size<n<_capacity,无需增容,直接将_size到n位置的数据置为ch;
②当n>_capacity时,需要增容,并将_size到n位置的数据置为ch,_size也置为n。
n>_size的这两种情况只有是否会增容的区别,其他没有区别,可以合二为一。
//resize(size_t n,char ch='\0')
void resize(size_t n, char ch = '\0')
{if (n < _size){_str[_size] = '\0';_size = n;}else{if (n > _capacity){reserve(n);}for (int i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\0';}
}
2.16 find
size_t find (const char* s, size_t pos = 0) const;
size_t find (char c, size_t pos = 0) const;
即从pos位置开始,寻找目标出现的下标,分为查找字符和查找字符串,找到返回字符或者字符串第一个字符的下标,没找到返回0。
//find--查找字符size_t find(char ch, size_t pos = 0) const{assert(pos < _size);size_t i = pos;while (i < _size){if (_str[i] == ch){return i;}++i;}return npos;}//find--查找字符串size_t find(const char* str, size_t pos = 0) const{assert(pos < _size);char* p = strstr(_str+pos, str);if (p == nullptr){return npos;}else{return p - _str;}}
2.17 字符串比较
字符串比较,只需要实现<和==,剩余的就可以用这两个实现重载
//s1<s2
bool operator<(const MyString& s)
{return strcmp(_str, s._str) > 0;
}//s1==s2
bool operator==(const MyString& s)
{return strcmp(_str, s._str) == 0;
}//s1<=s2
bool operator<=(const MyString& s)
{return *this < s || *this == s;
}bool operator>(const MyString& s)
{return !(*this <= s);
}bool operator>=(const MyString& s)
{return !(*this < s);
}bool operator!=(const MyString& s)
{return !(*this == s);
}
2.18 清空字符串
//clear--将字符串清空
void clear()
{_str[0] = '\0';_size = 0;
}
2.19 输入/输出函数
//输出函数<<
ostream& operator<<(ostream& out, const MyString& str)
{for (int i = 0; i < str.size(); ++i){cout << str[i];}return out;
}//输入
istream& operator>>(istream& in, MyString& s)
{while (1){char ch;//in >> ch;ch = in.get();if (ch == ' ' || ch == '\n'){break;}else{s += ch;}}return in;
}
string类的简单实现的完整代码可参考:string类的简单实现。
相关文章:

STL序列式容器之string的基本用法及实现
1.string类 在使用string类时,必须包含<string>头文件以及using namespace std; 接下来我们看看string类是如何被声明的: typedef basic_string<char> string; 可以看到:string类是被类模板basic_string用数据类型…...

lua脚本使用cjson转换json时,空数组[]变成了空对象{}
一、前言 项目lua使用工具:cjson 问题:reids中部分数据的json key存在为[]的值,使用cjson进行解析的时候将原本空数组[]解析成了空对象{} 目标:原本[] 转 [] 二、解决方案 在使用cjson类库时,先配置json转换要求 -…...

ImportError: /../lib/libstdc++.so.6: version `GLIBCXX_3.4.29解决方案
今天跑实验遇到了一个头疼的报错,完全看不懂,上网查了一下成功解决,但是网上的指令没法直接拿来用,所以在这里记录一下自己的解决方案。 报错信息: Traceback (most recent call last):File "/home/shizhiyuan/c…...

java-实现一个简单的httpserver-0.6.0
2024年10月14日14:17:07—0.6.0 java-实现一个简单的httpserver-0.6.0 背景功能具体代码打印 背景 通常写了一些接口,需要通过临时的http访问,又不需要spring这么厚重的框架 功能 设置并发监控并发两个get请求一个是根路径,一个是other增加…...

【论文#码率控制】ADAPTIVE RATE CONTROL FOR H.264
目录 摘要1.前言2.基本知识2.1 蛋鸡悖论2.2 基本单元的定义2.3 线性MAD预测模型 3.GOP级码率控制3.1 总比特数3.2 初始化量化参数 4.帧级码率控制4.1 非存储图像的量化参数4.2 存储图像的目标比特 5.基本单元级码率控制6.实验结果7.结论 《ADAPTIVE RATE CONTROL FOR H.264》 A…...

2024-10-16 学习人工智能的Day8
函数 定义(创建) 函数的创建def开始,后接函数名,在给参数表最后冒号表示函数基础信息给定 换行书写函数内部定义,在函数内部定义操作,最后函数自带返回,无定义返回值返回为None&…...

Python Django 数据库优化与性能调优
Python Django 数据库优化与性能调优 Django 是一个非常流行的 Python Web 框架,它的 ORM(对象关系映射)允许开发者以简单且直观的方式操作数据库。然而,随着数据量的增长,数据库操作的效率可能会成为瓶颈,…...

基于SpringBoot+微信小程序的农产品销售平台
基于SpringBoot微信小程序的农产品销售平台 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取项目下载方式🍅 一、项目…...

微前端学习以及分享
微前端学习以及分享 注:本次分享demo的源码github地址:https://github.com/rondout/micro-frontend 什么是微前端 微前端的概念是由ThoughtWorks在2016年提出的,它借鉴了微服务的架构理念,核心在于将一个庞大的前端应用拆分成多…...

【Linux-进程间通信】vscode使用通信引入匿名管道引入
一、新系统,新软件 1.新系统 哈喽宝子们,从今以后我们不再使用风靡一时的CentOS系统了,因为CentOS已经不在维护了,各大公司几乎也都从CentOS转入其他操作系统了;我们现在由原来的CentOS系统切换到最新的Ubuntu系统&a…...

nerd bug:VPG多次计算vnetloss的计算图报错的解决
待更 Reference https://www.cnblogs.com/StarZhai/p/15495292.htmlhttps://github.com/huggingface/transformers/issues/12613https://discuss.pytorch.org/t/inplace-operation-errors-when-implementing-a2c-algorithm/145406/6...

BigDecimal类Date类JDK8日期
一、BigDecimal类是什么?它有什么用?先看一段代码,看这个代码有什么问题再说BigDeimal这个类是干什么用的,这样会好理解一些。 public class Test {public static void main(String[] args) {System.out.println(0.1 0.2);Syste…...

MybatisWebApp
如何构建一个有关Mybatis的Web? 在这里给出我自己的一些配置。我的TomCat版本:10.1.28 ,IDEA版本:2024.1.4 Pom.XML文件 <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/200…...

第十五章 RabbitMQ延迟消息之延迟插件
目录 一、引言 二、延迟插件安装 2.1. 下载插件 2.2. 安装插件 2.3. 确认插件是否生效 三、核心代码 四、运行效果 五、总结 一、引言 上一章我们讲到通过死信队列组合消息过期时间来实现延迟消息,但相对而言这并不是比较好的方式。它的代码实现相对来说比…...

OpenAI 公布了其新 o1 模型家族的元提示(meta-prompt)
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...

Java基础14-网络编程
十四、网络编程 java.net.*包下提供了网络编程的解决方案! 基本的通信架构 基本的通信架构有2种形式: CS架构( Client客户端/Server服务端)、BS架构(Browser浏 览器/Server服务端)。无论是CS架构,还是BS架构的软件都必须依赖网络编程!。 1、网络通信的三要素 网络通…...

sed命令详解
sed命令详解 sed(stream editor,流编辑器)是 Linux 和 Unix 系统中功能强大的文本处理工具,它能够对输入流(如文件、管道输入等)进行逐行处理,从而实现多种多样的文本编辑操作。 基本语法 se…...

Linux高阶——1013—正则表达式练习
1、正则表达式匹配机制 问号放在或者*后面,表示切换成非贪婪模式 [^>]表示非右尖括号的都能匹配,直到找到href"为止 [^"]表示向右匹配,到"为止 因此,三个都能匹配 2、 正则函数 寻找结果 源文件 正则函数运…...

【CMake】为可执行程序或静态库添加 Qt 资源文件,静态库不生效问题
【CMake】添加静态库中的 Qt 资源 文章目录 可执行程序1. 创建资源文件(.qrc)2. 修改 CMakeLists.txt3. 使用资源文件 静态库1. 修改 CMakeLists.txt2. 使用资源2.1 初始化资源文件2.2 可执行程序中调用 这里介绍的不是使用 Qt 创建工程时默认的 CMakeLi…...

服务器、jvm、数据库的CPU飙高怎么处理
服务器 CPU 飙高处理 排查步骤: 监控工具:使用操作系统自带的监控工具,比如 top、htop、sar、vmstat 等,查看哪些进程占用了大量的 CPU 资源。进程排查:通过 top 等工具找到消耗 CPU 最高的进程,确定是哪…...

自适应过滤法—初级
#课本P144例题 """ Python 简单的自适应过滤移动平均预测方法 """ import numpy as np import matplotlib.pyplot as plt#用于迭代的函数 def self_adaptive( seq, N, k, maxsteps ):## 初始化序列seq_ada = np.zeros( len(seq) ) # 设置预测…...

UML图有用吗?真正厉害的软件开发,有用的吗?什么角色用?
UML(Unified Modeling Language,统一建模语言)图在软件开发中是有用的,但其使用取决于项目的规模、复杂度以及开发团队的实践习惯。真正厉害的开发者并非一定要依赖UML图,但在某些情况下,UML图确实能够提升…...

基于Java+Springboot+Vue开发的酒店客房预订管理系统
项目简介 该项目是基于JavaSpringbootVue开发的酒店客房预订管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java…...

OpenCV高级图形用户界面(5)获取指定滑动条(trackbar)的当前位置函数getTrackbarPos()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 返回滑动条的位置。 该函数返回指定滑动条的当前位置。 cv::getTrackbarPos() 函数用于获取指定滑动条(trackbar)的当前…...

拓扑排序在实际开发中的应用
1. 拓扑排序说明 简单解释:针对于有向无环图(DAG),给出一个可行的节点排序,使节点之间的依赖关系不冲突。 复杂解释:自行搜索相关资料。 本次应用中的解释:给出一个可行的计算顺序࿰…...

【CTF-SHOW】Web入门 Web27-身份证日期爆破 【关于bp intruder使用--详记录】
1.点进去 是一个登录系统,有录取名单和学籍信息 发现通过姓名和身份证号可以进行录取查询,推测录取查询可能得到学生对应学号和密码,但是身份证号中的出生日期部分未知,所以可以进行爆破 2.打开bp抓包 这里注意抓的是学院录取查…...

Windows 添加右键以管理员身份运行 PowerShell
在 Windows 系统中添加一个右键菜单选项,以便可以使用管理员权限打开 PowerShell,可以通过编辑注册表来实现。 打开注册表编辑器: 按 Win R 打开运行对话框。输入 regedit 并按回车,这将打开注册表编辑器。 导航到文件夹背景键&…...

数学建模算法与应用 第15章 预测方法
目录 15.1 微分方程模型 Matlab代码示例:求解简单的微分方程 15.2 灰色预测模型(GM) Matlab代码示例:灰色预测模型 15.3 自回归模型(AR) Matlab代码示例:AR模型的预测 15.4 指数平滑法 M…...

HC32F460KETA PETB JATA 工业 自动化 电机
HC32F460 系列是基于 ARM Cortex-M4 32-bit RISC CPU,最高工作频率 200MHz 的高性能 MCU。Cortex-M4 内核集成了浮点运算单元(FPU)和 DSP,实现单精度浮点算术运算,支持 所有 ARM 单精度数据处理指令和数据类型…...

linux系统,不定时kernel bug :soft lockup的问题
这个问题困扰好久,机器经常不定时卡死,只能重启 后来检查是因为没有安装nvidia显卡驱动,或者更新到最新驱动 下载地址:驱动详情 禁止nouveau就可以了...