参考书籍《STL源码剖析》
STL主要学以下几部分:
一.容器
1.顺序容器
vector list deque
2.关联容器 红黑树 BST
set multiset map multimap //观察者模式
3.容器适配器 deque
stack queue priority_queue
二.泛型算法
find sort
三.迭代器
容器: iterator reverse_iterator
const_iterator const_reverse_iterator
五种迭代器
输入型 输出型 正向 双向 随机访问
三种
1.反向
2.插入型
3.流式
四.函数对象 仿函数 //greater less
二种
1.一元函数对象
2.二元函数对象
五.适配器 bind2nd not1
六.空间配置器
1.一级空间配置器
2.二级空间配置器
----------------------------------------
零、前言:本片博客想写什么?
跟着《STL源码剖析》的思路,自己设计list和vector中的迭代器,遇到问题,解决问题,之后总结,再剖析源码。
但要从了解模板,因为STL中运用了大量的模板,模板的概念简单使用,模板特例化(部分特例化,完全特例化);迭代器的类型,几种迭代器的关系,剖析源码中的迭代器,萃取traits编程。。。
书中的总结:多读几遍去理解:
设计迭代器的相应型别,是迭代器的责任。
设计适当的迭代器,是容器的责任。
只有容器本身,才知道该设计出怎样的迭代器来走访自己,并执行迭代器该有的各种行为(前进、后退、取值、取用成员)。
至于算法,完全可以独立于容器的迭代器之外自行发展,只要设计时以迭代器为对外接口就行。
traits编程技法大量运用在STL中。它利用“内嵌性别”的编程技巧与编译器template参数推导功能,增强c++未能提供的关于型别认证的能力,
弥补c++不为强型别语言的缺憾。
了解traits编程,是打开STL源码剖析的钥匙。
一、几种迭代器的关系:
1.迭代器的相应型别:
“什麼是迭代器相应型别?答:迭代器所指之物的型別便是其一(即迭代器所指对象的类型)”
迭代器的相应型别有以下5种:
value_type 值类型 :迭代器所指对象的型别
difference_type 差值类型:用来表示两个迭代器之间的距离
reference_type 引用类型:
pointer_type 指针类型 const_pointer_type 常指针类型
iterator_category 迭代器类型 (分为下面的5种类型)
《STL源码剖析》这本书是台湾同胞所著,所以书中一些词语读起来感觉很别扭。
比如“对象的类型”,书中即为“对象的型别”。
2.五种迭代器
根据迭代器移动特性與施行动作,迭代器被分为五类:
输入型迭代器 input_iterator
输出型迭代器 output iterator
正向迭代器 Forward Iterator
双向迭代器 Bidirectional Iterator
随机迭代器 Random Access Iterator
5种迭代器的关系: 如图《STL源码剖析》126页
图中箭头表示继承关系,可以看到这五种迭代器的层层继承关系。
(《高效c++编程》第二版 继承 什么是共有继承?“是一个”的意思!什么是私有继承? “组合”......)
3. 容器中的迭代器:
hashtable slist: 正向迭代器
list map set : 双向迭代器
vector dequeue:随机迭代器
(这里好理解,容器中的迭代器就是5种迭代器其中的3个,因为正向迭代器继承了输入和输出型迭代器,所以这三中就够了。)
(还是那句话:“设计迭代器的相应型别,是迭代器的责任。 设计适当的迭代器,是容器的责任。”)
二、自己设计迭代器:
1.定义itreator作为标记用的类别
//自己创建头文件iterator.h
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag : public input_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag { };
struct random_access_iterator_tag : public bidirectional_iterator_tag { };
2.定义迭代器类型:
//写在iterator.h中
/* 下面模板中简写了模板类型:
_Cate : iterator_category 迭代器类型 (分为下面的5种类型)
_Ty: value_type 值类型 :迭代器所指对象的型别
_Dist:difference_type 差值类型:用来表示两个迭代器之间的距离
_P : pointer_type 指针类型 const_pointer_type 常指针类型
_ref:reference_type 引用类型:
*/
template<class _Cate,class _Ty,class _Dist class _P = _Ty*,class _Ref = _Ty&> //定义迭代器需要5个相应型別:迭代器类型 值 差值 指针 引用
struct iterator
{
//迭代器5个相应型别的重命名 typedef :
typedef _Cate iterator_category;
typedef _Ty value_type;
typedef _Dist difference_type;
typedef _P pointer;
typedef _Ref reference;
}
3.定义容器中的迭代器:双向 随机 正向:
设计3个class,共有继承iterator类,并给出迭代器的标志
//写在iterator.h中
template<class _Ty,class _Dist> //正向迭代器
class _Forit:public iterator<forward_iterator_tag,_Ty,_Dist>
{};
template<class _Ty,class _Dist> //双向迭代器
class _BIDrit:public iterator<bidirectional_iterator_tag,_Ty,_Dist>
{};
template<class _Ty,class _Dist> //随机迭代器
class _Ranrit:public iterator<random_access_iterator_tag,_Ty,_Dist>
{};
4.在设计自己的list
当然这里没写全,只涉及到迭代器的内容;还缺很多函数:插入,删除,构造......
//写在zdw_list.h 下
namespace zdw
{
template<class _Ty>
class list //设计一个链表 ,结点类,迭代器类
{
private:
struct _node;
typedef _node* nodeptr;
struct _node
{
nodeptr pre;
nodeptr next;
_Ty value; //结点的值
};
public:
class const_iterator:public zdw::_Bidit<_Ty,int> //继承双向迭代器
{};
class iterator:public const_iterator
{};
};
}
这里写在namespace中是为了防止命名污染,使用自己的命名。
4.在设计自己的 vector
//zdw_vector.h文件 设计一个vector
namespace zdw
{
template<class _Ty>
class vector
{
public:
class const_iterator:public zdw::_Ranrit<_Ty,int> //随机访问迭代器
{};
class iterator:public const_iterator
{};
};
}
6.设计迭代器的advance函数: 迭代器的移动
思考:再写advance 如何得到迭代器类型? 这就是书中的重点:类型萃取traits编程技巧
根据不同的容器,迭代器的移动也是不一样的:
正向迭代器(比如单链表):需要一步一步移动
双向迭代器(比如双链表):判断正反,前移动还是后移动
随机迭代器(比如vector 数组):一步到位直接跨过去
//在iterator.h中
template<class _II,class _Dist>
void _advance(_II &i,_Dist n,input_iterator_tag)//输入型迭代器:
{
while (n--) ++i;//只能一个一个走
}
template<class _II,class _Dist>
void _advance(_II &i,_Dist n,bidirectional_iterator_tag)//双向迭代器
{
if (n >= 0)
while (n--) ++i; //要判断正负值
else
while (n++) --i;
}
template<class _II,class _Dist>
void _advance(_II &i,_Dist n,random_accessl_iterator_tag)//随机迭代器
{
i += n; //随机迭代器可以直接+
}
但是最终的advance设计就是一个难点:
设计一:解决不了问题,error,pass掉
template<class _II,class _Dist>
void advance(_II &i,_Dist n) //这个函数的设计 重点难点
{
//advance 如何调用_advance(_II &i,_Dist n,input_iterator_tag)双向迭代器的移动函数?
}
设计二:将迭代器类型当做类型传参,但是还有问题
template<class _II,class _Dist> //类型加() 表示这是一个对象
void advance(_II &i,_Dist n,input_iterator_tag() ) //但是,系统如何根据_II迭代器类型从而知道它是input_iterator_tag
{
_advance(i,n,input_iterator_tag())
}
设计三://这是今天将的重点 :类型萃取 这里先不写结果,需要一步一步分析
7.写一个类型萃取的实例 :
我理解的类型萃取:就是要拿到template的类型,如何拿到? 加一个中间层,还是那个原理,“计算机中的问题可以加一个中间层,这里的中间层就是萃取萃取结构体)
(1)一个不够完美的程序:不能做到接口复用:
template<class _Ty>
class SeqList
{
public:
typedef _Ty value_type;
private:
_Ty data[10];
int cursize;
public:
SeqList():cursize(0){};
~SeqList(){};
void push(const _Ty &x)
{
data[cursize++] = x;
}
};
template<class Container>
void Append(Container &seq)
{
int x;
cin>>x;
seq.push(x);
}
int main()
{
SeqList<int> seq;
Append(seq);
}
这个程序有个问题就是:如果我们SeqList<double> seq;那么Append函数中的x就需要改为double。
(2)增加类型萃取机制:
template<class _Ty>
class SeqList //我们设计一个顺序表
{
public:
typedef _Ty value_type;
private:
_Ty data[10];
int cursize;
public:
SeqList():cursize(0){};
~SeqList(){};
void push(const _Ty &x)
{
data[cursize++] = x;
}
};
template<class Container> //中间层的萃取机制,拿到当前容器中的数据类型
struct traits_seq
{
typedef typename Container::value_type value_type;
};
template<class Container>
void Append(Container &seq)
{
typename traits_seq<Container>::value_type x;
cin>>x;
seq.push(x);
}
int main()
{
SeqList<int> seq;
Append(seq);
}
8.回到iterator中advance函数的萃取
template<class Iterator>
struct itreator_traits
{
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
}
9.写迭代器的方法:重载++ -- 前置后置等
10.解决普通指针不能萃取的问题
原生类型指针:是一种随机迭代器
指针的处理:模板函数的特化 部分特化 完全特化
//特化的几个示例:
template<class T>
class C{}; //无特化
template<class T>
class C<T*>{}; //部分特化
template<class T>
class C<const T*>{}; //部分特化
template<>
class C<char *>{}; //完全特化
关于模板特例化的形象说明:老师当时这样讲,让我印象深刻
模板特例化就像找对象:
完全特化就是刚毕业的小年轻们,“我要娶白富美,乖巧可爱的迪丽热巴”,
部分特化就是“我要找漂亮的就行”;
无特化就是:“我娶个女的,就行!”
//重新写萃取结构体
template<class T>
struct iterator_traits<const T*>
{
iterator_traits(){};
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef int difference_type;
typedef T* pointer;
typedef T& reference;
}
11.sgi的做法 和 vs的做法
12.distance函数的实现 :求得两个迭代器之间的举例
template<class _II>
typename iterator_traits<_II>::difference_type
__distance(_II _First,_II _Last,input_iterator_tag) //输入迭代器
{
typename iterator_traits<_II>::difference_type n = 0;
for(,_First != Last;++_First,++n);
return n;
}
template<class _RAI>
typename iterator_traits<_RAI>::difference_type
__distance(_RAI _First,_RAI _Last,random_access_iterator_tag) //随机迭代器
{
return _Last - _First;
}
template<class _II>
typename iterator_traits<_II>::difference_type
distance(_II _First,_II _Last) //最终distance
{
return __distance(_First,_Last,iterator_category(_First));
}
11.总结:
设计迭代器的相应型别,是迭代器的责任。
设计适当的迭代器,是容器的责任。
只有容器本身,才知道该设计出怎样的迭代器来走访自己,并执行迭代器该有的各种行为(前进、后退、取值、取用成员)。
至于算法,完全可以独立于容器的迭代器之外自行发展,只要设计时以迭代器为对外接口就行。
traits编程技法大量运用在STL中。它利用“内嵌性别”的编程技巧与编译器template参数推导功能,增强c++未能提供的关于型别认证的能力,
弥补c++不为强型别语言的缺憾。
了解traits编程,是打开STL源码剖析的钥匙。
12.iterator完全源码:
// 节录自 SGI STL <stl_iterator.h>
// 五种迭代器类型
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
// 为避免写代码时挂一漏万,自行开发的迭代器最好继承自下面这个 std::iterator
template <class Category, class T, class Distance = ptrdiff_t,class Pointer = T*, class Reference = T&>
struct iterator
{
typedef Category iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
};
// 「榨汁机」 traits
template <class Iterator>
struct iterator_traits
{
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
};
// 针对原生指针( native pointer)而设计的 traits 偏特化版(部分特化)。
template <class T>
struct iterator_traits<T*>
{
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};
// 针对原生之 pointer-to-const 而设计的 traits 偏特化版(部分特化)。 台湾同胞将部分特化称之为偏特化
template <class T>
struct iterator_traits<const T*>
{
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef const T* pointer;
typedef const T& reference;
};
// 这个函式可以很方便的决定某个迭代器的类型( category)
template <class Iterator>
inline typename iterator_traits<Iterator>::iterator_category
iterator_category(const Iterator&)
{
typedef typename iterator_traits<Iterator>::iterator_category category;
return category();
}
// 这个函式可以很方便的决定某个迭代器的 distance type
template <class Iterator>
inline typename iterator_traits<Iterator>::difference_type*
distance_type(const Iterator&)
{
return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
}
// 这个函式可以很方便的决定某个迭代器的value type
template <class Iterator>
inline typename iterator_traits<Iterator>::value_type*
value_type(const Iterator&)
{
return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
}
// 以下是整组 distance 函数
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
__distance(InputIterator first, InputIterator last,input_iterator_tag)
{
iterator_traits<InputIterator>::difference_type n = 0;
while (first != last)
{
++first; ++n;
}
return n;
}
template <class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last,random_access_iterator_tag)
{
return last - first;
}
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last)
{
typedef typename
iterator_traits<InputIterator>::iterator_category category;
return __distance(first, last, category());
}
// 以下是整组 advance 函式
template <class InputIterator, class Distance>
inline void __advance(InputIterator& i, Distance n,input_iterator_tag)
{
while (n--) ++i;
}
template <class BidirectionalIterator, class Distance>
inline void __advance(BidirectionalIterator& i, Distance n,bidirectional_iterator_tag)
{
if (n >= 0)
while (n--) ++i;
else
while (n++) --i;
}
template <class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator& i, Distance n,random_access_iterator_tag)
{
i += n;
}
template <class InputIterator, class Distance>
inline void advance(InputIterator& i, Distance n)
{
__advance(i, n, iterator_category(i));
}
--------------------------------------------
C++思想:模拟现实,模拟的及其相似!编程,是在计算机中反映世界!