文章目录
- 1、什么是STL
- 2、STL简介
- 3、什么是string类
- 4、string类的常用接口说明
- 1、常见构造函数
- 2、容量操作
- 3、迭代器
- 4、其他的标准库的string类
关于string类的内容,可以在cplusplus.com查看到。
1、什么是STL
STL是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
2、STL简介
STL最初是在惠普实验室诞生的,并向外开源。
原始版本
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意
运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使
用。 HP 版本–所有STL实现版本的始祖。
P. J. 版本
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,
符号命名比较怪异。
RW版本
由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
SGI版本
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,
可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。一般学习STL要阅读部分源代码,
主要参考的就是这个版本。
STL分为仿函数,算法,容器,迭代器,空间配置器,配接器
3、什么是string类
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,
但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
string类则是C++定义的一个类。根据官方文档记录:
- 字符串是表示字符序列的类
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作
单字节字符字符串的设计特性。 - string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信
息,请参阅basic_string)。 - string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits
和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。 - 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个
类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
比特就业课 - string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>
string; - 不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;
简单看一个代码
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1;
string s2("hello world");
for (size_t i = 0; i < s2.size(); ++i)
{
s2[i]++;
}
cout << s2 << endl;
return 0;
}
string需要头文件string,s2[i]++会让对应的字符往后一位,比如h变为i,e变为f。
4、string类的常用接口说明
1、常见构造函数
string(const char* s) 和上面的代码一样,可以string s2(“hello world”); 也可以string s2 = “hello world”。
string(const string& str, size_t pos, size_t len = npos)。这个构造函数的意思就是取字符串的一部分,从pos位置开始往后len个字符。
string s3 = "hello world";
string s4(s3, 6, 3);
结果是wor。第6个位置是空格,打印空格之后3个位置。如果后面的长度过大,那么就会取到字符串结束,而不是按照实际的len去越界。如果不给len,那么就会按照它的缺省值来打印,len的缺省值是npos,定义中给的值是无符号整数-1,所以就是整数的最大值,那么就会打印到字符串结束。
string(const char* s, size_t n),用字符串前n个进行构造。
string s5("hello world", 6);
cout << s5 << endl;
打印hello和空格。
string(size_t n, char c),用n个字符c来构造。
string s6(10, '*');
cout << s6 << endl;
cplusplus.com可以查到相关文档。
2、容量操作
int main()
{
string s1("hello world");
cout << s1.size() << endl;
cout << s1.length() << endl;
return 0;
}
size和length两个功能一样,返回有效字符串长度。max_size()则是察看能达到的最大值,42亿9千万多,没有多大意义。
capacity(),结果是15,不包含’\0’,返回空间总大小。
int main()
{
string s1("hello");
s1.push_back('?');
s1.append("asda");
cout << s1 << endl;
return 0;
}
push_back只能加字符,而append可以加字符串。append还有别的用法,在cplusplus.com查看。统合这两个功能,+=就可以全实现。
s1 += '!';
s1 += "asdghjfk";
cout << s1 << endl;
capacity函数,会在原有空间不足时进行自动扩容,这个根据不同的编译器有不同的结果,vs是1.5倍扩容,Linux不一样。
int main()
{
string s;
cout << sizeof(s) << endl;
size_t sz = s.capacity();
cout << "make s grow:\n";
cout << "capacity changed: " << sz << '\n';
for (int i = 0; i < 100; ++i)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << endl;
}
}
}
也有方法阻止自动扩容,reserve函数,预留空间。
string s;
s.reserve(100);
不一定会多开100空间,但是一定开得比100多。
resize可以开空间并初始化。
int main()
{
string s1("hello world");
s1.reserve(100);
cout << s1.capacity() << endl;
cout << s1.size() << endl;
string s2("hello world");
s2.resize(100);
cout << s2.capacity() << endl;
cout << s2.size() << endl;
return 0;
}
resize括号里第二个参数可以加上字符,用来初始化空白的空间为指定的字符。resize也可以删除数据,括号的数字n比size小,就会保留前n个,但不会缩小capacity,缩容对于系统来说难度比较大,系统原生一般不支持。
3、迭代器
int main()
{
string s1("hello world");
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
分析一下这段代码。我们可以暂且理解为s1指向hello world字符串,s1.begin就是首元素地址,给了it,end则是’\0’的位置,it指向了第一个元素,然后边打印边++。
我们也可以用范围for,不过底层原理都一样,都是迭代器。
for (auto ch : s1)
{
cout << ch << " ";
}
cout << endl;
iterator是string类里的typedef的一个函数。用迭代器需要用它。迭代器可以反向使用。
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{
cout << *rit << " ";
++rit;
}
反向迭代器需要用到reverse_iterator。rbegin指向hello world的d而不是’\0’,rend则指向h前面的位置,这里正是因为是反向,所以++就等于–。
如果是加上了const
void func(const string& s)
{
string::const_iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
如果是const的变量,那么就得用const_iterator,刚才的反向迭代器也一样,需要用专用的,并且rbegin这些也得对应着用。const修饰,就只能遍历和读,而不能写;反向迭代器加上const,类的函数就应当是string::const_reverse_iterator。
4、其他的标准库的string类
string s1("hello world");
s1[100];
s1.at(100);
越界了会直接报错,如果是at,则会抛异常,虽然也报错,但可以捕获异常。
insert整体是插入的意思,有很多函数,可以插入string类对象,可以插入字符串,可以选择位置来控制插入。
string s1("hello world");
//s1[100];
//s1.at(100);
s1.insert(0, "hhhh");
cout << s1 << endl;
s1.insert(4, 1, ' ');
cout << s1 << endl;
return 0;
或者s1.insert(4, " ")这样。括号里也可以加上迭代器。
s1.insert(s1.begin() + 4, ' ');
cout << s1 << endl;
string s2("hello world");
s2.erase(5, 1);
//s2.erase(s2.begin() + 5);
cout << s2 << endl;
erase可以用来删除
如果括号里给的数字过大时,那就会有多少删多少。
insert和erase可能存在挪动数据,效率低下,所以不推荐常用。
之前有在字符串空格处把空格换成别的字符的题,现在用C++写
string s1("hello, the world");
size_t pos = s1.find(' ');
if (pos != string::npos)
{
s1.replace(pos, 1, "%20");
}
cout << s1 << endl;
return 0;
这里会把第一个空格改成%20,加个循环就可以把所有空格都换上。
while(pos != string::npos)
{
s1.replace(pos, 1, "%20");
pos = s1.find(' ');
}
不过这个程序还有改进之处。pos = s1.find(’ ', pos + 3),可以从%20的0处往后开始找下一个空格,增加一点效率。replace存在扩容,会把1个字符扩成3个字符。所以我们可以提前开好,避免replace开空间。
string s1("hello the world");
size_t num = 0;
for (auto ch : s1)
{
if (ch == ' ')
++num;
}
s1.reserve(s1.size() + 2 * num);
size_t pos = s1.find(' ');
while(pos != string::npos)
{
s1.replace(pos, 1, "%20");
pos = s1.find(' ', pos + 3);
}
cout << s1 << endl;
结束。