//以下是铺垫,__type_trivial的作用在后面,下面是一些概念性介绍。
__type_trivial 双底线前缀,表示是SGI STL以外的东西,不在STL标准范围之内。
__type_trivial负责萃取型别(Type)的特性,究竟是什么特性呢?
注:trivial是英文“无意义的、不重要”的意思。
答曰:这个型别是否具备non-trivialdefalt ctor、non-trivial copy ctor、non-trivial assignment operator、non-trivial dtor? 这四个东西是什么?
即:构造函数(ctor)、复制构造函数(copy)、 赋值函数(assignment)、析构函数(dtor)
//以下针对原生指针设计__type_traits偏特化版本,原生指针被视为一种标量型别T
Template <class T> Struct __type_traits<T*> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; //POD:plain old data //C++的内建类型或传统的C结构体类型。POD类型必然有trivial ctor/dtor/copy/assignment四种函数</span> }
关于__type_traits<T>怎么判断是否含有traits函数,《stl源码剖析》并没有给出,这里查阅资料,下面这个比较好理解:
如果至少满足下面3条里的一条:
1. 显式(explict)定义了这四种函数。Ctor、copy、assignmen、dtor
2. 类里有非静态非POD的数据成员。比如string
3. 有基类
那么上面的四种函数是non-trivial函数,比如叫non-trivial ctor、non-trivialcopy…,也就是说有意义的函数,里面有一下必要的操作,比如类成员的初始化,释放内存等。
例:
//整个T是POD类型</span> class T { //没有显式定义ctor/dtor/copy/assignemt所以都是trivial int a; //POD类型 }; //整个T1是非POD类型</span> class T1 { T1() //显式定义了构造函数,所以是non-trivial ctor {} //没有显式定义ctor/dtor/copy/assignemt所以都是trivial int a;//POD类型 std::string b; //非POD类型 };
//基本概念介绍完毕,下面进入正题!
上面讲了那么多到底有什么用处呢? 重点
如果这个类都是trivial ctor/dtor/copy/assignment函数,我们对这个类进行构造、析构、拷贝和赋值时可以采用最有效率的方法,不调用无所事事正真的那些ctor/dtor等,而直接采用内存操作如malloc()、memcpy()等提高性能,这也是SGI STL内部干的事情。
如果你的类里面只用到的基本类型,如int char double等,系统的默认析构函数其实什么都没有做
但如果你使用了其他的类如vector,string等,系统的默认析构函数就会调用这些类对象的析构函数
如果是自己写析构函数的话,如果你的类里面分配了系统资源,如new了内存空间,打开了文件等,那么在你的析构函数中就必须释放相应的内存空间和关闭相关的文件;这样系统就会自动调用你的析构函数释放资源,避免内存泄漏
例如《STL源码剖析》2.2.3构造和析构基本工具:construct()和destroy(),关于destroy()部分源码如下:
//destroy()第一个版本,接受一个指针 Template <class T> Inline void destroy(T * pointer) { Pointer-> ~T(); //调用dtor ~T(); } //destroy()第二个版本,接受两个迭代器。此函数设法找出元素的数值型别 //然后利用__type_trivial<>求取最适当措施。 Template <class ForwardInterator> Inline void destroy(ForwardInterator first,ForwardInterator last, T*) { Typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor; __destroy_aux(first, last, trivial_destructor()); } //下面是对__destroy_aux的重载,第三个参数分别为__true_type、__false_type //如果元素的数值型别(value type)有non-trivial 函数 Template <class ForwardIterator> Inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) { for( ; first < last ; ++first) destroy(&*first); //对[first, last)范围的多有对象析构掉! } //如果元素的数值型别(value type)有trivial 函数 Template <class ForwardIterator> Inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}//什么也不做
主要思想:destroy()有两个版本,一个是接受一个指针,然后将该指针所指之物析构掉,直接调用该对象的析构函数即可。第二个版本接受first和last两个迭代器,准备[first,last)范围内的所有对象析构掉。我们不知道该范围有多大,万一很大,并且如果每个对象的析构函数都无关痛痒(既析构函数是trivial destructor),那么一次次的调用这些没有意义的析构函数对效率是一种伤害,例如上面所说的如果迭代器所指对象型别是int,那么析构函数啥也没有做,多次调用效率地下。所以!重点在这里:我们先利用value_type()获得迭代器所指对象的型别,再利用__type_traits<T>判断该型别的析构函数是否无关痛痒(是否是trivial函数)。如果是(__true_type),则什么也不做就结束;若否(__false_type),才会逐个对对象进行析构!
最后补充一点:究竟一个class什么时候该有自己的non-trivial defalt ctor、non-trivial copyctor、non-trivial assignment operator、non-trivial dtor呢?
答曰:一个简单的判断准则是:如果class内涵指针成员,并且对他进行内存动态配置,那么这个class就需要实现出自己的non-trivial-xxx。换句话说,如果一个class内含有指针成员、并且对它进行了内存动态配置,那么它必含non-trivial函数(前提是程序写对--)。
《STL源码剖析》