C++ Primer 笔记七
第十六章 string类和标准模板库
16.1 string类
1.构造字符串(string的构造函数)
|
名称 |
说明 |
|
将 String 类的新实例初始化为由指向 Unicode 字符数组的指定指针指示的值。 |
|
|
将 String 类的新实例初始化为由 Unicode 字符数组指示的值。 |
|
|
将 String 类的新实例初始化为由指向 8 位有符号整数数组的指针指示的值。 |
|
|
将 String 类的新实例初始化为由重复指定次数的指定 Unicode 字符指示的值。 |
|
|
初始化为由指向 Unicode 字符数组的指定指针、该数组内的起始字符位置和一个长度指示的值。 |
|
|
初始化为由 Unicode 字符数组、该数组内的起始字符位置和一个长度指示的值。 |
|
|
初始化为由指向 8 位有符号整数数组的指定指针、该数组内的起始字符位置和一个长度指示的值。 |
|
|
初始化为由指向 8 位有符号整数数组的指定指针、该数组内的起始字符位置、长度以及 Encoding 对象指示的值。 |
2.string类输入
对于C风格字符串有三种方式
char info[100];
cin>>info;
cin.getline(info,100); //固定大小
cin.get(info,100);
对于string对象有两种方式。
string str;
cin>>str;
getline(cin,str); //可自动调节str大小
两个版本的getline()都有一个可选参数,制定使用那个自负来确定输入边界cin.getline(info,100,’;’); getline(cin,str,’;’);
string是个类,这是操作符重载,如:getline(cin,str); operator>>(cin,str);
3.使用字符串
length()和size()成员函数都返回字符串中的字符数。
string::nops是字符串可存储的最大字符数,通常是无符号int或无符号long的最大取值
方法原型 |
描述 |
size_type find (const string &str, size_type pos=0) const |
从字符串的pos位置开始查找子字符串str,如找到则返回子字符串首次出现时其首字符的索引,否则返回string::nops |
size_type find (const char* s, size_type pos=0) const |
从字符串的pos位置开始查找子字符串s,如找到则返回子字符串首次出现时其首字符的索引,否则返回string::nops |
size_type find (const char* s, size_type pos=0,size_type n) |
从字符串的pos位置开始查找子字符串s的前n个字符组成的子字符串,如找到则返回子字符串首次出现时其首字符的索引,否则返回string::nops |
size_type find (char ch, size_type pos=0) const |
从字符串的pos位置开始查找字符ch,如找到则返回字符ch首次出现时其首字符的索引,否则返回string::nops |
string库还提供了相关方法,rfine()查找子字符串或字符最后出现的位置、find_first_of()在字符串中查找参数中任何一个字符首次出现的位置、find_last_of()在字符串中查找参数中任何一个字符最后依次出现的位置、find_first_not_of()在字符串中查找第一个不被包含在参数中的字符、find_last_not_of()字符串中查找最后一个不被包含在参数中的字符
string还提供了哪些功能
附录F
16.2 auto_ptr类
auto_ptr是一个模板类,用于管理动态内存分配的用法。(头文件:memory)
使用auto_ptr
auto_ptr模板定义了类似指针的对象,可以将new获得(直接或间接)的地址赋给这种对象。当auto_ptr过期时,其析构函数将使用delete来释放内存。因此如果将new返回的地址赋给auto_ptr对象时,无需记住稍后释放内存。
template <class X> class auto_ptr{
public:
explicit auto_ptr (X* p = 0)throw(); // throw()表示构造函数不引发异常
….} //explicit表示构造函数是显式的,不支持隐式类型转换
将使用new的函数转换为使用auto_ptr的函数:1.包含头文件memory;2.将指向string的指针替换成指向string的auto_ptr对象;3.删除delete语句。
auto_ptr <double> pd (new double); auto_ptr <string> pd (new string); auto_ptr <string> pd (new string(str));
new double 是new返回的指针,指向新分配的内存块。它是auto_ptr<double>构造函数的参数,即它是对应于原型中形参p的实参。
有关auto_ptr的注意事项
只能对new分配的内存使用auto_ptr对象,而不要对由new[]分配的,或通过声明变量分配的内存使用它。
auto_ptr <string> pd (new string(“It’s my bag.”));
auto_ptr <string> lily;
lily = pd;
两个指针将指向同一个string对象,其中一个是另一个的拷贝。上述赋值语句不可接受,因为当ps和lily都过期时,程序将试图删除同一个对象两次。
要避免这种问题可以有多种方法:
1.定义复制操作符,使之执行深复制。
2.建立所有权概念,对于特定的对象,只有一个智能指针可以拥有它。智能指针的构造函数只能删除该智能指针拥有的对象。并使复制操作转让所有权,这就是用于auto_ptr的策略。(但是如果所有权被转让,则前一个对象将不再引用该字符串。)
3.创建智能更高的指针,跟踪引用特定对象的智能指针数,这被成为引用计数,仅当最后一个指针过期时,delete才被调用。
16.3 STL
STL提供了一组表示容器、迭代器、函数对象和算法的模板。STL容器是同质的,即存储的值的类型相同;算法是完成特定任务的处方;迭代器是用来遍历容器的对象,与遍历数组的指针相似,是广义指针;函数对象是类似于函数的对象,可以是类对象或函数指针(包括函数名,因为函数名被用作指针)。STL不是面向对象的编程,而是一种不同的编程模式——通用编程技术。
vector模板类
分配器
与string类相似,各种STL容器模板都接受一个可选的模板参数,该参数制定使用哪个分配器对象来管理内存。例如,vector模板的开头与下面的类似
template <class T, class Allocator = allocateor<T> >
class vector {…..
如果省略该模版参数的值,则容器模板将默认使用allocator<T>类,这个类以标准方式使用new和delete
16.4通用编程技术
通用编程技术旨在编写独立于数据类型的代码。在C++中完成通用程序的工具是模板。
迭代器:是一个广义指针。事实上,它可以使指针,也可是是一个可以对其执行类似指针操作——如解除引用(*p)和递增(p++)——的对象。每个容器类都定义了一个合适的迭代器,该迭代器的类型是一个名为interator的typedef,其作用域为整个类。
vector<double>::iterator pd; //定义一个迭代器
vector<double> scores; //定义一个double类型的容器
pd = scores.begin(); //让pd指向scores的第一个元素
*pd = 22.3 //将pd指向的第一个元素的值设为22.3
++pd; //让pd指向下一个元素
为何需要迭代器:
模板使得算法独立于存储的数据类型,而迭代器是算法独立于使用的容器类型。模板还是和一个特定的数据结构(数组、链表…)关联在一起。
迭代器类型:输入迭代器、输出迭代器、正向迭代器、双向迭代器、随机访问迭代器。
输入迭代器
输入是从程序的角度来说的,即来自容器的信息被视为输入,就像来自键盘的信息对程序来说是输入一样,输入迭代器可被程序用作读取容器中的信息。只使用++来遍历容器信息。只读。
输出迭代器
将信息从程序传递给容器的迭代器叫做输出迭代器。用++来遍历容器信息。只写。
迭代器功能 |
输入 |
输出 |
正向 |
双向 |
随机访问 |
解除引用读取 |
有 |
无 |
有 |
有 |
有 |
解除引用写入 |
无 |
有 |
有 |
有 |
有 |
固定和可重复排序 |
无 |
无 |
有 |
有 |
有 |
++i 、 i++ |
有 |
有 |
有 |
有 |
有 |
--i 、 i-- |
无 |
无 |
无 |
有 |
有 |
i[n] |
无 |
无 |
无 |
无 |
有 |
i+n |
无 |
无 |
无 |
无 |
有 |
i-n |
无 |
无 |
无 |
无 |
有 |
i+=n |
无 |
无 |
无 |
无 |
有 |
i-=n |
无 |
无 |
无 |
无 |
有 |
容器概念
容器是存储其他对象的对象。被存储的对象必须是同一种类型的,它们可以是OOP意义上的对象,也可以使内置类型值。被存储在容器中的数据为容器所有,这意味着如果容器过期了,里面的数据也会过期。(不过如果是数据是指针的话,则它指向的数据并不一定过期。)
不是任何类型的对象都能存储在容器中的,类型必须是可复制构造的和可赋值的。
序列
deque、list、queue、priority_queue、stack、vector都是序列,队列能够在队尾添加元素,在队首删除元素。deque表示的双端队列允许在两端添加和删除元素。
序列要求其元素按严格的线性顺序排列,不会在两次迭代之间发生变化。
表 序列的要求
表达式 |
返回值类型 |
说明 |
X a(n, t) |
|
声明一个名为a的、由n个t组成的序列 |
X(n, t) |
|
创建一个由n个t组成的匿名序列 |
X a(i,j) |
|
声明一个名为a的序列,并将其初始化为区间[i, j)的内容 |
X(i, j) |
|
创建一个匿名序列,并将其初始化为区间[i, j)的内容 |
a.insert(p, t) |
迭代器 |
将t插入p前面 |
a.insert(p, n, t) |
void |
在p前面插入n个t |
a.insert(p, i, j) |
void |
在p前面插入区间[i, j)的内容 |
a.erase(p) |
迭代器 |
擦除删除p指向的元素 |
a.erase(p, q) |
迭代器 |
删除区间[p, q]区间的元素 |
a.clear() |
void |
把a清空,等价于a.clear(a.begin(), a.end()) |
表 序列的可选要求
表达式 |
返回值类型 |
含义 |
容器 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1.vector
vector是数组的一种类表示,它提供了自动内存管理功能,可以动态地改变vector对象的长度,并随着元素的添加和删除而增大和缩小,他提供了对元素的随机访问。在尾部添加和删除元素的时间是固定的,但是在头部或中间插入和删除元素的复杂度为现行时间。
vector是可翻转容器,增加了两个方法rbegin()和rend(),返回的迭代器都是类级类型reverse_iterator。对这样的迭代器进行弟子增,将导致它反向遍历可反转容器。
2.deque
双端队列double-ended queue,在STL中类似于vector容器,支持随机访问,主要区别是,从deque对象的开始位置插入和删除元素的时间是固定的,不像vector中那样是线性时间。因为实现更复杂,所以它比vector实现更慢。
3.list
双向链表。除了第一个和最后一个元素外,每个元素都与前后的元素相链接,这意味着可以双向遍历链表。list和vector之间关键的区别在于,list在链表中任意位置进行插入和删除的时间都是固定的。因此,vector强调的是通过随机访问进行快速访问,而list强调的是元素的快速插入和删除。
list也是可反转容器。但是list不支持数组表示法和随机访问。与矢量迭代器不同,从容器中插入和删除元素后曼联表迭代器指向的元素将不变,因为它不会移动元素的位置,知识会改变链接信息。指向某个元素的迭代器在容器插入或删除其他元素后,仍指向它原来指向的元素,但它的链接元素可能和原来不同。