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

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类时&#xff0c;必须包含<string>头文件以及using namespace std&#xff1b; 接下来我们看看string类是如何被声明的&#xff1a; typedef basic_string<char> string; 可以看到&#xff1a;string类是被类模板basic_string用数据类型…...

lua脚本使用cjson转换json时,空数组[]变成了空对象{}

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

ImportError: /../lib/libstdc++.so.6: version `GLIBCXX_3.4.29解决方案

今天跑实验遇到了一个头疼的报错&#xff0c;完全看不懂&#xff0c;上网查了一下成功解决&#xff0c;但是网上的指令没法直接拿来用&#xff0c;所以在这里记录一下自己的解决方案。 报错信息&#xff1a; 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 背景功能具体代码打印 背景 通常写了一些接口&#xff0c;需要通过临时的http访问&#xff0c;又不需要spring这么厚重的框架 功能 设置并发监控并发两个get请求一个是根路径&#xff0c;一个是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

函数 定义&#xff08;创建&#xff09; 函数的创建def开始&#xff0c;后接函数名&#xff0c;在给参数表最后冒号表示函数基础信息给定 换行书写函数内部定义&#xff0c;在函数内部定义操作&#xff0c;最后函数自带返回&#xff0c;无定义返回值返回为None&…...

Python Django 数据库优化与性能调优

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

基于SpringBoot+微信小程序的农产品销售平台

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

微前端学习以及分享

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

【Linux-进程间通信】vscode使用通信引入匿名管道引入

一、新系统&#xff0c;新软件 1.新系统 哈喽宝子们&#xff0c;从今以后我们不再使用风靡一时的CentOS系统了&#xff0c;因为CentOS已经不在维护了&#xff0c;各大公司几乎也都从CentOS转入其他操作系统了&#xff1b;我们现在由原来的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类是什么&#xff1f;它有什么用&#xff1f;先看一段代码&#xff0c;看这个代码有什么问题再说BigDeimal这个类是干什么用的&#xff0c;这样会好理解一些。 public class Test {public static void main(String[] args) {System.out.println(0.1 0.2);Syste…...

MybatisWebApp

如何构建一个有关Mybatis的Web&#xff1f; 在这里给出我自己的一些配置。我的TomCat版本&#xff1a;10.1.28 &#xff0c;IDEA版本&#xff1a;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. 确认插件是否生效 三、核心代码 四、运行效果 五、总结 一、引言 上一章我们讲到通过死信队列组合消息过期时间来实现延迟消息&#xff0c;但相对而言这并不是比较好的方式。它的代码实现相对来说比…...

OpenAI 公布了其新 o1 模型家族的元提示(meta-prompt)

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

Java基础14-网络编程

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

sed命令详解

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

Linux高阶——1013—正则表达式练习

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

【CMake】为可执行程序或静态库添加 Qt 资源文件,静态库不生效问题

【CMake】添加静态库中的 Qt 资源 文章目录 可执行程序1. 创建资源文件&#xff08;.qrc&#xff09;2. 修改 CMakeLists.txt3. 使用资源文件 静态库1. 修改 CMakeLists.txt2. 使用资源2.1 初始化资源文件2.2 可执行程序中调用 这里介绍的不是使用 Qt 创建工程时默认的 CMakeLi…...

服务器、jvm、数据库的CPU飙高怎么处理

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

自适应过滤法—初级

#课本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&#xff08;Unified Modeling Language&#xff0c;统一建模语言&#xff09;图在软件开发中是有用的&#xff0c;但其使用取决于项目的规模、复杂度以及开发团队的实践习惯。真正厉害的开发者并非一定要依赖UML图&#xff0c;但在某些情况下&#xff0c;UML图确实能够提升…...

基于Java+Springboot+Vue开发的酒店客房预订管理系统

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

OpenCV高级图形用户界面(5)获取指定滑动条(trackbar)的当前位置函数getTrackbarPos()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 返回滑动条的位置。 该函数返回指定滑动条的当前位置。 cv::getTrackbarPos() 函数用于获取指定滑动条&#xff08;trackbar&#xff09;的当前…...

拓扑排序在实际开发中的应用

1. 拓扑排序说明 简单解释&#xff1a;针对于有向无环图&#xff08;DAG&#xff09;&#xff0c;给出一个可行的节点排序&#xff0c;使节点之间的依赖关系不冲突。 复杂解释&#xff1a;自行搜索相关资料。 本次应用中的解释&#xff1a;给出一个可行的计算顺序&#xff0…...

【CTF-SHOW】Web入门 Web27-身份证日期爆破 【关于bp intruder使用--详记录】

1.点进去 是一个登录系统&#xff0c;有录取名单和学籍信息 发现通过姓名和身份证号可以进行录取查询&#xff0c;推测录取查询可能得到学生对应学号和密码&#xff0c;但是身份证号中的出生日期部分未知&#xff0c;所以可以进行爆破 2.打开bp抓包 这里注意抓的是学院录取查…...

Windows 添加右键以管理员身份运行 PowerShell

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

数学建模算法与应用 第15章 预测方法

目录 15.1 微分方程模型 Matlab代码示例&#xff1a;求解简单的微分方程 15.2 灰色预测模型&#xff08;GM&#xff09; Matlab代码示例&#xff1a;灰色预测模型 15.3 自回归模型&#xff08;AR&#xff09; Matlab代码示例&#xff1a;AR模型的预测 15.4 指数平滑法 M…...

HC32F460KETA PETB JATA 工业 自动化 电机

HC32F460 系列是基于 ARM Cortex-M4 32-bit RISC CPU&#xff0c;最高工作频率 200MHz 的高性能 MCU。Cortex-M4 内核集成了浮点运算单元&#xff08;FPU&#xff09;和 DSP&#xff0c;实现单精度浮点算术运算&#xff0c;支持 所有 ARM 单精度数据处理指令和数据类型&#xf…...

linux系统,不定时kernel bug :soft lockup的问题

这个问题困扰好久&#xff0c;机器经常不定时卡死&#xff0c;只能重启 后来检查是因为没有安装nvidia显卡驱动&#xff0c;或者更新到最新驱动 下载地址&#xff1a;驱动详情 禁止nouveau就可以了...