string类
string类包含大量的方法,包括了若干构造函数,用于将字符串赋给变量、合并字符串、比较字符串和访问各个元素的重载运算符以及在字符串中查找字符和子字符串的工具。
首先指出:NBTS为以空字符结束的字符串。
构造函数 | 描述 |
---|---|
string(const char *s) | 将string对象初始化为s指向的NBTS |
string(size_type n,char c) | 创建一个包含n个元素的string对象,其中每个元素都被初始化为字符c |
string(const string &str) | 讲一个string对象初始化为string对象str |
string() | 创建一个默认的string对象,长度为0 |
string(const char *s,size_type n) | 将string对象初始化为s指向的NBTS的前n个字符,即使超过了NBTS结尾 |
template<class Iter> string(Iter begin,Iter end) | 将string对象初始化为区间[begin,end)内的字符,其中begin和end的行为就像指针 |
string(const string & str, size_type pos=0,size_type n=npos) | 将一个string对象初始化为对象str中从位置pos开始到结尾的字符,或从位置pos开始的n个字符 |
//第六种构造函数需要例子来解释
string s("that's very good.");
string s1(s+2,s+5);
string s2(&s[2],&s[5]);
//也可以使用迭代器
string中的+已经被重载,用于将字符串联结,第二个参数可以是字符串也可以是字符,+=也是这样。[]也被重载了,因此可以像数组那样访问。
string的输入,对于C风格字符串,有三种方式:
char info[100];
cin>>info; //只读一个单词
cin.getline(info,100); //读一行,无视换行符
cin.get(info,100); //读一行,把换行符留在输入缓冲区中
对于string对象,有两种方式:
string stuff;
cin>>stuff; //只读一个单词
getline(cin,stuff); //读一行,无视换行符
两个getline()都有一个可选参数,用于指定使用哪个字符来确定输入的边界:
cin.getline(info,100,':'); //读到:为止,并抛弃:
getline(cin,stuff,':'); //读到:为止,并抛弃:
string版本的两个函数能够自动调整大小。
string版本的getline()函数从输入中读取字符,并将其存储到目标string中,直到发生下列三种情况之一:
(1)到达文件尾,这意味着方法fail()和eof()都将返回true
(2)遇到分界字符(默认为\n),这种情况下将把分解字符从输入流中删除,但不储存
(3)读取的字符数达到最大允许值(string::npos和可供分配的内存字节数中较小的一个),在这种情况下,fail()将返回true
C++对6个关系运算符进行了重载,使得string对象与另一个string对象或是C风格字符串进行比较,比较的是按照ASCII的字典序的大小。
可以通过length()和size()都可以返回字符串的长度。
可以以多种不同的方式在字符串中搜索给定的子字符串或字符。string::npos表示字符串可存储的最大字符数,通常是无符号int或无符号long的最大取值。
方法原型 | 描述 |
---|---|
size_type find(const string &str,size_type pos=0)const | 从pos位置开始查找子字符串str,如果找到返回首次出现的首字符索引,否则返回string::npos |
size_type find(const char *s,size_type pos=0)const | 从pos位置开始查找子字符串s,如果找到返回首次出现的首字符索引,否则返回string::npos |
size_type find(const char *s,size_type pos=0,size_type n)const | 从pos位置开始查找子字符串s的前n个字符组成的字符串,如果找到返回首次出现的首字符索引,否则返回string::npos |
size_type find(char ch,size_type pos=0)const | 从pos位置开始查找字符ch,如果找到返回首次出现的位置,否则返回string::npos |
string库还提供了相关的方法:rfind()查找子字符串或字符最后一次出现的位置,find_first_of()查找参数中任何一个字符首次出现的位置,find_last_of()查找参数中任何一个字符最后一次出现的位置,find_first_not_of()查找字符串中第一个不包含在参数中的字符,find_last_not_of()也类似。它们的重载函数特征标都和find相同。
调用c_str()方法能让string对象返回一个指向C风格字符串的指针。
智能指针模板类
三个智能指针模板(auto_ptr,unique_ptr,shared_ptr)都定义了类似指针的对象,可以将new获得的地址赋给这种对象。如果将new返回的地址赋给这些对象,将无需记住稍后释放这些内存,当智能指针过期时,这些内存将自动释放。
void demo(){
auto_ptr<double> ap(new double);
*ap=25.5;
return;
}
其他两种智能指针使用同样的语法。
string vacation("I wandered lonely as a cloud.");
shared_ptr<string> pvac(&vacation);
//不要这么做,pvac过期时,会delete掉vacation,这个字符串就不能使用了
看下面一个例子:
auto_ptr<string> ps (new string("I reigned lonely as a cloud.");
auto_ptr<string> vocation;
vocation=ps;
如果这是两个普通指针,这会导致ps和vocation指向同一个字符串对象,因此这个字符串会被delete两次。而上述代码会导致ps的所有权转让给了vocation。
有三种方法:
(1)定义赋值运算符,使之执行深复制。
(2)对于特定的对象,只能有一个智能指针可拥有它,而赋值操作符相当于转让所有权,这是unique_ptr和auto_ptr的策略。
(3)跟踪引用特定对象的智能指针数,赋值时计数+1,过期时计数-1,最后一个指针过期时,调用delete,这是shared_ptr的策略。
unique_ptr比auto_ptr更优,原因是它能在上面例子中“vocation=ps”这句语句上报出编译错误,而不是在运行时出错。另一个好处是,unique_ptr可以调用delete和delete[],而auto_ptr只能调用delete。
标准模板库
先介绍一下vector类。
在头文件vector中定义了一个vector模板,要创建vector模板对象,可使用通常的表示法来指出要使用的类型。另外vector模板使用动态内存分配,因此可以使用初始化参数来指出需要的大小。
#include<vector>
using namespace std;
vector<int> ratings(5);
int n=5;
vector<double> scores(n);
由于[]已经被重载,因此可以使用通常的数组表示法来访问各个元素。
size()方法返回容器中元素个数,swap()交换两个容器的内容,begin()返回一个指向容器中第一个元素的迭代器,end()返回一个表示超过容器尾的迭代器。
迭代器实际上是一个广义指针。迭代器的类型时一个名为iterator的typedef,作用域为整个类。
vector<double>::iterator pd;
vector<double> scores;
pd=scores.begin();
*pd=22.3;//第一个元素修改值
++pd;//pd指向第二个元素
for (pd=scores.begin();pd!=scores.end();pd++){
cout<<*pd<<endl;
}
vector类可以使用push_back()方法,它能将元素添加到容器末尾。这样做时,容器将负责内存管理,增加自身的长度。
erase()方法删除给定区间的元素。它接受两个迭代器参数。第一个参数指向区间的起始处,第二个参数指向区间终止处的后一个元素。也就是区间为左闭右开。
insert()方法接受三个迭代器参数,第一个参数指定了新元素插入的位置,第二个和第三个迭代器参数定义了被插入区间,该区间通常为另一个容器的一部分。当然第一个参数可以是end()方法返回的迭代器位置,这样相当于在最后接上一段。
下面介绍三个更广泛的STL函数:
for_each()函数接受3个函数,前两个是定义容器区间的迭代器,最后一个是函数对象。for_each()函数将被指向的函数应用于容器区间中的每个元素。被指向的函数不能修改容器元素的值。
vector<Review>::iterator pr;
for (pr=books.begin();pr!=books.end();pr++){
ShowReview(*pr);
}
替换为:
for_each(books.begin(),books.end(),ShowReview);
Random_shuffle()函数接受两个指定区间的迭代器参数,并随机排列该区间中的元素。该函数要求容器能够随机访问,vector可以做到。
sort()函数也要求容器支持随机访问。该函数有两个版本,第一个版本接受两个定义区间的迭代器参数,并使用为存储在容器中的类型元素定义的<运算符。如果容器元素是用户定义的对象,则必须定义能够处理该类型对象的operator<()函数。
另一种格式的sort()接受三个参数,前两个是指定区间的迭代器,最后一个参数是指向要使用的函数的指针(函数对象),而不是operator<()。返回值可转换为bool,false代表顺序不正确。
bool WorseThan(const Review & r1,const Review & r2){
if (r1.rating<r2.rating) return true;else return false;
}
sort(books.begin(),books.end(),WorseThan);
泛型编程
泛型编程旨在编写独立于数据类型的代码。
理解迭代器是理解STL的关键所在。迭代器是遍历容器中值的通用表示。
如下是迭代器的部分特征:
(1)应能够对迭代器执行解除引用的操作。
(2)应能够将迭代器赋给另一个。
(3)应能够将一个迭代器与另一个进行比较,看是否相等。
(4)应能够使用迭代器遍历容器中所有元素。
使用容器类时,不用知道其迭代器是如何实现的,也无需知道超尾是如何实现的。
下面介绍5种不同的迭代器,这5种迭代器都可以执行解除引用操作,也可进行比较。
1.输入迭代器
输入迭代器可被程序用来读取容器中的信息,却不一定可以让程序修改值。输入迭代器必须能访问容器中所有值,这通过++(前缀和后缀)来实现。
基于输入迭代器的任何算法都应当是单通行的,不依赖前一次遍历的迭代器值,也不依赖本次遍历中前面的迭代器值。
输入迭代器是单向迭代器,可以递增,不能倒退。
2.输出迭代器
输出迭代器只是解除引用让程序能修改容器值,而不能读取。它也是单通行的。
3.正向迭代器
正向迭代器只使用++来遍历容器。将正向迭代器递增后,仍然可以对前面的迭代器值解除引用,是多通行的。正向迭代器既可以读也可以写,如果加上const修饰符则变成只读。
4.双向迭代器
双向迭代器具有正向迭代器的所有特性,同时支持递减运算符。
5.随机访问迭代器
直接跳到容器中的任何一个元素,这叫随机访问。随机访问迭代器拥有双向迭代器的所有特性,同时添加了支持随机访问的操作和用于对元素进行排序的关系运算符。
它支持a+n,a-n,r+=n等操作。
在编写算法时应尽可能使用要求最低的迭代器,并让它适用于容器的最大区间。各种迭代器的类型不是确定的,而是一种概念性描述。
下面讨论容器。
容器是存储其他对象的对象。被存储的对象必须是同一类型的,可以是OOP的对象,也可以是内置类型。存储在类型中的数据类型必须是可复制构造的和可赋值的。
常用的容器有序列(vector,deque,list,queue,priority_queue,stack,array),关联容器(set,multiset,map,multimap)。这里就不详细展示了。偷懒
没错就是烂尾了,如果有时间这个系列补一补吧