一 Auto_ptr的使用
1 auto_ptr 拥有权的转移(auto_ptr要求一个只能有一个对象只能有一个拥有者,严谨一无二主)
Std::auto_ptr<ClassA>ptr1(new ClassA)
Ptr1拥有new出来的对象
Std::auto_ptr<ClassA>ptr2(ptr1)
把控制权交给ptr2此后ptr2就拥有了new出来的对象而ptr1不在拥有它了
2通过赋值实现
Std::auto_ptr<ClassA>ptr1(new ClassA);
Std::auto_ptr<ClassA>ptr2;
Ptr2=ptr1;
通过赋值动作将拥有权从ptr1转移至ptr2于是ptr2拥有了先前被ptr1所拥有的那个对象,如果ptr2被赋值之前正拥有另一个对象,赋值动作发生时会调用delete将该对象删,【先前的ptr1就失去了拥有权拥有权交出后就两双空空只剩null指针在手】
注意2:只有auto_ptr可以拿来当做另一个auto——ptr的初始值,普通指针不行。
3拥有权的转移的用处
某个函数可以利用auto_ptr将拥有权转交给另一个函数:
(1)某个函数是数据的终点如果auto_ptr以传值方式被当做一个参数传给某个函数此时被调用端获得了这个auto_ptr的拥有权如果函数不在将它传递出去,它就会在函数被退出时被删除
Void sink(std::auto_ptr<ClassA>)
(2)某函数是数据的起点当一个auto_ptr被返回,其拥有权便被转交给调用端
(3)通过使用constant reference可以另auto_ptr在向函数传值时无法交出控制权
template<classT>
voidbad_print(std::auto_ptr<T>p)
{
if(p.get()==NULL)
{
cout<<"NULL"<<endl;
}
else
{
std::cout<<*p<<endl;
}
}
Void f()
{
Conststd::auto_ptr<int>p(newint);//定义为const型
*p=42;
bad_print(p);//此处将会编译出错
*p=48;//此处将不会出错
}
关键字const并不意味你不能更改auto_ptr所拥有的对象而是意味着你不能更改auto_ptr的所有权。
4 auto_ptr 的注意事项
(1) auto_ptrs 之间不能共享拥有权
(2) 并不存在针对array而设计的auto_ptrs
(3) Auto_ptrs绝非一个“四海通用”的智能指针
(4) Auto_ptrs不满足STL容器对其他元素的要求
二数值极限(Numeric Limits)
1 C++标准库通过template numeric_limits 提供极值
C语言的整数常量定义于<climits>和<limits.h>浮点常量定义于<cfolat>和folate.h
中内建型别的最小长度:
Char 1byte
Short int 2byte
Int 2byte
Long int 4byte
Float 4byte
Double 8byte
Long double 8byte
2 numeric_limits不但能提供通用型的template还能提供其特化版本
(1) 通用性的template,为所有型别提供缺省极值
(2) 各具体型别的极值,由特化版本提供
(3) Template<>classnumeric_limits<int>
{
Static const bool is_specialized=true;
Static T min() throw()
{
Return -2147483648
}
……….
}
这里把is_specialized 设为true 所有其他成员根据特定型别极值加以设定
使用范例:
cout<<boolalpha<<endl;
cout<<"max(short)"<<numeric_limits<short>::max()<<endl;
cout<<"max(long)"<<numeric_limits<long>::max()<<endl;
cout<<”string is_specialized”<<numeric_limits<string>::is_specialized()<<endl;//是否定义数值极限
三头文件<cstddef>和<cstdlib>
<cstddef>内的各种定义
Null 同时定义在<cstdio><cstdlib><cstring><ctime><cwchar><clocle>
size_t 一种无正负号的型别用来表示大小如元素个数
ptrdiff_t 一种带有正负号的型别用来表示指针的距离
offsetof 表示一个成员在struct或union 中的偏移量
<cstdlib>内的各种定义
EXIT_SUCCESS和EXIT_FAILURE用来当做exit()的参数也可以当做main()的返回值
Eixt(intstatus) 退出(离开exit)程序(并清理static对象)
abort() 退出程序(在某些系统上可能导致崩溃)
atexit(void(*)function())退出程序时调用某函数
五 C++标准模板库(Standard Template Library)
1 STL组建(STL Components)
关键组建:容器,迭代器,算法
STL的基本观念就是将数据和操作分离,数据由容器类加以管理,操作则由可定制的算法定义之,迭代器在两者之间充当粘合剂,使任何算法都可以和任何容器交互运作
2 容器(Containers)和迭代器
迭代器的分类:
1 双向迭代器:
可以双向进行,以递增运算前进或以递减运算符后退(list set multiset map multimap 均提供此类迭代器)
2随机存期迭代器:
随机存取迭代器具备双向迭代器的所有有属性还具备随机访问能力:vector deque string
3之间差异:
For(pos=coll.begin();pos!=coll.end();++pos)
{
…………………
}
For(pos=coll.begin();pos<coll.end();++pos)
………
只有随机访问迭代器才支持operation<<; 所以代码一般会写成第一种
容器相关函数:
reverse()将区间能元素翻转
coll2.resize(coll.size());//改变coll2元素个数
deque<int>coll3(coll1.size());初始化时指明要有的足够空间
六迭代器之配接器:
(一)三种迭代器配接器:
1 Insert iterators(安插型迭代器)
2stream iterators(流迭代器)
3Reverse iterators(逆向迭代器)
安插型迭代器:
Inset iterators 可以使算法以安插的方式而非覆写方式运作可以解决算法的“目标空间不足”的问题是的它会促使目标区间的大小按需要成长
如果对某个元素设值会引发所属群集的安插操作,至于插入位置在容器的最前还是最后或是某个指定位置上,需要视三种情况而定:
(1) 单步前进不造成任何动静
1.1 Back inserters(安插于容器最尾端)
Back inseters 内部调用push_back()在容器尾端插入元素
Copy(coll1.begin(),coll1.end(),back_inserter(coll2)
(只有vector deque list提供push_back())
2.2 Front Inserters(安插于容器最前端)
Copy(coll1.begin(),coll1.end(),front_inserter(coll3));
这种动作逆转了被安插元素的次序如果先安插1再向前安插2那么1会在2后边提供push_front的容器只有:deque list
2.3 Generalinerters(一般性安插器):
一般性的inserters它的作用是将元素插入初始化时接受第二个参数的位置的前方inserters 内部调用成员函数insert()并以新值和新位置做参数所有STL容器都提供insert()函数
(2)Stream Iterator(流迭代器)
copy(istream_iterator<string>(cin),istream_iterator<string>(),back_inserter(coll));
sort(coll.begin(),coll.end());
unique_copy(coll.begin(),coll.end(),ostream_iterator<string>(cout,"\n"));
2.1istream_iterator<string>(cin)
这个产生一个可从标准输入流cin读取数据的streamiterator
2.2 istream_iterator<string>()
调用istreamiterator 的default构造函数产生一个代表”流结束”符号的迭代器它表示你不能在从中读取任何东西
2.3 ostream_iterator<string>(cout,”\n”)
产生一个output stream iterator,透过operator<<向cout写入strings第二个参数作为元素之间分割符
(2) Reverse Iterator(逆向迭代器)
逆向方式进行操作
所有容器都可以通过成员函数rbegin()和rend()产生出reverse iterator
(二)更易型算法(manipulatingalgorithm删除或重排或修改元素的算法)
1 算法remove()自某个区间删除元素
for(inti=1;i<=6;++i)
{
coll.push_front(i);
coll.push_back(i);
}
remove(coll.begin(),coll.end(),3);
结果:654211245656
数值为3删除后被其后元素覆盖,至于群集尾端那些违背覆盖的元素,原封不动但从逻辑上说,那些元素已经不属于这个群集了
改进:
List<int>::iterator end=remove(coll.begin(),coll.end(),3)
copy(coll.begin()end,ostream_iterator<int>(cout,","));
结果:6542112456
或者采用:
Distance(end,coll.end());
Distance()是返回两个迭代器之间的距离
如果真想把删除的元素斩草除根你必须调用该容器的相应成员函数容器提供成员函数erase()适用此目的erase()可以删除“参数所指示区间”内的全部元素
2更易型算法和相关容器
Erase()成员函数返回删除元素的个数
(三)以函数作为算法的参数
void print(intelem)
{
cout<<elem<<" ";
}
for_each(coll.begin(),coll.end(),print);
2判断式(predicate)
所谓predicate就是返回布尔值的函数他们通常用来指定排序规则和搜索准则,并非任何返回bool值得一元函数或二元函数就是合法的predicate STL要求面对相同的值predicate必须得出相同的结果,这条戒律将那些“被调用时会改变自己内部状态的函数排除了”
3仿函数(functors,FunctionObject即函数对象)
传递给算法的“函数参数”不一定是函数,可以是行为类似函数的对象
仿函数的优点:
(1) 智能型含糊 smart point
仿函数可拥有成员函数和成员变量这意味着仿函数拥有状态,其次你可以在初始时初始化他们,当然必须在他们被调用之前
(2) 仿函数都有自己的型别(可以设计函数继承体系)
(3) 仿函数比一般函数要快
预定义的仿函数:
Set<int>coll;会被扩展为set<int,less<int>>coll;
所以反向排列这些元素可以是set<int,greater<int>>coll;
透过一些特殊函数配接器你可以将预定义的仿函数和其它数值组合到一起或使用特殊状况
例如:
Transform(coll.begin(),coll.end(),back_inserter(coll2),bind2d(mltiiplies<int>(),10)
将coll中的所有元素乘以10后安插到coll2中这里使用配接器使得multiple<int>运算时以源群集的元素作为第一个参数,10作为第二个参数
仿函数一般声明为:inline
特殊仿函数:
For_each(coll.begin(),coll.end(),mem_fun_ref(&Person::save());
仿函数mem_fun_ref用来调用他所作用的元素的某个成员函数
(四)容器内的元素
1 容器元素的条件
(1)必须可以通过copy构造函数进行复制,copy函数的性能尽可能优化
(2)必须可以透过assignment操作符完成赋值操作(即=号)
(3)必须可以通过析构函数完成销毁动作析构函数决不能设计成preivate
析构函数决不能抛出异常
(4) 对于序列式容器而言,元素的default构造函数必须可用
(5) 对于某些动作必须定义operatpor==以执行相等测试
(6)在关联式容器中元素必须定义排序准则缺省情况下operator<透过仿函数less<>调用
2 value语意和reference语意
所有容器都会建立元素的副本并返回该副本这意味着容器内的元素与你放进去的对象相等但并非同一