《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

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

SprinBoot+Vue网络商城海鲜市场的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质创作者,全网30w+

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素