string类
- C++11新增的构造函数
- 构造函数
string(string &&str)
- 类似于复制构造函数,导致新创建的
string
为str
的副本。但与复制构造函数不同的是,它不保证将str
视为const
。这种构造函数被称为移动构造函数。在有些情况下,编译器可使用它而不是复制构造函数,以优化性能。
- 类似于复制构造函数,导致新创建的
- 构造函数
string(initializer_list<char>il)
-
使得能够将列表初始化语法用于
string
类。string piano_man={'L','i','s','z','t'};
-
- 字符串种类
-
string
库实际上是基于一个模板类的具体化。template<class charT,class traits=char _traits<charT>,class Allocator=allocator<charT> > basic_string{...};
-
模板
basic_string
有4个具体化,每个具体化都有一个typedef
名称:typedef basic_string<char> string; typedef basic_string<wchar_t> wstring; typedef basic_string<char16_t> u16string;//c++11 typedef basic_string<char32_t> u32string;//c++11
-
- 构造函数
智能指针模板类
-
其作用为帮助管理动态内存分配,在函数终止后(不管是正常终止,还是由于出现了异常而终止),智能指针就会自动删除其指向的内存。
-
使用方法
- 包含头文件
memory
; - 将指向
xxx
的指针替换为指向xxx
的智能指针对象; - 删除
delete
语句
- 包含头文件
-
一个例子
#include<memory> using namespace std; void remodel(string &str) { auto_ptr<string>ps(new string(str)); if(weird_thing()) throw exception(); str=*ps; //delete ps; NO LONGER NEEDED return; }
-
有关智能指针的注意事项
-
一个例子
auto_ptr<string>ps(new string("hello")); auto_ptr<string>vocation; vocation=ps;
-
如果
ps
和vocation
是常规指针,则两个指针将指向同一个string
对象。这是不能接受的,因为程序将试图删除同一个对象两次——一次是ps
过期时,另一个是vocation
过期时。要避免这种问题,方法有多种。- 定义赋值运算符,使之执行深复制。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的副本。
- 建立所有权(ownership)概念,对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的构造函数会删除该对象。然后,将赋值操作转让所有权。这就是用于
auto_ptr
和unique_ptr
的策略。但unique_ptr
的策略更严格。 - 创建智能更高的指针,跟踪引用特定对象的智能指针数。这被称为引用计数(reference counting)。例如,赋值时,计数将加1,而指针过期时,计数将减1。仅当最后一个指针过期时,才调用
delete
。这是shared_ptr
采用的策略。
-
一个例子
#include<bits/stdc++.h> using namespace std; int main() { auto_ptr<string> films[3]= { auto_ptr<string>(new string("one")), auto_ptr<string>(new string("two")), auto_ptr<string>(new string("three")) }; auto_ptr<string>pwin; pwin=films[1]; for(int i=0;i<3;i++) cout<<*films[i]<<endl; } /* output: one Segmentation fault(core dumped) */
将
auto_ptr
改成unique_ptr
后,编译器则会出现错误。
-
-
uniqu_ptr
为何优于auto_ptr
unique_ptr<string> p3(new string("auto")); unique_ptr<string>p4; p4=p3;//not allowed
编译器将会认为这种语句非法,避免了p3不再指向有效数据的问题。因此,
unique_ptr
比auto_ptr
更安全(编译阶段错误比潜在的程序崩溃更安全)
但有时候,将一个智能指针赋给另一个并不会留下危险的悬挂指针。unique_ptr<string> demo(const char *s) { unique_ptr<string>temp(new string(s)); return temp; } unique_ptr<string>ps=demo("hello");
demo()
返回一个临时的unique_ptr
。然后让ps接管了原本归返回的unique_ptr
所有的对象,而返回的unique_ptr
被销毁。
总之,如果源unique_ptr
是一个临时右值,编译器允许这么做;如果源unique_ptr
将存在一段时间,编译器将禁止这么做。
unique_str
还有一个优点,即可以使用new[]
和delete[]
的版本:unique_ptr< double[]>pda(new double[5]);//will use delete []
-
选择智能指针
- 如果程序要使用多个指向同一个对象的指针,应选择
shared_ptr
- 否则选择
unique_ptr
- 写函数的时候,参数列表不能值传递智能指针
unique_ptr
,必须使用引用传递
- 如果程序要使用多个指向同一个对象的指针,应选择
泛型编程
- 迭代器类型
迭代器功能 | 输入 | 输出 | 正向 | 双向 | 随机访问 |
---|---|---|---|---|---|
解除引用读取 | 有 | 无 | 有 | 有 | 有 |
解除引用写入 | 无 | 有 | 有 | 有 | 有 |
固定和可重复排序 | 无 | 无 | 有 | 有 | 有 |
++i i++ | 有 | 有 | 有 | 有 | 有 |
–i i– | 无 | 无 | 无 | 有 | 有 |
i[n] | 无 | 无 | 无 | 无 | 有 |
i+n | 无 | 无 | 无 | 无 | 有 |
i-n | 无 | 无 | 无 | 无 | 有 |
i+=n | 无 | 无 | 无 | 无 | 有 |
i-=n | 无 | 无 | 无 | 无 | 有 |
-
输出流迭代器
ostream_iterator
模板#include<iterator> ... ostream_iterator<int,char> out_iter(cout," ");
out_iter
迭代器现在是一个接口,使得能够使用cout
来显示信息。第一个模板参数(这里是int)指出了被发送给输出流的数据类型;第二个模板参数(这里是char)指出了输出流使用的字符类型(另一个可能的值是wchar_t
).构造函数的第一个参数(这里是cout
)指出了要使用的输出流,它也可以是用于文件输出的流;最后一个字符串参数是在发送给输出流的每个数据项后显示的分隔符。copy(dice.begin(),dice.end(),out_iter);
同理还有一个输入流迭代器
istream_iterator
与之类似。 -
反向迭代器
rbegin()
和rend()
-
rbegin()
和end()
返回相同的值(超尾),但类型不同。同样rend()
和begin()
也返回相同的值(指向第一个元素的迭代器),但类型不同。 -
反向指针先递减,再解除引用
#include<bits/stdc++.h> using namespace std; int main() { int casts[10]={4,2,5,6,2,3,4,3,4,5}; vector<int>dice(10); copy(casts,casts+10,dice.begin()); ostream_iterator<int,char>out_iter(cout," "); copy(dice.begin(),dice.end(),out_iter); cout<<endl; copy(dice.rbegin(),dice.rend(),out_iter); cout<<endl; vector<int>::reverse_iterator ri; for(ri=dice.rbegin();ri!=dice.rend();++ri) cout<<*ri<<' '; cout<<endl; } /* output: 4 2 5 6 2 3 4 3 4 5 5 4 3 4 3 2 6 5 2 4 5 4 3 4 3 2 6 5 2 4 */
-
-
插入迭代器
-
插入新添加的元素,而不会覆盖已有的数据。并使用自动内存分配来确保能够容纳新的信息。
-
back_insert_iterator
将元素插入到容器尾部 -
front_insert_iterator
将元素插入到容器前端 -
insert_iterator
将元素插到insert_iterator
构造函数的参数指定的位置前面 -
一些限制
back_insert_iterator
只能用于允许在尾部快速插入的容器(时间固定)front_insert_iterator
只能用于允许在起始位置快速插入的容器(时间固定)insert_iterator
没有限制,因此可以用它把信息插入到vector
的前端,然而,front_insert_iterator
对于那些支持它的容器来说,完成任务的速度更快
-
一个例子
#include<bits/stdc++.h> using namespace std; void show(const string &s){cout<<s<<' ';} int main() { string s1[4]={"hello","world","fish","fashion"}; string s2[2]={"busy","bats"}; string s3[2]={"silly","singers"}; vector<string>words(4); copy(s1,s1+4,words.begin()); for_each(words.begin(),words.end(),show); cout<<endl; copy(s2,s2+2,back_insert_iterator<vector<string> >(words)); for_each(words.begin(),words.end(),show); cout<<endl; copy(s3,s3+2,insert_iterator<vector<string> >(words,words.begin())); for_each(words.begin(),words.end(),show); cout<<endl; } /* output: hello world fish fashion hello world fish fashion busy bats silly singers hello world fish fashion busy bats */
容器种类
- 存储在容器中的数据为容器所有,这意味着当容器过期时,存储在容器中的数据也将过期(然而,如果数据是指针的话,则它指向的数据并不一定过期)。
- 不能将任何类型的对象存储在容器中,具体地说,类型必须是可赋值构造的和可赋值的。基本类型满足这些要求:只要类定义没有将复制构造函数和赋值运算符声明为私有或保护的,则也满足这种要求。
-
函数对象
- 函数对象也叫函数符。函数符是可以以函数方式与()结合使用的任意对象。这包括函数名,指向函数的指针和重载了()运算符的类对象(即定义了函数
operator()()
的类)。 - 函数符概念
- 生成器(generator)是不用参数就可以调用的函数符
- 一元函数(unary function)是用一个参数可以调用的函数符
- 二元函数(binary function)是用两个参数可以调用的函数符
- 返回
bool
值的一元函数是谓词(predicate) - 返回
bool
值的二元函数是二元谓词(binary predicate)
- 一个例子
#include<bits/stdc++.h> using namespace std; bool cmp(int x,int y) { return x>y; } void show(int x){cout<<x<<endl;} int main() { int a[5]={2,4,1,3,5}; //list<int>b(a,a+5); sort(a,a+5,cmp);//二元谓词 for_each(a,a+5,show);//一元函数 }
- 预定义的函数符
-
一个例子
#include<bits/stdc++.h> using namespace std; int main() { plus<double>add; double y=add(2.2,3.4); vector<double>gr8{3.3,4.2,5.5}; vector<double>m8{2.7,3.6,4.3}; ostream_iterator<double,char>out(cout," "); transform(gr8.begin(),gr8.end(),m8.begin(),out,add); cout<<endl; transform(gr8.begin(),gr8.end(),m8.begin(),out,plus<double>());//使用默认构造函数创建一个对象 } /* output: 6 7.8 9.8 6 7.8 9.8 */
-
- 自适应函数符和函数适配器
- 有的时候函数需要接受一个一元函数参数的函数符,但自带函数符为二元函数时,这时候就需要使用
bind1st
和bind2nd
对象#include<bits/stdc++.h> using namespace std; int main() { vector<double>gr8{3.3,4.2,5.5}; vector<double>m8(3); ostream_iterator<double,char>out(cout," "); transform(gr8.begin(),gr8.end(),out,bind1st(multiplies<double>(),2.5)); } /* output: 8.25 10.5 13.75 */
- 有的时候函数需要接受一个一元函数参数的函数符,但自带函数符为二元函数时,这时候就需要使用