1 #include <iostream> 2 #include <string> 3 4 int main() 5 { 6 using namespace std; 7 8 cout << "1 --- string(const char* s):将string对象初始化为s指向的C风格字符串" << endl; 9 string one("benxintuzi_1"); 10 cout << "one = " << one << endl; // 重载了 << 11 12 cout << "2 --- string(size_type n, char c):以字符c初始化包含n个字符的string对象" << endl; 13 string two(20, '$'); 14 cout << "two = " << two << endl; 15 16 cout << "3 --- string(const string& str):复制构造函数初始化" << endl; 17 string three(one); 18 cout << "three = " << three << endl; 19 20 cout << "4 --- string():默认构造函数" << endl; 21 string four; 22 four += one; // 重载了 += 23 four = two + three; // 重载了 + 24 cout << "four = " << four << endl; 25 26 cout << "5 --- string(const char* s, size_type n):用s指向的C风格字符串的前n个字符初始化string对象" << endl; 27 char s[] = "benxintuzi"; 28 string five(s, 5); 29 cout << "five = " << five << endl; 30 31 cout << "6 --- string(Iter begin, Iter end):用[begin, end)区间内的字符初始化string对象" << endl; 32 string six(s + 2, s + 5); 33 cout << "six = " << six << endl; 34 35 cout << "7 --- string(const string& str, string size_type pos = 0, size_type n = npos):将string对象初始化为str中从pos开始的n个字符" << endl; 36 string seven(four, 5, 2); 37 cout << "seven = " << seven << endl; 38 39 /* 40 cout << "8 --- string(string&& str) noexcept:将string对象初始化为str,并可能修改str,移动构造函数[C++11新特性]" << endl; 41 42 cout << "9 --- string(initializer_list<char>il:将string对象初始化为初始化列表il中的字符[C++11新特性]" << endl; 43 string nine = {'t', 'u', 'z', 'i'}; // or string nine{'t', 'u', 'z', 'i'}; 44 cout << "nine = " << nine << endl; 45 */ 46 }
对于 C 风格字符串,有3种输入方式ios |
char info[100];算法 cin >> info; // 从流中读一个单词存放到info中数组 cin.getline(info, 100); // 从流中读入一行存放到info,删除流中的\n安全 cin.get(info, 100); // 从流中读入一行存放到info,保留流中的\nide |
对于 string 对象,有2种输入方式函数 |
string stuff;spa cin >> stuff; // 从流中读取一个单词操作系统 getline(cin, stuff); // 从流中读取一行,删除流中的\n指针 |
getline能够指定用于分割单词的分隔符,将其放到第三个参数中,如:code
cin.getline(info, 100, ‘:’); // C 风格字符串使用这种形式
getline(stuff, ‘:’); // string 类使用这种形式
string与传统 C 风格字符串的比较:
使用string类输入时,不须要具体指定输入的字符个数,其能够自动匹配字符串大小,使用C风格字符串必须显示指定,由于C风格字符串使用的是istream类的方法,而string版本使用的是独立的函数,所以形式上有所区别,这种区别在其余运算符上也有体现,如>>操做符:
C 风格字符串使用:cin.operator>>(fname),而string风格为:operator(cin, fname);
说明:
string版本的getline()在以下三种状况下结束读取:
1 到达文件末尾:此时输入流中的eofbit置位;
2 遇到分割符:默认为\n,此时,删除流中的\n;
3 读取的字符个数达到最大值[min(string::npos, 空闲内存)],此时将输入流的failbit置位。
在输入流中有一个统计系统,用于跟踪流的状态:
1 检测到文件末尾,设置eofbit寄存器;
2 检测到错误,设置failbit寄存器;
3 检测到没法识别的故障,设置badbit寄存器;
4 检测到一切顺利,设置goodbit寄存器。
string 类对所有 6 个关系运算符进行了重载,对于每一个关系运算符,又进行了3种重载,分别是:
string 对象与 string 对象;
string 对象 C 风格字符串;
C 风格字符串与 string 对象。
智能指针是行为相似于指针,但倒是类。其能够帮助管理动态分配的内存,有三种可选:分别为auto_ptr\unique_ptr\shared_ptr。auto_ptr是C++ 98提供的,C++ 11已经抛弃了(虽然已经被抛弃,可是仍然被大量使用)。
小知识: 为什么新标准要放弃auto_ptr?理由以下: 假设以下赋值: auto_ptr<string> ps1(new string(“benxintuzi”)); auto_ptr<string> ps2; ps2 = ps1; 若是ps2和ps1是普通指针,那么他们将同时指向一块堆内存,那么auto_ptr类型的指针若同时指向一块堆内存,那么就会调用两次delete,这个太可怕了,所以,auto_ptr采用的策略是若是发生赋值,那么就会令ps1为0,一样unique_ptr也是采用这种策略,可是更加严格。 以下,当ps2 = ps1,即ps1置空时,若是发生调用*ps1,那么至关于调用了一个空指针指向的对象,在auto_ptr状况下,能够编译经过,在运行时会出错;但在unique_ptr状况下,不会让你经过编译的。 结论就是unique_ptr比auto_ptr更安全一些。
shared_ptr对于每一个堆对象,其维护一个指向同一对象的引用计数,只有当引用计数为0时才调用delete,所以能够很好地支持智能指针赋值操做。 |
要建立智能指针,必须包含头文件memory,而后使用模板语法实例化所需类型的指针,例如auto_ptr包含以下构造函数:
template<class X> class auto_ptr
{
public:
// throw()意味着不引起异常【C++ 11也将throw()抛弃了】
explicit auto_ptr(X* p = 0) throw();
...
}
所以,智能指针的使用很是简单,只须要用特定类型的指针初始化便可,如:
auto_ptr<double> pd(new double);
说明:
智能指针都放在名称空间std中(注意shared_ptr和unique_ptr都是C++ 11新增的,旧时的编译器可能不支持)。
1 #include <iostream> 2 #include <string> 3 #include <memory> 4 5 class Report 6 { 7 public: 8 Report(const std::string s) : str(s) 9 { 10 std::cout << "Object created!" << std::endl; 11 } 12 ~Report() 13 { 14 std::cout << "Object deleted!" << std::endl; 15 } 16 void comment() const 17 { 18 std::cout << "str = " << str << std::endl; 19 } 20 21 private: 22 std::string str; 23 }; 24 25 int main() 26 { 27 std::auto_ptr<Report> pa(new Report("using auto_ptr")); 28 pa->comment(); 29 30 std::shared_ptr<Report> ps(new Report("using shared_ptr")); 31 ps->comment(); 32 33 std::unique_ptr<Report> pu(new Report("using unique_ptr")); 34 pu->comment(); 35 36 return 0; 37 } 38 39 /** output */ 40 Object created! 41 str = using auto_ptr 42 Object created! 43 str = using shared_ptr 44 Object created! 45 str = using unique_ptr 46 Object deleted! 47 Object deleted! 48 Object deleted!
注意:
绝对不要将非堆内存指针赋予智能指针,不然虽然能够经过编译,可是状况并不是老是乐观的,这就至关于delete了非堆内存,容易引起难以发现的问题。
关于如何选择合适的智能指针,以下给出参考建议:
若是须要多个指向同一堆对象的有效指针,那么选择使用shared_ptr。这种状况包括:
1 有一个指针数组,而且使用一些辅助指针标识特殊元素,如标识最值元素;
2 两个对象中都包含指向第三个对象的指针;
3 STL容器中的指针等,不少STL算法都支持复制和赋值操做。
若是程序不须要多个有效指针同时指向同一堆对象,那么使用unique_ptr。若是须要将unique_ptr做为右值时,可将其赋值给shared_ptr;在知足unique_ptr条件时,也可以使用auto_ptr,但unique_ptr彷佛是更好的选择(若是编译器不提供unique_ptr,可使用boost库提供的scoped_ptr,其功能与unique_ptr相似)。
迭代器就是广义的指针,可对其进行递增操做和解引用操做等的对象。每一个容器类都定义了一个与之相关的迭代器,该迭代器是一个名为iterator的typedef定义,做用域为整个类。模板使算法独立于数据类型,而迭代器使算法独立于容器类型。
迭代器主要用于遍历容器中的元素,其应该具备以下基本功能:
1 支持解引用操做
2 赋值操做
3 比较操做,如 ==、!=
4 自增操做
具有以上能力就差很少了。其实STL按照迭代器功能强弱定义了多种级别(5种)的迭代器,说明以下:
迭代器类型 |
说明 |
输入迭代器 |
【读容器】可来读取容器中的元素,但可能不会修改容器中的元素。输入迭代器必须能够访问容器中的全部元素,所以,支持++操做。 |
输出迭代器 |
【写容器】程序能够修改容器中的元素值,但不能读取容器中的元素。 |
正向迭代器 |
【读或者写容器】单向读写。 |
双向迭代器 |
【双向读或者写容器】双向读写。支持自减运算符。 |
随机访问迭代器 |
【双向读或者写容器】提供了附加的“跳步”访问能力。 |
迭代器支持的操做含义:
表达式 |
说明 |
a + n、n + a |
指向a所指元素后的第n个元素 |
a - n |
指向a所指元素前的第n个元素 |
r += n、r -= n |
r = r + n、r = r – n |
a[n] |
*(a + n) |
b - a |
a ~ b区间的元素个数 |
a < b、a > b、a >= b、a <= b |
逻辑判断 |
STL迭代器功能汇总:
总结:
迭代器支持的一般操做为解引用读或者写;全部类型的迭代器都支持自增操做;自减操做只有双向迭代器和随机访问迭代器支持;随机访问迭代器为“跳步”访问提供了可能。所以迭代器能力大小能够表示以下:
随机访问迭代器 > 双向迭代器 > 正向迭代器 > 输入迭代器 == 输出迭代器
迭代器是广义的指针,而指针知足全部迭代器的要求。所以STL算法可使用指针来对基于指针的非STL容器进行操做。例如,可将STL算法用于数组:
若是要将一个double Receipts[100]进行排序,可使用STL的算法sort,但传入的确是指针,如:
sort(Receipts, Receipts + 100);
STL提供了一些预约义的迭代器:copy()、ostream_iterator、istream_iterator。
假设有以下定义:
int casts[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int> dice(10);
copy(casts, casts + 10, dice.begin()); // 将[casts, casts + 10)范围内的数据复制到dice.begin()开始的目标空间中
输出流迭代器:
假设如今须要将数据复制到显示器上,那么须要一个表示输出流的迭代器,STL为咱们提供了ostream_iterator模板。该迭代器是一个适配器,可将全部的接口转换为STL使用的接口。咱们使用时必须包含头文件iterator,而且作出以下声明来建立该迭代器:
#include <iterator>
...
ostream_iterator<int, char> out_iter(cout, “ ”);
说明:
int: 表示发送到输出流中的数据类型;
char: 表示输出流使用的字符类型;
构造函数中,cout: 表示要使用的输出流;“ ”:表示所发送数据的分隔符;
能够这样使用迭代器:
*out_iter++ = 15; // 等价于cout << 15 << “ ”;
这条语句代表将”15 ”赋予指针指向的位置,而后指针向前移动一个单位。
实现如上的copy动做为:
copy(dice.begin(), dice.end(), out_iter)便可将dice容器的整个区间复制到显示器中显示。
固然也能够建立匿名的迭代器,以下:
copy(dice.begin(), dice.end(), ostream_iterator<int, char>(cout, “ ”));
输入流迭代器:
对于的输入流迭代器为istream_iter模板:
copy(istream_iterator<int, char>(cin), istream_iterator<int, char>(), ostream_iterator<int, char>(cout, “ ”));
说明:
int:要读取的数据类型;
char:输入流使用的数据类型;
构造函数中,第一个参数cin表示读取由cin管理的输入流;省略构造函数意味着输入失败,所以上述代码从输入流中读取int型数据,直到文件末尾、类型不匹配或者出现其余输入故障为止才输出到屏幕上。
1 #include <iostream> 2 #include <iterator> 3 using namespace std; 4 5 int main() 6 { 7 copy(istream_iterator<int, char>(cin), istream_iterator<int, char>(), 8 ostream_iterator<int, char>(cout, " ")); 9 return 0; 10 } 11 12 /** output */ 13 1 2 3 4 5 14 1 2 3 4 5 __
除了istream_iterator和ostream_iterator外,头文件iterator中还提供了一些专用的迭代器,如:reverse_iterator、back_insert_iterator、front_insert_iterator和insert_iterator。
reverse_iterator执行递增时,指针实际上发生递减操做。例如:vector类中的rbegin()和rend()分别返回指向最后一个元素下一位置的指针、指向第一个元素的指针。
强调:
虽然rbegin()和end()返回的指针虽然指向同一个位置,可是类型不一样,前者类型是reverse_iterator,后者类型是iterator。
一样要注意的是:对于反向迭代的解引用的具体实现实则是先递减,再解引用。不然会出错。试想一下,若是一个rp是rbegin()返回的,直接解引用将致使操做一个空指针。
1 #include <iostream> 2 #include <iterator> 3 #include <vector> 4 using namespace std; 5 6 /* 7 int main() 8 { 9 copy(istream_iterator<int, char>(cin), istream_iterator<int, char>(), 10 ostream_iterator<int, char>(cout, " ")); 11 return 0; 12 } 13 */ 14 15 int main() 16 { 17 int casts[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 18 vector<int> dice(10); 19 copy(casts, casts + 5, dice.begin()); // 将casts的前5个数据复制到dice中 20 21 ostream_iterator<int, char> out_iter(cout, " "); // 建立输出流迭代器 22 copy(dice.begin(), dice.end(), out_iter); // 将dice中的所有元素复制到屏幕上输出 23 24 cout << endl; 25 26 /* 隐式使用reverse_iterator */ 27 copy(dice.rbegin(), dice.rend(), out_iter); // 将dice中的所有元素反向输出到屏幕上 28 29 cout << endl; 30 31 /* 显式使用reverse_iterator */ 32 vector<int>::reverse_iterator ri; 33 for(ri = dice.rbegin(); ri != dice.rend(); ++ri) 34 cout << *ri << " : "; 35 cout << endl; 36 37 return 0; 38 } 39 40 /** output */ 41 1 2 3 4 5 0 0 0 0 0 42 0 0 0 0 0 5 4 3 2 1 43 0 : 0 : 0 : 0 : 0 : 5 : 4 : 3 : 2 : 1 :
在形式上,STL中的不少算法都和copy函数相似,以下为三种插入迭代器操做:
back_insert_iterator将元素插入容器尾部;
front_insert_iterator将元素插入容器头部;
insert_iterator将元素插入指定位置。
插入操做相比于copy操做而言,能够自动增长空间,而copy确是静态分配好的空间,即便不够了也不会向操做系统去申请,默认状况下会截断后边的数据,只复制容量容许的数据。使用插入迭代器时,使用容器类型做为模板参数,使用容器变量做为构造参数,以下所述:
back_insert_iterator<vector<int>> back_iter(dice);
对于insert_iterator,还须要一个指定插入位置的构造参数:
insert_iterator<vector<int>> insert_iter(dice, dice.begin());
容器用于存储对象,其要求所存储的对象必须具备相同的类型,并且该类型必须是可复制构造和可赋值的。通常基本类型和类类型都知足要求(固然除非你把这两种函数声明为私有或保护类型的)。C++ 11中又添加了可复制插入和可移动插入要求。
容器类型是用于建立具体容器的模板。以前共有11个容器类型:deque/list/queue/priority_queue/stack/vector/map/multimap/set/multiset/biset,C++ 11新增了5个:forward_list/unordered_map/unordered_multimap/unordered_set/unordered_multiset。
何为序列?
序列保证了元素将按特定的顺序排列,不会在两次迭代之间发生变化。序列还要求其元素严格按线性顺序排列,即存在第一个/第二个/.../等。好比数组和链表都是序列,但分支结构就不是序列。
因为序列中的元素具备特定的顺序,所以能够执行像插入元素到特定位置、删除特定区间等操做。以下容器都为序列容器:
vector |
vector是数组的一种类的表示,它提供了自动管理内存的功能,能够动态改变vector的长度,增大或减少。vector是能够反转的,主要是经过rbegin()和rend()实现的,而且其返回的迭代器类型都是reverse_iterator。 |
deque |
双端队列。其实现相似于vector,支持随机访问。主要区别在于能够从deque中开头和结尾处插入和删除元素,并且其时间复杂度固定。 |
list |
双向链表。与vector相似,list也能够反转。主要区别是:list不支持随机访问。除此以外,list模板类还包括了链表专用的成员函数,以下所示: void merge(list<T, Alloc>& x): 将链表x与调用链表合并,而且已然有序。合并后的链表保存在调用链表中,x为空。 void remove(const T& val): 从链表中删除val的全部实例。 void sort(): 使用<运算符对链表进行排序,时间复杂度为nlgn。 void splice(iterator pos, list<T, Alloc>x): 将链表x的内容插入到pos前边,插入后x为空。 void unique(): 将链表中连续的相同元素压缩为单个元素。 说明: insert()和splice()之间的主要区别在于:insert是插入原始区间的副本到目标地址,而splice是将原始区间转移到目标地址。 C++ 11新增了容器类forward_list,实现了单链表,与之关联的迭代器是正向迭代器而非双向迭代器。 |
queue |
queue模板类是一个适配器类,如前所述,ostream_iterator模板也是一个适配器,可让输出流使用迭代器接口,一样,queue模板类让底层类(默认为deque)展现出队列接口。 queue模板的限制比deque更多。他不只不容许随机访问,并且不容许遍历操做。其把使用限制在队列基本操做上,如入队、出队、取队首、判队空等,具体以下: bool empty() const: 若是队列为空则返回true;不然返回false。 size_type size() const: 返回队列中元素的数目。 T& front(): 返回指向队首元素的引用。 T& back(): 返回指向队尾元素的引用。 void push(const T& x): 在队尾插入x。 void pop(): 删除队首元素。 说明: priority_queue与queue的主要区别在于:最大元素被移到队首。其内部实现为vector,能够指定内部元素排序规则。 |
stack |
适配器类,使用vector实现,支持操做以下: bool empty() const: 若是栈为空则返回true;不然返回false。 size_type size() const: 返回栈中元素的数目。 T& top(): 返回指向栈顶元素的引用。 void push(const T& x): 在栈顶插入x。 void pop(): 删除栈顶元素。 |
array(C++ 11 新增) |
模板类array并不是STL容器,由于其长度是固定的。所以不能使用动态调整容器大小的函数如push_back()和insert()等,可是其定义了颇有用的成员函数,如operator[]()和at(),正如以前所述,许多标准的STL算法也可用于array对象,如:copy()和for_each()。 |
操做示例:
1 #include <iostream> 2 #include <list> 3 #include <iterator> 4 #include <algorithm> 5 using namespace std; 6 7 void PrintList(int n){ cout << n << " "; } 8 9 int main() 10 { 11 list<int> one(5, 2); // 5个2 12 cout << "list one: "; 13 for_each(one.begin(), one.end(), PrintList); 14 cout << endl; 15 16 list<int> two; 17 int stuff[3] = {2, 5, 9}; 18 two.insert(two.begin(), stuff, stuff + 3); 19 cout << "list two: "; 20 for_each(two.begin(), two.end(), PrintList); 21 cout << endl; 22 23 list<int> three(two); 24 three.insert(three.end(), one.begin(), one.end()); 25 cout << "list three: "; 26 for_each(three.begin(), three.end(), PrintList); 27 cout << endl; 28 29 cout << "merge one and three: "; 30 three.splice(three.begin(), one); 31 for_each(three.begin(), three.end(), PrintList); 32 cout << endl; 33 34 cout << "delete the duplicate elements: "; 35 three.unique(); 36 for_each(three.begin(), three.end(), PrintList); 37 cout << endl; 38 39 cout << "merge with another: four = "; 40 list<int> four(3, 8); 41 four.merge(three); 42 for_each(four.begin(), four.end(), PrintList); 43 cout << endl; 44 45 cout << "remove the value 8 in four: "; 46 four.remove(8); 47 for_each(four.begin(), four.end(), PrintList); 48 cout << endl; 49 50 return 0; 51 } 52 53 /** output */ 54 list one: 2 2 2 2 2 55 list two: 2 5 9 56 list three: 2 5 9 2 2 2 2 2 57 merge one and three: 2 2 2 2 2 2 5 9 2 2 2 2 2 58 delete the duplicate elements: 2 5 9 2 59 merge with another: four = 2 5 8 8 8 9 2 60 remove the value 8 in four: 2 5 9 2