一、STL的基础介绍
1. 什么是STL
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
2. 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想要阅读部分源代码,可以主要参考这个版本。
3. STL的六大组件
二、string类的介绍
string类是STL中的容器,也是我们学习STL的开始。
1. 为什么学习string类
- C语言中的字符串
C语言中基本类型有char
类型没有字符串类型,字符串是以'\0'
结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数(如:strlen, strcmp,strcat,strcpy
等),但是这些库函数与字符串是分离开的,不太符合OOP(面向对象)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
因此为了更加高效的使用和管理字符串,C++提出了string
类的概念,关于string
类的学习我们先学习string
类的使用,后面我们进一步深入时我们再去动手模拟实现string
类,在这里我们首先知道string
类的底层其实就是char
类型的顺序表就行了。
2. 标准库中的string类
从文档中我们能看到:
string类
是basic_string
模板类的一个实例,它使用char来实例化basic_string模板类。- string在底层实际是:basic_string模板类的别名,
typedef basic_string<char>string;
- 不能操作多字节或者变长字符的序列。(如英文使用ASCII编码使用一个字节表示一个字母,汉字如果采用GBK编码占用两个字节表示一个汉字)
- 处理多字节或者变长字符的序列要用到以下三个类:
- 在使用string类时,必须包含
#include<string>
以及using namespace std
;
三、string类的常用接口说明
在上面的string类的文档介绍中我们可以看到string
类接近有140个不同的接口函数,在这么多的成员函数中,很多都是冗余不必要的,因此下面我只讲解最常用的接口。
在讲解这些函数接口之前,我们先来介绍npos
,npos
是string
类里面的一个静态成员变量,因此用string
类创建的所有对象访问的都是同一个npos
,其次我们来看pos的定义:
首先npos的值是被const
修饰了,其值不能更改,然后size_t
是无符号整形unsigned
的typedef
,定义将-1的值赋值给了npos,但npos是无符号整形,因此此时nops的值就是无符号整形的最大值65535。
1. string类对象的常见构造函数
- 缺省构造函数,用空字符串初始化string对象
- 拷贝构造函数,用
string
类的对象另一个对象 - 用
string
类的对象中的字符串的子串初始化一个对象,子串的起始位置是pos
,结束位置是pos向后的len
个字符,如果len
没有给缺省值,默认为npos
即从pos
位置后面有多少拿多少。 - 用C风格的字符串初始化string类的对象
- 用C风格的字符串前n个字符初始化string类的对象
- 用n个字符初始化string类的对象
#include<iostream> #include<string> using namespace std; int main() { string s0("hello world!"); string s1; //default (1) 缺省构造函数 //用空字符串初始化s1对象 string s2(s0); //copy (2) 拷贝构造函数 //用string类的对象s0初始化s2对象 string s3(s0, 6, 5); //substring (3) 用string类的对象s0的字符串的子串"world"初始化s3 //这里pos参数表示位置,len表示的是个数 string s4("hello string"); //from c-string (4) //C风格的字符串初始化string类的对象s4 string s5("hello world",5); //from sequence(5) //用C风格的字符串前五个字符"hello"初始化string类的对象s5 string s6(5, 'x'); //fill (6) //用5个'x'初始化string类的对象s6 cout << s0 << endl; cout << s1 << endl; cout << s2 << endl; cout << s3 << endl; cout << s4 << endl; cout << s5 << endl; cout << s6 << endl; }
2. string类对象的容量操作
- 我们先来看看不涉及扩容的函数
#include<iostream> #include<string> using namespace std; int main() { string s1("hello world"); cout << s1.size() << endl; cout << s1.length() << endl; cout << s1.capacity() << endl; cout << s1.max_size() << endl; s1.clear(); cout << s1.empty() << endl; return 0; }
- 我们再来看看涉及扩容的函数
std::string::reserve()
函数与std::string::shrink_to_fit()
函数
//std::string::reserve()函数 #include<iostream> #include<string> using namespace std; int main() { string s1("hello world"); cout << s1.size() << endl; cout << s1.capacity() << endl; s1.reserve(200); //提前把空间扩容到200字节,但不初始化 //由于string类内部的一些原因(如:对齐等原因),虽然向系统申请的的200字节, //但不一定就是200字节,总之系统申请的空间会 >= 你给的指定值 cout << s1.size() << endl; cout << s1.capacity() << endl; s1.shrink_to_fit(); //缩减容量让 size == capacity cout << s1.size() << endl; cout << s1.capacity() << endl; return 0; }
观察会发现,经过reserve()
函数的处理后size(字符个数)没有变,但是capacity(容量)改变了。
经过shrink_to_fit()
函数处理后size(字符个数)没有变,但是capacity(容量)改变了。(虽然,shrink_to_fit()
函数能够减少空间的浪费,但是我们还是一般不缩容,因为缩容的消耗是很大的)