C++ STL内核分析(1)


本文预览:

  • 源代码分布
  • OOP(面向对象编程) 和 GP(泛型编程)
  • STL内核基础:操作符重载与模板
  • 分配器(Allocator)
  • 探索std::list源码
  • 迭代器的设计原则与Iterator Traits(萃取机)的设计与作用
  • 探索std::vector源码
  • 探索std::array源码

概述

对于C++的STL源码,相信大多数人没有看过的,即使看过也未必看得懂。为什么写了很久的C++代码却看不懂标准库的源码,原因也很简单,我们写的代码使用OOP编程,而模板使用的是GP编程,内部全是模板、迭代器,各种操作符重载,各种的typedef与预编译宏,再加上不同编译器以及同编译器不同版本的实现方式不同,使得阅读STL源码如同阅读天书一般。OOP企图将data和method结合起来放在class里,而GP企图将data和method分开。操作符重载和模板是阅读标准库源码的基础。

源代码分布


不同的编译器分布是不同的,但是所有的源码都在include文件夹下面。以mac为例:

bogon:include alex$ which g++
/opt/local/bin/g++
bogon:include zhouzhan$ cd ..
bogon:usr zhouzhan$ ls
bin     lib     local       share
include     libexec     sbin        standalone

include这里面就是我的GNU编译器存放的源代码了。Linux和这个做法应该是相同的。windows请自行百度。

OOP(面向对象编程) 和 GP(泛型编程)


  1. OOP企图将data和method关联起来
list为什么提供了自己的sort()
  1. GP企图将data和method分开
algorithms提供了sort(),vector和deque不需再写

GP将算法和数据分开的好处:

  • 算法和容器独立开发,其间通过迭代器沟通。
  • Algorithms可以通过Iterator来确定操作范围,并通过Iterator取用Container元素。

STL内核基础:操作符重载与模板


操作符重载之前在语言部分就讲过了,我们这儿就不再重复了,需要注意的是有四个操作符是不能重载的,(:: and . and .* and ?:)这四个知道就可以了,一般也没有人会去想重载他们。
模板分为:类模板、函数模板和成员模板,其核心就是类型替换。
关于类模板需要说的是特化和偏特化,这个在使用模板的时候用到的还是非常多的:

模板的特化

特化:顾名思义就是特殊处理,模板的意图就是泛化,恨不得所有的代码就用一个模板实现,但是有这是很难做到兼顾所有,即使兼顾了,性能也未必就满足要求,因此为了照顾某些特殊类型,模板实现了特化。

除了特化之外还有偏特化,这里的偏和偏微分方程一样,都是局部的意思:

偏特化

两个参数中,有一个是固定的类型或者其类型是根据泛化参数来定的。

分配器


分配器一般是给容器来操作内存空间的,虽然提供了外部调用接口,但是一般没有人直接调用Allocator来进行内存操作,分配器的原理就是调用new 和delete操作符,而new和delete又分别调用C的malloc和free,几乎所有的关于底层内存操作的都是malloc和free了,因为malloc和free调用的是系统级的调用,所以,如果我们不用过C或者C++语言,也就不需要关注内存的分配,像java或者js等脚本语言,其编译器或者解释器不但屏蔽了像malloc这样的方法调用,甚至连内存的概念都淡化了,用户不需要再管理内存,其内存的所有操作都有编译器或者解释器来完成,当然编译器和解释器就需要用C这样的系统级语言来写了。

分配器的使用

每一个容器在模板定义的时候都有俩参数,一个是类型,一个是分配器,但我们在使用模板的时候都是传一个参数:
vector<int>,我们没有传分配器,因为模板使用了默认的分配器。分配器承载了内存管理策略,正如malloc一样,如果你malloc的是一个int,那么显然你得到的内存还包括8个字节的cookie等其他描述内存的信息,假如我malloc了一个int,实际内存占了16个字节,那么如果我malloc了1000000个int,内存浪费是16*1000000,VC和Booland貌似就是这么干的,所以运行时非常消耗内存,GNU在2.9的时候用了16条链表来管理优化,很好的解决了这个问题,但是在4.9的时候又回来了,不知道什么原因。

一个分配器的简单用法:

#include<alloca.h>
int main(){
    int* p = allocator<int>().allocate(512);
    allocator<int>().deallocate(p, 512);
}

其缺点在于,回收的时候我还要告诉分配器回收多大的空间,这中设计显然不是给用户使用的,因为记录空间大小将是一件非常繁琐的事情。上述调用是临时对象调用。

探索std::list源码


2.9版本源码

上图是GNU 2.9版本的源码,虽然老了一点,但还是很清晰的,简单易懂,里面就一根指向node的指针。4.9的继承关系太复杂,需要花费很大的精力去读。

list迭代器部分

迭代器主要有两部分构成,一是各种typedef,二是操作符重载。

list的迭代器

迭代器的设计原则与Iterator Traits(萃取机)的设计与作用

算法通过迭代器操作容器中的数据,所以,算法需要通过迭代器对容器的一些特性做出理解,以便更好操作容器。标准库中定义了5种关联类型:

  • iterator_category (迭代器的类型,能提供哪些操作)
  • value_type (操作数据的类型)
  • diffence_type (要操作的迭代器范围)
    其他两种从来没有用到过。
迭代器与算法关系

那么问题来了,如果迭代器不是class,而是指针,那么这种迭代器就无法对这5种关联类型做出说明,为了解决这类问题,C++标准库引入了iterator_traits(迭代器萃取机),由萃取机来做出说明,把萃取机实现为模板,通过偏特化来实现不同类型的Iterator。

通过偏特化实现的萃取机模板

探索std::vector源码

vector的源码实现比较简单了,里面有三根指针,元素开始和结束位置,存储空间结束位置。每次插入元素的时候都会检查finish指针是否到达最大存储空间,到的话就二倍增长,从新开辟一块新内存,把原来的内容拷贝过去,然后再销毁原来的内存。由此可见,实现动态增长是需要付出代价的,因为内存的开辟和拷贝都是耗时操作,而且还会造成内存的浪费。

vector

探索std::array源码


语言核心已经提供了静态数组,STL又把它封装了一层,C++11才出来的,感觉用的人不多吧应该,大概是为了把静态数组统一纳入STL中,以便使用迭代器和算法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容