《STL源码剖析》迭代器以及Traits设计

2024-01-21 21:08

本文主要是介绍《STL源码剖析》迭代器以及Traits设计,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

       C++的class templates和function templates可以实现容器和算法的泛型化。难点和关键是设计这两者的胶着剂角色——迭代器——提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴漏该容器的内部数据结构和内部表述方式。

      迭代器是一种Samart pointer。

      每一种STL容器都提供有专属的迭代器!原因:为了让实现细节封装起来而不让使用者看到,所以把迭代器的开发工作交给了具体容器的设计者。

      (首先明确一点:设计适当的相应型别,是迭代器的责任;设计适当的迭代器,则是容器的责任!)


Traits编程技法(特性萃取机)——解决迭代器常用的五种相应型别:

       首先,我们来分析一下为什么要引入所谓的Traits。(不断加入限制条件(特例),繁衍traits的由来)
       一个容器可以装各种不同类型的数据,结合value type,也就是说指迭代器所指对象的型别是各种各样的。为了解决获取“迭代器所指对象的型别”,比如“以“迭代器所指对象的型别”为型别 来 声明一个变量”,我们首先会想到    

     

       (1)function    template的参数推导机制


       上图中以func为对外接口,却把实际操作全部置于func_impl中。又由于func_impl是一个function template,func_impl一旦被调用,编译器自动进行template参数推导,导出型别T。(也就说,采用上述function    template的参数推导机制 我们获得了“迭代器所指对象的型别”)


       (2) 声明内嵌型别

        我们知道,“template参数推导机制”推而导之的只是参数类型,无法推导函数的返回值型别。所以,万一,value type必须用于函数的传回值,“template参数推导机制”就行不通了。为了解决这个问题,声明内嵌型别似乎是一个好主意。

          


       (3)偏特化

        但是,并不是所有迭代器都是class type!原生指针就不是!如果不是原生指针,就无法定义内嵌型别。、?????。所以,为了让一般化概念针对特定情况做特殊化处理,我们使用——template partial specialization——在泛化设计中提供一个特化版本,也就是将泛化版本中某些template参数赋予明确的指定,提供另外一份template定义式,而其本身仍旧是templatized。

         template <typename T>
         class C   {......}      //这个泛化版本允许接受T为任何型别

         template <typename T>
         class C<T* >   {......}      //这个特化版本仅适用于“T为原生指针”的情况。,解决了内嵌型别未能解决的问题。

       (4)Traits——特性萃取机,萃取各个迭代器的特性(迭代器的相应型别)

        Traits,其实就是内嵌型别方法的一种升级——Traits特性萃取机,约定:自行 以内嵌型别定义的方式 定义出相应型别。

struct iterator_traits
{
typedef typename I::iterator_category iterator_category;
typedef typename I::value_type value_type;
typedef typename I::difference_type difference_type;
typedef typename I::pointer pointer;
typedef typename I::reference reference;
}

        iterator_traits必须针对传入之型别为 pointer 以及 pointer-to-const 者,设计特化版本。各相应型别的具体设计详见下面。

       如果希望自己开发的容器与STL水乳交融,一定要为自定义的容器的迭代器定义这五种型别。

       Traits的意义是:如果 I 定义有自己的value type,那么通过traits 的作用,萃取出来的就是I::value type。不论面对的是迭代器MyIterator,或者是原生指针int * ,或者是const Int *,我们都可以通过Traits取出正确的value type。

       Traits编程技法——利用了“内嵌型别”的编程技巧与编译器的template参数推导功能,增强C++未能提供的关于型别认证方面的能力。


1、迭代器相应型别之一:value type——指迭代器所指对象的型别
      在类中定义自己的value type内嵌型别。


2、迭代器相应型别之二:difference type——用来表示两个迭代器之间的距离

template < class I ,class T>
typename iterator_traits<T>::difference_type const(I first, I last, const T& value)
{typename iterator_traits<I>::difference_type n = 0;for (; first != last; ++first){if (*first == value)++n;}return n;
}template <class I>
struct iterator_traits{typedef typename I::difference_type difference_type;
};template <class I>
struct iterator_traits<T*>{        //针对原生指针而设计的“偏特化”版本typedef ptrdiff_t difference_type;
};
template <class I>
struct iterator_traits<const T*>{  //针对原生的pointer-to-const 而设计的“偏特化”版本typedef ptrdiff_t difference_type;
};</span>
有了上面的设计,很显然我们就可以在需要任何迭代器I的difference type的时候,统一这么写:

           typename iterator_traits<T>::difference_type


3、迭代器相应型别之三:reference type(引用类型)——传回左值,代表P所指之物;

     迭代器相应型别之四:pointer  type(指针类型)——传回一个pointer,指向迭代器所指之物;

