通用工具Utilities
C++程序库中的通用工具:它们一般由短小精悍的类和函数组成,执行一般性的工作:
1.通用类型 general types
2.一些重要的C函数
3.数值极值 numeric limits
大部分的通用工具定义在C++的utility头文件中,某些辅助函数定义在algorithm头文件中。
1.pairs 对组
class pair将两个值视为一个单元,在map和multimap中使用pair来管理键值对(key-value)。
struct pair定义在标准库的utility中:
namespace std{
template <class T1,class T2>
struct pair{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair():first(T1()),second(T2()){} //默认的初始化
pair(const T1& a, const T2& b):first(a),second(b){}
template <class U,class V>
pair(const pair<U,V>& p):first(p.first),second(p.second){} //模板构造函数,可能存在隐式类型转换
template <class T1,class T2>
bool operator== (const pair<T1,T2>&,const pair<T1,T2>&);
template <class T1,class T2>
bool operator< (const pair<T1,T2>&,const pair<T1,T2>&);//比较时第一值具有较高的优先级
template <class T1,class T2>
pair<T1,T2> make_pair(const T1& x,const T2& y){return pair(x,y);}
//convinience function
pair被定义为struct,而不是class,那么所有成员都是public的,可以直接存取pair的个别值。
C++标准库中,凡是必须返回两个值的函数,都使用pair类。
2.class auto_ptr
a kind of smart pointer,help programmer to void memory leak when exception happens.
使用auto_ptr的动机:
申请资源,如果发生异常,资源释放没有执行。那么需要捕捉所有的异常,并在catch子句中释放资源。
这样多个catch语句的中都要释放资源。
只能指针保证,只要自己被摧毁,一定连带释放所指向的资源。
auto_ptr是区域变量,无论正常退出,还是异常退出,只要是退出,它就一定会被摧毁。它是它所指对象的拥有者。
#include<memory>
void f(){
std::auto_ptr<A> ptr(new A());
没有定义++操作,不能做++操作。
std::auto_ptr<A> ptr1(new A()); //ok
std::auto_ptr<A> ptr2 = new A; //error
auto_ptr拥有权的转移:
auto_ptr的copy构造函数和赋值操作符将对象拥有权交出去。
std::auto_ptr<A> ptr1(new A);
std::auto_ptr<A> ptr2(ptr1); //ptr1对象拥有权交给了ptr2
or ptr2 = ptr1; //ptr1对象拥有权交给了ptr2,若ptr2原来拥有指向其他对象,这个对象会被delete掉。
只有auto_ptr可以拿来当做另一个auto_ptr的初值,普通的指针不可以,不能以普通指针的赋值方法初始化auto_ptr。
拥有权的转移,使得一个函数使用auto_ptr将拥有权转移给另一个函数。
auto_ptr语意就有转移所有权,那么如果不想转移所有权,就应该避免使用auto_ptr。当把auto_ptr作为参数传入其他函数时,那么就意味着拥有权的终结。如果不注意这个细节,可能带来未知的错误。
std::auto_ptr<A> p(new A());
*p = 41;
bad_print(p); // auto_ptr会交出所有权,*p所指会被销毁
*p = 1; // runtime error
同样使用Reference传递auto_ptr让人难以捉摸,也要避免使用这种糟糕的方法。
总之,auto_ptr减少了不经意间转移所有权所带来的危险。
使用关键字const,那么不能修改auto_ptr的所有权。
const std::auto_ptr<A> p(new A());
*p = 2;
bad_print(); //runtime error 不能转交拥有权
*p = 1; //可以修改指针所指对象的值
p = q; //runtime error
auto_ptr作为成员之一,当对象删除时,auto_ptr会自动删除它所指的成员对象,于是也就不需要析构函数了。但是auto_ptr意味着要自己重写COPY构造函数和==操作符重载函数。
如果无意转交拥有权,那么使用const auto_ptr.
auto_ptr错误运用实例:
1.auto_ptr之间不能共享拥有权
2.不存在针对array的auto_ptr
3.auto_ptr并不是四海通用的smart指针,如适用的计数型指针
4.auto_ptr并不符合STL容器对元素的要求,因为在copy和赋值前后,原本的值和之后的值不相等。
切记,非const的auto_ptr并不比普通的指针更安全,不应该以任何方式传递auto_ptr。
namespace std{
//auxiliary type to enable copies and assignments
template<class Y> struct auto_ptr_ref{
Y* yp;
auto_ptr_ref(Y* rhs):yp(rhs){}
}
template<class T> class auto_ptr{
private:
T * ap;
public:
typedef T element_type;
//constructor
explicit auto_ptr(T* ptr = 0)throw() :ap(ptr){}
//copy constructors,capture ownership
auto_ptr(auto_ptr& rhs)throw() :ap(release(rhs)){}
template<class U> auto_ptr(auto_ptr<U>& rhs) throw(): ap(release(rhs)){} //implicit copy
//assignment constructors
auto_ptr& operate=(auto_ptr& rhs) throw(){reset(release(rhs)); return *this;};
template<class U> auto_ptr& operater=(auto_ptr<U>&) throw(){reset(release(rhs));
return *this;} //implicit assignment
//destructor
~auto_ptr()throw(){delete ap;}
//value access
T* get() const throw(){return ap;}
T& operater*() const throw(){return *ap;} //deference operater
T* operater->() const throw(){return ap;} //member access operater
//release ownership
T* release() throw(){T* temp(ap); ap = 0; return temp;}
//reset value
T* reset(T* ptr = 0)throw(){if(ap != ptr){delete ap; ap = ptr;}}
//conversions :cann't copy const auto_ptr
public:
auto_ptr(auto_ptr_ref<T>) throw();
auto_ptr& operater=(auto_ptr_ref<T> rhs) throw();
一般的拷贝函数肯定会拷贝参数的,如果参数被定义为Reference to const object,那么发生拷贝,ownership发生改变,这将违背const本意。我们在前面已经强调过,当使用拷贝时,将auto_ptr参数定义为const很容易让人混淆。因为对于auto_ptr,拷贝就意味着ownership的转移,而const试图去组织这种ownership的转移。
变通的办法是找到一种办法,让右值变左值。rvalue----》lvalue。
引入auto_ptr_ref,协助将右值转化为左值,而不是去尝试const auto_ptr&这样糟糕的办法。
3.数值极限Numeric limits
一般来讲,数值极限是与平台相关的。C++标准库使用template numeric_limits提供了这些极值,取代了传统C语言所采用的预处理常数preprocessor constants。
这些预处理常数定义依然可以使用,整数常数定义在climits和limits.h头文件中,而浮点型常数定义在cfloat和float.h头文件中。
提供通用的template,同时提供其特化specialization版本。
1.common:
namespace std{
template <class T>
class numeric_limits{
public:
static const bool is_specialized = false;
2.specialization
namespace sta{
template < > class numeric_limits<int>{
public:
static const bool is_specialized = true;
static T min()throw(){return -2147483648;}
static T max()throw(){return 2147483648;}
static const int digits = 31;
使用范例:
numeric_limits<int>::max() // int类型最大取值
numeric_limits<float>::is_signed // 是否带符号
4.辅助函数
算法程序库中定义于头函数algorithm中:
1.求最大值和最小
namespace{
template <class T>
inline const T& min(const T& a,const T& b){return a<b?a:b;}
template <class T>
inline const T& max(const T& a,const T& b){return a<b?b:a;}
加入比较函数的版本:
namespace{
template <class T,class Compare>
inline const T& min(const T& a,const T& b,Compare comp){ return comp(a,b)?a:b;}
template <class T,class Compare>
inline const T& max(const T& a,const T& b,Compare comp){ return comp(a,b)?b:a;}
作为比较函数的comp应该是一个函数或者是一个仿函数functor。
2.两值交换swap
template <class T> void swap(T& a,T& b){
T tmp(a); a = b; b = temp;
}
5.辅助操作符
定义在头文件utility中,!=,>,>=,<=,它们是由<和==来实现的,那么只要定义了<和==就可以实现了。
namespace rel_ops{
template <class T>
inline bool operater!=(const T& a,const T& b){return !(a == b);}
template <class T>
inline bool operater<=(const T& a,const T& b){return !(b < a);}
template <class T>
inline bool operater>(const T& a,const T& b){return (b < a);}
template <class T>
inline bool operater>=(const T& a,const T& b){return !(a < b);}
使用时,using namespace std::rel_ops; ,就可以使用这些其他的比较符了。
6.头文件
cstddef :
C++中避免使用晦涩的NULL,C++中NULL为整型。
size_t: 无符号类型大小,常用来标识size长度
ptrdiff_t: 有符号类型,用来标示指针距离
offsetof: 表示一个成员在struct和union中的偏移量
cstdlib:
exit(int status); //退出前做大量的清理工作,销毁所有的static对象,清空缓冲区,关闭所有I/O 通道,终止程序(之前,调用经由atexit()登陆的函数),如果atexit发生 异常,调用terminate();
EXIT_SUCCESS
EXIT_FAILURE
abort(); //退出不做任何的清理工作
atexit(void (*function))
exit和abort函数都不会消除局部对象,因为堆栈辗转Stack unwinding开展动作不会执行。