《STL源码剖析》读书笔记(一)

本文为阅读《STL源码剖析》所作的读书笔记,仅供自己学习备份。

  • STL设计的目的
    建立数据结构和算法的一套标准,并且降低期间的耦合关系以提升各自的独立性、弹性、交互操作性。
  • 组成
    • 迭代器:设计适当的响应型别
    • 容器:设计适当的迭代器
    • 算法:完全独立于容器和迭代器之外自行发展,只要设计时以迭代器为对外接口就行
  • STL六大组件
    容器(containers),指各种数据结构:vector、list、deque、set、map。从实现的角度,STL容器是一种class template。
    算法(algorithms),常用算法例如sort、search、copy、erase…
    迭代器(iterators),容器与算法之间的胶合剂,是所谓的“泛型指针”
    仿函数(functors),行为类似函数,可作为函数的某种策略
    配接器(adapters),一种修饰容器或仿函数或迭代器接口的东西,例如queue和stack,底层借助于deque
    配置器(allocators),空间配置器

  • 环境组态
    stl_config.h中定义许多常量,标识某些组态的成立与否。所有的STL头文件都会直接或者间接包含这个组态文件,并以条件式写法,让预处理器根据各个常量决定取舍哪一段程序代码

  • 临时对象的产生和运用
    临时变量,在型别名称之后加上一对小括号,可指定初值,其意义相当于调用相应的constructor并且不指定对象名称,例如vector(n,1)用于初始化一个二维vector

  • 前闭后开区间表示法
    任何一个STL算法需要获得一对迭代器(泛型指针)所标示的区间,用于表示操作的范围。一般是前闭后开的区间,[begin, last),迭代器last指的是最后一个元素的下一个位置

    for(;first!=last;first++){
    ...//do sth
    }
  • 函数指针
    将一整组操作当做参数来传递,可通过函数指针来达成
    缺点在于无法持有自己的状态(所谓的局部状态),也无法达到组件技术中的可适配性(无法再将某些修饰条件加在它上面从而改变它的状态)

  • 仿函数
    使用起来像函数一样的东西,例如针对某个class进行operator()重载

    template <class T>
    struct plus{
        T operator()(const T&x, const T&y) const{ return x+y;}
    }
    //产生放函数对象
    plus<int> plusobj;
    //使用仿函数
    cout<<plusobj(3,5)<<endl;

    plus非常接近STL的实现,唯一差别在于它缺乏“可配接能力”

  • SGI空间配置器
    STL的操作对象(所有的数值)都存放在容器之内,而容器一定需要配置空间以置放资料。

    • 标准的空间配置器:std::allocator,因为效率低,一般不建议使用。只是对基层内存配置/释放行为::operator new和::operator delete做了薄薄的包装
    • 特殊的空间配置器:std:alloc
  • c++内存配置操作和释放操作
    new阶段:调用::operator new配置内存::construct();调用构造函数构造对象内容alloc::allocate()
    delete阶段:调用析构函数::destroy();调用::operator delete释放内存alloc::deallocate()

  • 空间的配置和释放
    SGI以malloc()和free()完成内存的配置和释放。考虑到小型区块可能造成内存破碎问题,SGI设计了双层级配置器。第一层直接用malloc和free,第二级配置器视情况采用不同策略。如果配置区别大于128bytes,选用第一级配置器;当小于128bytes时,采用复杂的memory pool的整理方式。是否同时开放第二级配置器,取决于__USE_MALLOC是否被定义。

  • 内存基本处理工具

    • construct()接受一个指针和一个初值value,用途是将初值设定到指针所指的空间上
    • destroy()有两个版本,一个版本是接受一个指针,准备将该指针所指之物析构掉;版本2是接受first和last两个迭代器,将[first, last)范围内所有对象析构掉(要考虑效率问题)
    • uninitialized_copy使我们能够将内存的配置与对象的构造函数分离开来。对于输入范围的每个对象产生一个复制品,放入输出范围中。
    • uninitialized_fill使我们能够将内存的配置与对象的构造函数分离开来。如果输入范围内的迭代器指向未初始化的内存,那么会在该范围内产生x的复制品。
    • unintialized_fill_n使我们能够将内存的配置与对象的构造函数分离开来。会为指定范围内的所有元素设定相同的初值
  • POD(plain old data)
    标量型别或传统的c struct型别,有trival/ctor/dtor/copy/assignment函数。
    对于POD型别,一般是直接调用copy函数/最有效的初值填写手法fill()/
    对于非POD型别,一般逐个使用构造函数/一个一个元素构造/

  • 迭代器模式
    提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表达方式

  • 迭代器
    迭代器是一种类似指针的对象,最重要的工作就是内容提领(deference)和成员访问(member access),因此最重要的是对operator*和operator->进行重载工作。

  • 迭代器表达方式

    • constant iterator:不允许改变“所指对象的内容”
    • mutable iterator:允许改变“所知对象的内容”
  • Traits编程技法
    凡原生指针,都没有能力定义自己的相应型别
    凡class-type iterators,都有能力定义自己的相应型别
    通过class template partial specialization的作用,不论是原生指针还是class-type iterator,都可以让外界很方便地获取其相应型别。
    traits就是一个特性萃取机,获取各个迭代器的特性(相应型别)

    template <class I>
    struct iterator_traits{
        typedef typename I::value_type value_type;
    }

    如果traits能够有效运作,每个迭代器必须遵循约定,自行以内嵌型别定义的方式定义出相应型别。
    traits技法大量使用于STL实现品中,利用“内嵌型别”的编程技巧与编译器的template参数推导功能,增强c++未能提供的关于型别认证方面的能力,弥补c++不为强型别语言的遗憾。

  • 迭代器的相应型别

    • value type:指迭代器所指对象的型别
    • difference type:表示两个迭代器之间的距离
    • pointer:传回一个左值,令它代表p所指之物的地址
    • reference:传回一个左值,令它代表p所指之物
    • iterator catagory:产生一个临时变量,其型别隶属于四个迭代器类型(I、F、B、R)之一,然后根据这个型别,编译器再决定用哪个重载函数
      template<class InputeIterator, class Distance>
          inline void advance(InputeIterator& i, Distance n);
  • iterator_traits
    萃取迭代器的特性

  • __type_traits
    萃取型别(type)的特性,这个型别是否具备non-trivial defalt ctor/non-trival-copy ctor/non-trival assignment operator

  • -

猜你喜欢

转载自blog.csdn.net/hgyan25/article/details/80976707