标准模板库(Standard Template Library,STL)是惠普实验室开发的一系列软件的统称。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室工作时所开发出来的。虽说它主要出现到C++中,但在被引入C++之前该技术就已经存在了很长时间。STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
标准模板库是一个C++软件库,大量影响了C++标准程序库但并非是其的一部分。其中包含4个组件,分别为算法、容器、函数、迭代器。
模板是C++程序设计语言中的一个重要特征,而标准模板库正是基于此特征。标准模板库使得C++编程语言在有了同Java一样强大的类库的同时,保有了更大的可扩展性。
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,
但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可
能还会越界访问。
为了优化这个问题,我们引入了String容器。
string本身是一个类模板:
string就是char类型的数组,char类型一个字节
wstring是wchar类型的数组,wchar为两个字节
char16_t类型为2字节,char32_t为4个字节
第二种: string s3 = "hello world";
这里是一个const char*类型隐式转换成string类型
第三种: string s4(s3, 6, 3);
子字符串构造函数
复制str中从字符位置pos开始并跨越len个字符的部分(如果str太短或len为string::npos,则复制到str的末尾)。
第三个参数有缺省值,npos是无符号整形,-1代表整形最大值为42亿多
string s1;
string s2("hello world");
string s3 = "hello world";
string s4(s3, 6, 3);
string s5(s3, 6, 13);
cout << s5 << endl;
string s6(s3, 6);
cout << s6 << endl;
string s7("hello world", 5);
cout << s7 << endl;
string s8(10, '*');
cout << s8 << endl;
(constructor)函数名称 | 功能说明 |
---|---|
string() (重点) | 构造空的string类对象,即空字符串 |
string(const char* s) (重点) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) (重点) | 拷贝构造函数 |
构造空的string类对象,即空字符串
这里的空字符串指的是连\0都没有的
string s1(""); string s2();
复制以s为指向的以空结尾的字符序列(C-string)。
int main()
{
const char* p = "aaaaa";
string s1(p);
cout <<s1 << endl;//aaaaa
return 0;
}
用字符c的n个连续副本填充字符串。
string s2(4, 'b'); cout << s2 << endl;//bbbb
构造一个str的副本。
string s3(s2);
函数名称 | 功能说明 |
---|---|
size(重点) | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty (重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear (重点) | 清空有效字符 |
reserve (重点) | 为字符串预留空间** |
resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
size和length的效果是一样的,都是返回字符串的有效长度(不包括\0)
注意
size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
致,一般情况下基本都是用size()。
这个容量不一定等于字符串长度。它可以等于或大于,额外的空间允许对象在向字符串中添加新字符时优化其操作。
在对象被修改的任何时候,字符串的容量都可以被改变,即使这种修改意味着减小大小或者容量没有耗尽(这与vector容器中对容量的保证相反)。
可以通过调用成员reserve(后面会介绍)显式地更改字符串的容量。
void Teststring1()
{
// 注意:string类对象支持直接用cin和cout进行输入和输出
string s("hello, xiaolu!!!");
cout << s.size() << endl;//13
cout << s.length() << endl;//13
cout << s.capacity() << endl;//15
cout << s << endl;
}
void TestPushBack()
{
string s;
size_t sz = s.capacity();
cout << "making 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 << '\n';
}
}
}
在debug和release版本下,我们发现编译器每次扩容1.5倍左右
clear()只是将string中有效字符清空,不改变底层空间大小。
这里的情况是指清空size和length,不改变capacity
void Teststring1()
{
// 注意:string类对象支持直接用cin和cout进行输入和输出
string s("hello, xiaolu!!!");
cout << s.size() << endl;//13
cout << s.length() << endl;//13
cout << s.capacity() << endl;//15
cout << s << endl;
// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
s.clear();
cout << s.size() << endl;//0
cout << s.capacity() << endl;//15
}
如果n小于当前字符串长度,则将当前值缩短到前n个字符,删除第n个字符以外的字符。
如果n大于当前字符串长度,则通过在末尾插入尽可能多的字符来扩展当前内容,以达到n的大小。如果指定了c,则新元素被初始化为c的副本,否则,它们是值初始化的字符(空字符)。
void Teststring1()
{
// 注意:string类对象支持直接用cin和cout进行输入和输出
string s("hello, xiaolu!!!");
s.clear();
cout << s.size() << endl;//0
cout << s.capacity() << endl;//15
// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
// “aaaaaaaaaa”
s.resize(10, 'a');
cout << s.size() << endl;//10
cout << s.capacity() << endl;//15
// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
// "aaaaaaaaaa\0\0\0\0\0"
// 注意此时s中有效字符个数已经增加到15个
s.resize(15);
cout << s.size() << endl;//15
cout << s.capacity() << endl;//15
cout << s << endl;
// 将s中有效字符个数缩小到5个
s.resize(5);
cout << s.size() << endl;//5
cout << s.capacity() << endl;//15
cout << s << endl;
}
void Teststring2()
{
string s;
// 测试reserve是否会改变string中有效元素个数
s.reserve(100);
cout << s.size() << endl;//0
cout << s.capacity() << endl;//111
// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小
s.reserve(50);
cout << s.size() << endl;//0
cout << s.capacity() << endl;//111
}
这里必须要对象存在才可以调用,像string()出来的对象不存在,根本调用不了
函数名称 | 功能说明 |
---|---|
operator[] (重点) | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭 代器 |
rbegin + rend | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭 代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
我们在用字符数组的时候,我们经常用[]来访问字符,我们在stirng中同样可以这样使用
C++11支持更简洁的范围for的新遍历方式
void Teststring4()
{
string s("hello xiaolu");
for (size_t i = 0; i < s.size(); ++i)
cout << s[i] << " ";
cout << endl;
for (auto ch : s)
cout << ch << " ";
}
迭代器(iterable)是一个超级接口! 是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦。
迭代器可以近似理解为指针,后面会详细讲解的
begin返回指向字符串开头的迭代器。
❓这里的const跟我们之前的const int i是不是一样的?
并不是,后者是一个关键字被修饰的不可以修改,前置是一个类型名称,被修饰的是这个迭代器指向的内容
end返回一个指向字符串末尾字符的迭代器。
rbegin和rend 跟begin和end完全相反
void Teststring4()
{
string s("hello xiaolu");
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// string::reverse_iterator rit = s.rbegin();
// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
auto rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+= (重点) | 在字符串后追加字符串str |
c_str(重点) | 返回C格式字符串 |
find + npos(重点) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
将字符c添加到字符串的末尾,使其长度增加1。
string str; str.push_back(' '); // 在str后插入空格
追加到字符串
通过在当前值的末尾添加额外的字符来扩展字符串:
void Teststring5()
{
string str;
str.push_back(' '); // 在str后插入空格
str.append("hello"); // 在str后追加一个字符"hello"
}
在字符串后追加字符串str
void Teststring5()
{
string str;
str.push_back(' '); // 在str后插入空格
str.append("hello"); // 在str后追加一个字符"hello"
str += 'x'; // 在str后追加一个字符'x'
str += "iaolu"; // 在str后追加一个字符串"iaolu"
cout << str << endl;
}
获取C字符串等价项
void Teststring5()
{
string str;
str.push_back(' '); // 在str后插入空格
str.append("hello"); // 在str后追加一个字符"hello"
str += 'x'; // 在str后追加一个字符'x'
str += "iaolu"; // 在str后追加一个字符串"iaolu"
cout << str << endl;
cout << str.c_str() << endl; // 以C语言的方式打印字符串
}
在字符串中搜索由其参数指定的序列的第一个匹配项。
当指定pos时,搜索只包含pos位置或之后的字符,忽略任何可能包含pos之前字符的情况。
请注意,与find_first_of成员不同的是,每当搜索多个字符时,仅匹配其中一个字符是不够的,必须匹配整个序列。
rfind
跟find完全相反,她从pos位置开始向前走
当指定pos时,搜索只包含从pos位置开始或之前的字符序列,忽略任何从pos位置之后开始的可能匹配。
生成子串,返回一个新构造的string对象,其值初始化为该对象的子字符串的副本。
void Teststring5()
{
string str;
str.push_back(' '); // 在str后插入空格
str.append("hello"); // 在str后追加一个字符"hello"
str += 'x'; // 在str后追加一个字符'x'
str += "iaolu"; // 在str后追加一个字符串"iaolu"
cout << str << endl;
cout << str.c_str() << endl; // 以C语言的方式打印字符串
// 获取file的后缀
string file("string.cpp");
size_t pos = file.rfind('.');
string suffix(file.substr(pos, file.size() - pos));
cout << suffix << endl;
// npos是string里面的一个静态成员变量
// static const size_t npos = -1;
// 取出url中的域名
string url("http://www.cplusplus.com/reference/string/string/find/");
cout << url << endl;//http://www.cplusplus.com/reference/string/string/find/
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;//www.cplusplus.com
// 删除url的协议前缀
pos = url.find("://");
url.erase(0, pos + 3);
cout << url << endl;
}
注意:
函数 | 功能说明 |
---|---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> (重点) | 输入运算符重载 |
operator<< (重点) | 输出运算符重载 |
getline (重点) | 获取一行字符串 |
relational operators (重点) | relational operators (重点) |
连接字符串,返回一个新构造的string对象,其值由lhs中的字符和rhs中的字符拼接而成。
getline(istream& is, string& str, char delim);
int main()
{
string line;
char delim = ':';
cout << "Enter a line with as delimiter :" << endl;
getline(cin, line, delim);
cout << "You entered:" << line << endl;
return 0;
}
getline(istream& is, string& str);
这个形式的函数从输入流is中读取一行文本,并将其存储在str字符串中,直到遇到换行符为止。读取的换行符不包括在存储的字符串中。
operator>> (重点)
从流中提取字符串,从输入流中提取一个字符串,将该序列存储在str中,str被覆盖(之前的str值被替换)。
该函数重载运算符>>,使其行为与istream::operator>>中描述的一样,适用于c-string对象。
提取出的每个字符都会像调用push_back成员一样被添加到字符串中。
请注意,istream提取操作使用空格作为分隔符。因此,这个操作只会从流中提取可以认为是单词的内容。要提取整行文本,请参阅全局函数getline的字符串重载。
向流中插入字符串,将符合str的值的字符序列插入到os中。
这个后面写模拟实现的时候,会详细讲解
在pos(或p)指定的字符之前插入额外的字符:
int main()
{
string s1("world");
s1.insert(0, "hello");
cout << s1 << endl;
s1.insert(5, " ");
cout << s1 << endl;
return 0;
}
从字符串中删除字符,删除字符串的一部分,减少其长度:
string s2("hello world");
s2.erase(5, 1);
cout << s2 << endl;
当删除的字符的个数超过了字符的个数会怎么样?
int main()
{
string s1("world");
s1.insert(0, "hello");
cout << s1 << endl;
s1.insert(5, " ");
cout << s1 << endl;
return 0;
}