一、STL简介
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
在我们的学习C++的过程中,对STL的学习是必不可少的,只有熟悉了STL,才算是真正学过C++。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。STL的发展过程中,有过许多不同的版本,诸如HP 版本、P. J. 版本、RW版本、SGI版本,其中,SGI版本被Linux采纳,该版本的源码可移植性强、可读性好,在我们的学习过程中,可以结合该版本STL的源码进行学习,以加深我们对STL的理解。
STL有六大组件:仿函数、算法、迭代器、空间适配器、八大容器和配接器,接下来会对八大容器中的string作较为详细的介绍。
一句话总结:STL很重要!!!
学习STL的三大境界:能用,明理,能扩展。
二、string的介绍
我们在学C++的时候可能疑惑,我们在C语言阶段已经学过了字符串的表示类型,可以通过字符数组或者字符指针来定义字符串,为什么C++要添加string类呢,这么做有什么好处?
很明显,C语言中字符串的两种定义方式,都不是直接定义字符串类型,而是通过字符数组将字符串中的每个字符(包括'\0')存储在内,用这种方式定义字符串不方便调节数组的大小;或者是通过字符指针来定义字符串,但是这种方式定义的字符串具有常性,字符串本身的内容不可修改的;如果定义动态数组(用malloc()在堆区上申请空间,可以对字符串进行增删查改,但是需要我们自己去写函数,就算是调用C语言库函数,也需要用户自己控制,比较麻烦。所以,不论是哪种定义方式,都不够直接,而且使用起来不方便。
STL的string类很好的体现了类封装的好处,我们在定义字符串,操作字符串时,实际上是调用的string类的接口函数,可以简化代码,增强代码的复用性。
string类的特性:
1. string是表示字符串的字符串类。
2. 该类的接口与其他容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
4. 不能操作多字节或者变长字符的序列。
注意:在使用string类时,必须包含头文件<iostream>、展开std命名空间,即
#include <iostream>
using namespace std;
三、string的定义
string类是basic_string模板的一个实例化。
basic_string模板可以被实例化成string、wsting、u16string、u32string。
1. typedef basic_string<char> string;
char的大小是1个字节。
string 一个字符占一个字节 而且string有一个字符表示结尾 这个字符不会被length计算 所以length+1就是所占字节 sizeof出来的数字是默认开的空间 也就是只要定义一个string 系统就会为它分配32字节。
2. typedef basic_string<wchar_t> wstring;
wchar_t 在windows上大小是2个字节, 在linux下是4个字节,一个字符占了4个byte,与Java的String不一样。
3. typedef basic_string<char16_t> u16string;
sizeof(char16_t)=2,无论什么平台, char16_t占两个字节。
4. typedef basic_string<char32_t> u32string;
sizeof(char32_t)=4,无论什么平台, char32_t占4个字节。
在sizeof(wchar_t)=4的系统,wstring与u32string是一样的,在sizeof(wchar_t)=2的系统,wstring与u16string是一样的。
class string
{
public:
//string类的成员函数
private:
char* _str;
size_t _capacity;
size_t _size;
static const size_t npos = -1;
}
四、string的常用接口
1.构造函数和赋值重载
1.1构造函数
string类重载了多个构造函数,结合上图进行说明:
(1)无参构造函数:构造一个空字符串对象。
(2)拷贝构造函数:构造一个内容同str的字符串对象。
(3)拷贝构造函数:构造一个内容同str子串(从pos下标对应的字符开始的长度为len的子串,如果pos+len > str.length(),则将从pos位置开始的剩余部分全部拷贝)的字符串对象。
(4)构造一个内容同s指向的字符串的字符串对象。
(5)构造一个内容同s指向的字符串的前n个字符相同的对象(如果n>strlen(s),则构造一个内容同s指向的字符串相同的对象)。
(6)构造一个内容为n个c字符组成的字符串对象。
(7)以相同的顺序构造一个内容同[first,last)的字符序列的字符串对象。
代码测试各个构造函数的使用:
void test1()
{
string s0("Initial string");
// constructors used in the same order as described above:
string s1; //(1)
string s2(s0); //(2)
string s3(s0, 8, 3); //(3)
string s4("A character sequence"); //(4)
string s5("Another character sequence", 12); //(5)
string s6a(10, 'x'); //(6)
string s6b(10, 42); // 42 is the ASCII code for '*' (6)
string s7(s0.begin(), s0.begin() + 7); //(7)
cout << "s1: " << s1 << "\ns2: " << s2 << "\ns3: " << s3;
cout << "\ns4: " << s4 << "\ns5: " << s5 << "\ns6a: " << s6a;
cout << "\ns6b: " << s6b << "\ns7: " << s7 << '\n';
}
运行结果:
1.2赋值重载函数
赋值重载和拷贝构造大同小异:
同:用已有的string对象或字符串来构造其他string对象
异:拷贝构造是构造新对象,即定义时构造(初始化);赋值重载是赋值给已存在的对象,即赋值。
注意二者的区别。
代码测试各个赋值重载函数的使用:
void test2()
{
string str1, str2, str3;
str1 = "Test string: "; // c-string
str2 = 'x'; // single character
str3 = str1 + str2; // string
cout << str3 << '\n';
}
运行结果:
2.析构函数
析构函数是用来释放类对象占用资源的函数,如果用户未定义,则编译器会自动生成默认析构函数。但是默认析构函数无法释放堆区资源,只能用户自己定义。
如果不存在占用堆区资源的类对象,可以不用自定义析构函数。
另外,析构函数无需显式调用,编译器在类生命周期结束时自动调用
3.迭代器相关的函数
3.1 begin()
返回指向string类对象首元素的迭代器,string类的迭代器可以理解为指针。
3.2 end()
返回指向string类对象最后一个元素的后一个位置的迭代器。
3.3 rbegin()
迭代器包括正向迭代器和反向迭代器,即iterator和reverse_iterator。
正向迭代器和反向迭代器的主要显式区别就是进行++和--运算时,移动方向相反。
rbegin()返回指向string类最后一个元素的反向迭代器。
3.4 rend()
rend()返回执行string类第一个元素的前一个位置的反向迭代器。
另外,cbegin()和cend()的返回类型是const迭代器;crbegin()、cend()的返回类型是const反向迭代器。
代码测试各个跟迭代器相关函数的使用:
//3.测试迭代器相关的函数
void test3()
{
string str("Test string");
//1.begin() end()
for (string::iterator it = str.begin(); it != str.end(); ++it)
std::cout << *it;
cout << '\n';
//2.rbegin() rend()
for (string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit)
std::cout << *rit;
cout << '\n';
}
运行结果:
4.容量有关的函数
4.1 size()
返回string类对象的字符串长度。
4.2 length()
函数功能同size()。不过size()函数是求STL容器大小(区别于STL容器容量)的通用函数,length()函数接口只有string容器有。
4.3 resize()
重置string类的_size,即充值string类字符串的大小。
如果n > _size,则
void resize(size_t n); 将string类字符串大小置为n,增长的部分用' '(即空格)补充;
void resize(size_t n, char c); 将string类字符串大小置为n,增长的部分用字符变量c的值补充。
如果n < _size,则舍弃string类字符串长度大于n的部分。
4.4 capacity()
返回string类对象的容量。
4.5 reserve()
当n > capacity时,扩容,函数调用结束后capacity == n;当n < capacity时,本该缩容,但是缩容代价大,编译器不会这么做,所以capacity的值不变。
4.6 clear()
清空string类对象字符串,size == 0。
4.7 empty()
判空函数,判断string类对象是否为空。
为空返回true;不为空返回false。
代码测试各个和size以及capapcity相关函数的使用:
//4.测试容量相关的函数
void test4()
{
string str("I like to code in C");
//1.size() length()
cout << "The size of str is " << str.size() << " bytes.\n";
cout << "The size of str is " << str.length() << " bytes.\n";
cout << str << '\n';
unsigned sz = str.size();
//2.resize()
str.resize(sz + 2, '+');
cout << str << '\n';
str.resize(14);
cout << str << '\n';
//3.capacity()
cout << "capacity:" << str.capacity() << endl;
//4.reserve()
str.reserve(100);
cout << "capacity:" << str.capacity() << endl;
str.reserve(10);
cout << "capacity:" << str.capacity() << endl;
//5.clear() empty()
cout << "调用clear()函数前:";
if (str.empty())
cout << "str为空" << endl;
else
cout << "str不为空" << endl;
str.clear();
cout << "调用clear()函数后:";
if (str.empty())
cout << "str为空" << endl;
else
cout << "str不为空" << endl;
}
运行结果:
5.元素访问相关的函数
5.1 operator[]
下标访问字符串元素。
对越界访问会做检查,assert暴力检查。
5.2 at()
下标访问字符串元素。
对越界访问会做检查,抛错。
代码测试operator[]()和at()函数的使用:
void test5()
{
string str("Test string");
for (int i = 0; i < str.length(); ++i)
{
cout << str[i];
}
cout << endl;
for (int i = 0; i < str.length(); ++i)
{
cout << str.at(i);
}
cout << endl;
越界检查
//for (int i = 0; i <= str.length()+1; i++)
//{
// cout << str[i];
//}
//cout << endl;
//for (int i = 0; i <= str.length(); ++i)
//{
// cout << str.at(i);
//}
//cout << endl;
}
运行结果:
6.修改容器相关的函数
6.1 operator+=
添加string字符串的内容。
1)string& operator+= (const string& str); 添加string类对象str的字符串到*this
2)string& operator+= (const char* s); 添加字符串s到*this
3)string& operator+=(char c); 添加字符c到*this
6.2 append()
向string对象追加相应的内容。
6.3 push_back()
尾插,将字符变量c追加到string对象。
6.4 insert()
在下标pos或迭代器p表示的位置插入相应的内容。
6.5 erase()
擦除string类对象的部分内容。
6.6 replace()
代替string类对象的部分内容。
6.7 swap()
交换两个string类对象的内容。
6.8 pop_back()
尾删。
7.字符串操作相关的函数
7.1 c_str()
返回string类对象的成员c_str的首元素的地址。
7.2 find()
string类对象的内容查找。
7.3 rfind()
功能同find(),从后往前找。
7.4 substr()
返回一个以sring类对象内容的一个子串为内容构造的string类对象,即返回一个新string类对象,其内容是原string对象内容的子串。
8.特殊的成员变量
静态成员变量npos:
npos的值通常被定义为-1,表示没有匹配的下标,用作部分函数的返回值。
四、总结
string是STL的八大容器之一,内部数据结构是字符串,支持元素的随机访问(通过下标访问)。相比用C语言的字符串额日眼,这里很好的体现了类封装的优势,可以在类内定义函数,对外提供函数接口,方便用户调用常用功能函数来进行平时的代码设计和实现,提高效率。
string容器的模拟实现可以加深我们对其的理解,大家感兴趣的可以自己尝试,我就不再多做说明。如果本文有陈述不正确的地方,欢迎各位斧正!
另外补充说明:本文中关于string接口函数原型的截图以及代码测试来源为官网:Reference - C++ Reference,以后STL容器相关的博客也会参照该网站。