Item& operator*()  const { return *ptr; }          // reference type
Item* operator->() const { return  ptr; }</span>   // pointer type

     Item&是reference type,Item*是pointer type。“传回一个左值,令它代表p所指之物”是可以的,“传回一个地址,令它代表所指之物的地址”也是可以的。


4、迭代器相应型别之五:iterator_category——迭代器类别
      iterator_category表示迭代器的分类,共有五类。input_iterator、output_iterator、forward_iterator、bidirectional_iterator、random_access_iterator,分别是只读、只写、前向移动读写、双向移动读写、随机读写。

      此处,为了消除“单纯传递调用的函数”,STL运用了C++的重载技术。先来看一下五种迭代器类型的从属关系:

//迭代器各类间的关系  
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 {}; 


如果没有上面的继承机制,我们就需要像下面一样,去传递一个调用函数:

template <class InputIterator, class Distance>
inline void _advance(InputIterator& i, Distance n, input_iterator_tag)  //第一个函数——单向,逐一前进
{while (n--) ++i;
}template <class ForwardIterator, class Distance>
inline void _advance(ForwardIterator& i, Distance n, forward_iterator_tag)  
{_advance(i, n, input_iterator_tag);   //单纯的进行传递调用
}


我们再来看使用继承机制后的调用情况:

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_traits(InputIterator)::iterator_category()); //产生一个暂时对象(就像int()会产生一个int暂时对象),具体调用哪一个待决定
}



附:

泛型指针,原生指针和智能指针:
      1. 泛型指针 泛型指针有多种含义。 (1) 指void*指针,可以指向任意数据类型,因此具有“泛型”含义。 (2) 指具有指针特性的泛型数据结构,包含泛型的迭代器、智能指针等。 广义的迭代器是一种不透明指针,能够实现遍历访问操作。通常所说的迭代器是指狭义的迭代器,即基于C++的STL中基于泛型的iterator_traits实现的类的实例。 总体来说,泛型指针和迭代器是两个不同的概念,其中的交集则是通常提到的迭代器类。
       2. 原生指针就是普通指针,与它相对的是使用起来行为上象指针,但却不是指针。 说“原生”是指“最简朴最基本的那一种”。因为现在很多东西都抽象化理论化了,所以“以前的那种最简朴最基本的指针”只是一个抽象概念(比如iterator)的表现形式之一。 原生指针即   (类型名*p)样子的指针,类型名可以是基础类型,如int,double等,也可以是一个自己定义的Class类,相反的如果一个类重载了‘*’和‘->’的运算符,可以像指针一样用‘*’和‘->’操作,就不是原生的,如iterator等。auto_ptr(在头文件memory),这是一个用来包装原生指针的对象。
       3. 智能指针是C++里面的概念:由于 C++ 语言没有自动内存回收机制,程序员每次得自己处理内存相关问题,但用智能指针便可以有效缓解这类问题。 引入智能指针可以防止出现悬垂指针的情况 一般是把指针封装到一个称之为智能指针类中,这个类中另外还封装了一个使用计数器,对指针的复制等操作将导致该计数器的值加1,对指针的delete操作则会减1,值为0时,指针为NULL。


《STL源码剖析》源代码下载:http://pan.baidu.com/s/1c0x2kP6


这篇关于《STL源码剖析》迭代器以及Traits设计的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/630823

相关文章

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

MyBatis设计SQL返回布尔值(Boolean)的常见方法

《MyBatis设计SQL返回布尔值(Boolean)的常见方法》这篇文章主要为大家详细介绍了MyBatis设计SQL返回布尔值(Boolean)的几种常见方法,文中的示例代码讲解详细,感兴趣的小伙伴... 目录方案一:使用COUNT查询存在性(推荐)方案二:条件表达式直接返回布尔方案三:存在性检查(EXI

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3

C++迭代器失效的避坑指南

《C++迭代器失效的避坑指南》在C++中,迭代器(iterator)是一种类似指针的对象,用于遍历STL容器(如vector、list、map等),迭代器失效是指在对容器进行某些操作后... 目录1. 什么是迭代器失效?2. 哪些操作会导致迭代器失效?2.1 vector 的插入操作(push_back,

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Python 迭代器和生成器概念及场景分析

《Python迭代器和生成器概念及场景分析》yield是Python中实现惰性计算和协程的核心工具,结合send()、throw()、close()等方法,能够构建高效、灵活的数据流和控制流模型,这... 目录迭代器的介绍自定义迭代器省略的迭代器生产器的介绍yield的普通用法yield的高级用法yidle

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++