🚀标准库中的string类
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
6. string是表示字符串的字符串类
7. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
8. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
9. 不能操作多字节或者变长字符的序列。在使用string类时,必须包含#include头文件以及using namespace std;
🚀1.string类对象的构造
库里string类对象的构造方法:
string类对象有多种构造方式,但常用的只有几种。
⚡️1.1 无参构造
string()是string的无参构造,
int main()
{
string s1;
}
该构造函数构造空的string类对象,即空字符串。
⚡️1.2 拷贝构造
string(const string&s) 是string的拷贝构造,
int main()
{
string s1;
string s2(s1); //传入string类对象
}
该构造会拷贝一个与实参一致的string类
⚡️ 1.3 C-string构造
string(const char* s) 支持传入C语言的字符串来构造string类对象,一般有两种形式,
int main()
{
//第一种形式
string s1("Hello world");
//第二种形式
const char temp[] = "Hello world";
string s2(temp);
}
当然可以用 “ = ”来构造,
int main()
{
string s1 = "Hello world";
}
这里存在隐式类型转换,即将常量字符串“Hello world”隐式转换为string类临时对象,进行拷贝构造,
再来看一段构造,为何它能成立?
const string& s1 = "Hello world";
“Hello world”是常量字符串,具有有常性,相应的隐式类型转换的临时对象类型为const string ,对这个临时对象其别名,必须要用const修饰
⚡️ 1.4 子字符串构造
子字符串构造允许构造一个string类对象的部分内容:str为被拷贝对象,pos为开始拷贝的下标,len为拷贝的长度。
其中npos为缺省参数,是整形最大值,一定大于后面的长度,不写len时,会默认拷贝pos下标后的所有内容。
int main()
{
string s1("Hello world");
string s2(s1, 0, 6);
string s3(s1, 6);
cout << "s2 = " << s2 << endl;//s2 = Hello
cout << "s3 = " << s3 << endl;//s3 = world
}
🚀2.string类对象的访问即遍历
⚡️ 2.1 operator[]访问类对象及遍历
string库中对[ ]进行了重载,使得类对象可以像数组一样访问类的成员,甚至对其做修改,
int main()
{
string s1("Hello world");
//访问s1的第一个字符
s1[0];
//修改s1的第一个字符
s1[0] = 'a';
}
让我们深层的为什么operator[]能做到修改,
//string的[]符号重载
char& operator[](size_t i)
{
assert(i < _size);
return _str[i];
}
可以看到其返回的是引用,返回的是成员本身,所以可以对其修改。除此之外,返回引用也可以减少拷贝,减少消耗。
对于[]符号,string还重载返回类型为 const char&,当string类对象被const修饰时,[ ]符号就不能修改
int main()
{
const string s1("Hello world");
s1[0] = 'a';//报错,调用const char& operator[],不能对const对象进行修改
}
由此,这里就有了第一种遍历string的方式
:
string s1("hello world");
// 遍历方式1:下标+[]
for (size_t i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
}
⚡️ 2.2 迭代器iterator遍历string
⚡️ 2.2.1 begin与end
利用迭代器遍历string,要用到string的成员函数:begin,end。
这两个函数返回的类型就是迭代器iterator,仅在遍历功能下,迭代器类型就类似于指针类型,begin函数返回的迭代器指向string的第一个成员,end函数返回的迭代器指向string的最后一个成员的下一位。
iterator begin();
const_iterator begin() const;
begin函数返回的迭代器指向string的第一个成员
iterator end();
const_iterator end() const;
end函数返回的迭代器指向string的最后一个成员的下一位
由此就有了第二种遍历的方式:
int main()
{
string s1("Hello world");
string::iterator it1 = s1.begin();
while (it1 != s1.end())
{
*it1 += 3;
cout << *it1 << " ";
++it1;
}
cout << endl;
}
注意:
- 因为每种容器都有自己的迭代器,因此在iterator前要限定作用域string::
- string::iterator可以替换为auto,让编译器自己推断类型,我们自己就可以不用那么累的写出具体的类型。
⚡️ 2.2.2 rbegin与rend
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
rbegin迭代器指向string的最后一个成员
reverse_iterator rend();
const_reverse_iterator rend() const;
rend迭代器指向string的第一个成员的前一位
通过rbegin与rend遍历string
int main()
{
string s1("Hello world");
string::reverse_iterator it1 = s1.rbegin();
while (it1 != s1.rend())
{
*it1 -= 1;
cout << *it1 << " ";
++it1;
}
cout << endl;
}
⚡️ 2.2.3 const_iterator
无论是begin与end,rbegin与rend都有一个返回值const_iterator的重载,
iterator 可读可写
const_iterator 可读不可写
⚡️ 2.4 范围for遍历
遍历方式3: 范围for:
// 底层角度,他就是迭代器
string s1("hello world");
for (auto& e : s1)
{
e++;
cout << e << " ";
}
注意:范围for从底层角度,从编译器运行角度就是迭代器。
可以从反汇编角度看到调用了begin和end。
🚀3.string类对象的增与删
string类对象的增与删对应的函数有非常多,但只有几个常用,
⚡️3.1 增
⚡️3.1.1 push_back
push_back用于追加一个字符->尾插
int main()
{
string s1("hello world");
s1.push_back('!');
}
⚡️3.1.2 append
append多用于追加C语言的字符串,红框是用的最多的
string& append (const char* s);//追加C语言的字符串
int main()
{
string s1("hello world");
s1.append("!!!!!");
}
⚡️3.1.3 operator+=(用得最多)
+=符号重载可以追加 字符/c语言字符串/string类对象。
int main()
{
string s1("hello world");
string s2("??");
s1 += '!';//追加字符
s1 += "!!";//追加c语言字符串
s1 += s2;//string类对象
}
⚡️3.1.4 insert
insert可以在pos位置后插入一个字符/c语言字符串/string类对象,慎用,效率不高->O(n)
。
常用的:
string& insert (size_t pos, const string& str);//插入string类对象
string& insert (size_t pos, const char* s);//插入c语言字符串
string& insert (size_t pos, size_t n, char c);//插入c字符
//头插字符串
string s2("hello world");
s2.insert(0, "xxxx");
cout << s2 << endl;
//头插字符
char ch = 'y';
s2.insert(0, 1, ch);
cout << s2 << endl;
//用迭代器头插字符
s2.insert(s2.begin(), 'y');
cout << s2 << endl;
//用迭代器头插string类对象
s2.insert(s2.begin(), s1.begin(), s1.end());
cout << s2 << endl;
⚡️3.1.5 replace
replace替换pos位置的字符为 c语言字符串/string类对象,与insert不同的是一个是插入一个是替换。并且效率不高,慎用,和insert类似,要挪动数据
常用的:
//替换为string类对象
string& replace (size_t pos, size_t len, const string& str);
//替换为c语言字符串
string& replace (size_t pos, size_t len, const char* s);
⚡️3.2 删
⚡️3.2.1 pop_back
pop_back用于删除最后一个字符->尾删
int main()
{
string s1("hello world");
s1.pop_back();
}
⚡️3.2.2 erase
erase对范围的内容进行删除,慎用
,因为效率不高,常用的有:
常用的:
string& erase (size_t pos = 0, size_t len = npos);
iterator erase (iterator first, iterator last);
int main()
{
//string& erase (size_t pos = 0, size_t len = npos);
string s1("hello world");
//1.删除从下标0开始往后的6个字符
s1.erase(0, 6);
//2.可以传入迭代器
auto begin = s1.begin();auto end = s1.end();
s1.erase(begin, end);
//不传第二个参数直接,删完
s1.erase(0);
}