[CPP侯捷视频课笔记]C++标准库——架构&源码

[TOC]

源码之前 了无秘密 —— 侯捷

第一讲 架构

基础:C++基本语法(包括如何正确使用模板,templates

目标:

  1. 使用C++标准库
  2. 认识C++标准库(胸中自有丘壑)
  3. 良好使用C++标准库
  4. 扩充C++标准库

参考资料

STL 六大部件

image-20210617122354355

container通过allocator取得数据存储空间,algorithm通过iterator存取container内容,functor协助algorithm完成不同策略变化,adapter可以修饰或套接functor。

  • 容器(containers):vector.list.deque,set,map等数据存放的类。
  • 算法(algorithms):各种排序算法;sort、search、copy、erase..等
  • 迭代器(iterators):扮演容器与算法之间的胶合剂,即泛型指针
  • 功能函数(functors):行为类似函数,可以视为算法的某种策略
  • 适配器(adapters):一种用来修饰容器(containers)或仿函数(functors)或迭代器(iterators)借口的东西
  • 分配器(allocators):负责空间配置与管理

C++ 11开始

  • range-based for statement
1
2
3
for (decl : coll) {
	statement
}
  • auto 关键字

容器 —— 分类

  • Sequence Containers

    • Array image-20210723110741841

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      
      array<long, ASIZE> a;  // 大小为ASIZE,类型为long的数组a 
      qsort(a.data(), ASIZE, sizeof(long), compareLongs);   // cstdlib 库包含qsort,bsearch
      long* Item = (long*)bsearch(&target, (a.data()), ASIZE, sizeof(long), compareLongs); //二分查找(&target传引用),bsearch()返回的是target对应位置的指针
      array.data() //获得指向底层数组的指针
      
      int compareLongs(const void* a, const void* b) {
      	return ( *(long*)a - *(long*)b );
      }
      int compareStrings(const void* a, const void* b) {
      	if ( *(string*)a > *(string*)b ) {
              return 1;
      	} else if ( *(string*)a < *(string*)b ) {
              return -1;
      	} else 
              return 0;
      }
      
    • Vectorimage-20210723110704793 vector向后扩展是 两倍扩展的。

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      #include <cstdlib> // abort()
      #include <cstdio> 	// sprintf()
      #include <algorithm> // sort()
      vector<string>c;
      vector.size()     // vector当前实际占用大小
      vector.max_size() // 返回可容纳的最大元素数,此值通常反映容器大小上的理论极限,至多为 std::numeric_limits<difference_type>::max() 。运行时,可用 RAM 总量可能会限制容器大小到小于 max_size() 的值。
      vector.capacity() // 返回当前存储空间能够容纳的元素数,指在发生 realloc 前能允许的最大元素数,即预分配的内存空间。
      sort(c.begin(), c.end());
      snprintf(buf, 10, "%d", rand());//按照%d格式将rand()整数格式化为字符串,并将字符串复制到buf中,10为要写入的字符的最大数目,超过 10 会被截断。
      auto pItem = ::find(c.begin(), c.end(), target); // :: 表示 模板函数find()是全局函数,前序查找
      
    • List image-20210723112343104

      1
      2
      
      list<string>c;
      c.sort(); // 容器list自带的sort(优先使用)
      
    • Forward-List image-20210723113625765

      1
      
      forward_list<string>c;
      
    • slist (非标准库内的) 在gnu中的,与forward_list都相同。 __gnu_cxx::slistc;

    • dequeimage-20210723114752744 dequec;

      image-20210723114924743

      每个buffer(8个bit)连续,两边扩展。

  • Associative Containers

    • Set / Multiset

      1
      2
      3
      
      multiset<string>c;
      auto pItem = ::find(c.begin(), c.end(), target); 
      auto pItem = c.find(target); 						//比::find()快很多
      
    • Map / Multimap

  • Unordered Containers (不定序容器,也是关系容器的一种)

    • Unordered Set / Multiset

      1
      2
      3
      4
      5
      6
      
      unordered_multiset<string>c;
      c.bucket_count();
      c.load_factor();
      c.max_load_factor();
      c.max_bucket_count();
      // 与multiset有相同用法 c.find(),比::find()快很多【unordered_set、set 的用法与其相同】
      
    • Unordered Map / Multimap

      1
      2
      3
      
      unordered_multimap<string>c;
      c.insert(pair<long, string>(i, buf)); // multimap不可使用[] 进行insertion
      auto pItem = c.find(target); // long target = get_a_target_long(); 对应的value为(*pItem).second
      

image-20210617143635326

红色圈起的部分即为 C++11新增的。

image-20210723164034517

第二讲 源码(主要是容器)

OOP(Object-Oriented programming)vs. GP(Generic Programming)

  • OOP企图将datas和methods关联在一起

    为什么list不能使用::sort()排序? first + (last - first) /2 只有RandomAccessIterator才能如此操作

    image-20210723175412006

  • GP欲将datas和methods分开来 左边是数据,右边是全局的函数

    image-20210723175549120

    采用GP:

    • Containers和Algorithms团队可各自闭门造车,其间以Iterators连通即可
    • Algorithms通过Iterators确定操作范围,并通过Iterators取用Container元素

    image-20210723180644482

    所有algorithms,其内最终涉及元素本身的操作 无非就是比大小

技术基础 操作符重载 与 模板(特化、全特化、偏特化)

《C++ Template》第二版

类模板:

1
template <typename T>class complex {public:     complex (T r = 0, T i = 0) : re(r), im(i) {}        complex& operator += (const complex&);    T real() const {return re;}    T imag() const {return im;}private:    T re, im;        friend complex& _doapl (complex*, const complex&);};{    complex<double> c1(2.5, 1.5);    complex<int> c2(2, 6);    ....}

函数模板:

1
template <class T>inline const T& min(const T& a, const T& b) {	// 编译器对函数模板进行 `参数推导`    return b < a ? b : a;}

成员模板:

特化

出现template<> 则是特化

image-20220303170508356

image-20220303170720262

image-20220303170814639

偏特化

分类:

  1. 模板中有 多个参数,绑定部分参数;(个数的偏特化
  2. 范围的偏特化

image-20220303171042920

allocators分配器

malloc()的额外开销 在于:下图中 上下两个红色的部分(习惯叫 cookie,记录着这个分配空间的大小)

image-20220303185640506

VC6的标准库,其中allocator的实现中 operator new()调用malloc(), operator deallocate

allocator接口的难用之处在于 要指出当时分配的内存大小即 size_type. 容器用它则没有这个问题。(为什么? 封装好了底层自动完成吗?

image-20220303184446313

image-20220303184650956

BC5标准库的 allocator实现:

image-20220303184902098

image-20220303185144443

VC, BC, G2.9的 allocator都是以 ::operator new和::operator delete完成allocate() 和 deallocate(),没有任何特殊设计。

G 2.9 STL对allocator的使用:

image-20220303185525995

G 2.9中alloc分配器的实现:(减少了前面allocator的额外开销(cookie))一次分配很大的一块内存,然后根据申请的空间划分成小块的内存,用单向链表连接起来。

image-20220303185558473

但是G4.9版本中没有使用alloc分配器。 而是std::allocatorimage-20220303190356565

image-20220303190424425

G 4.9所附的标准库有很多 extention allocators,其中**__pool_alloc 就是G2.9的alloc**。

image-20220303190642170

为什么默认使用的是 std::allocator 而不是更省空间的 __pool_alloc? 不知道。

容器,结构与分类

下图中 以 缩排的形式表示 “基层与衍生层”的关系,这里的衍生 是 复合关系, 而不是继承。

image-20211213212056517

容器list

每次为list申请分配空间的时候,会为每个节点多申请两个指针的空间 存储 prev 和 next。

注意:__list_node中的 pointer类型是 void*

image-20220303191641071

除了vector和array以外的 所有容器中的 iterator 必须是一个 class 才能是一个 智能指针。

list的iterator

iterator ++

image-20220303192710656

image-20220303192737214

**整数类型 不能使用后加加 加两次 即 i++++是不允许的。**所以为了模拟int类型的操作,(后缀形式,后加加) operator++(int)的返回类型是 self,但 (前缀形式,前加加)operator++() 的返回类型是self &后缀形式调用前缀形式来实现 加法(这样的方法在STL中经常使用)

image-20220303193711364

image-20220303193900503

G4.9的模板参数只有一个了(容易理解)。node的成员的type更精确了,而不是void*.

image-20220303194241602

list的类之间的关系更加复杂了。(所以用G2.9版本作为讲解

迭代器的设计规则和Iterator Traits的作用与设计

Iterator Trait 是人为设计的一种萃取机(萃取特征)

  • iterator_category 指的是 iterator的移动性质(++, –, += 3)。

  • value_type指的是实际元素的类型。

  • difference_type 指的 是 两个元素之间的距离对应的类型 。

  • 另外两种(这两种在算法中从未使用过)分别是:reference_type

  • pointer_type

image-20220303194836307

算法提问, 迭代器回答。

image-20220303195621193

指针pointer 视作是一种退化的iterator,iterator是一种泛化的pointer。

Traits特性

Iterator Traits是一个萃取机。 用于**分离 classs iterators 和 non-class itertors **

image-20220303195646320

银弹)即 解决计算机问题的尚方宝剑:加一个中介层。(这里的中介层 就是 Iterator Traits)

image-20220303200435147

上图中 第三个 iterator_traits 里的声明是 typedef T value_type,而不是const T的原因是:若声明为const T,那么无法被赋值 就没有用了。

image-20220303201028194

各种各样的Traits

image-20220303201616790

容器vector

image-20220303201750758

sizeof(vector<T>) 的大小为 12(32位操作系统,在64位操作系统上。即为3个指针 start, finish, end_of_storage的大小)

image-20220303203736142

image-20220303203754788

vector的iterator

image-20220303204122528

image-20220303204435316

image-20220303204646094

image-20220303204849892

容器array

TR1:Technical Report 1. C++98与C++11之间的过渡版本(大概是2003年)。

array 没有 ctor,也没有dtor。

像array、vector连续空间的容器,可以 直接用单纯的指针作为iterator。

image-20220303204958134

**typedef int T[100]; **

T c;

image-20220303205155360

容器forward_list

image-20220303210117026

容器deque

deque号称是连续的,实际上是分段连续的。

可以双向扩充,每个buffer对应一个buffer。

deque是分段连续的,为了维持连续的表象,迭代器走到边界(first或者last)的时候需要有能力(利用node)跳到下一个元素对应的空间位置。

image-20220303210131160

map_size 是以两倍的速度增长的(就是一个vector)。

image-20220303210158039

sizeof(deque<T>) = 16 + 16 + 4 + 4 = 40(32位操作系统)

image-20220303211411111

deque iteraotr里面有四个指针 即cur、first、last和node。因此sizeof(__deque_iterator<T>)的大小是16(32位操作系统)

deque<T>::insert()

逻辑:

  • 如果插入的位置在deque最前端,则直接push_front();
  • 如果插入的位置在deque 最尾端,则直接push_back();
  • 否则 判断插入的位置是靠近 前半部分 还是 后半部分,然后移动相应部分的元素,最后插入元素。

image-20220303212223671

image-20220303212243391

deque如何模拟连续空间

全是deque iterators的功能。 求size() 中的 finish - start 的减号经过重载的

image-20220303212821984

image-20220303213052092

image-20220303213334651

+= (-= 通过调用 += 来实现的)

image-20220303213902847

image-20220303214135640

G4.9版本中的deque

image-20220303214338131

image-20220303214555811

deque中的map 内存大小是 两倍增长的;而两端剩余未使用的内存空间会保持相等,也就是会通过移动map的元素到中段,以保证deque可以两端扩充

容器queue

queue的内部使用了 deque。 (因为是将deque改装了,自己实际上没做什么事,所以有时称为“适配器”)

image-20220303214843423

容器stack

同queue一样,内部使用了deque。

image-20220303215123081

queue和stack 关于其iterator和底层结构

stack 和 queue都不允许遍历,也不提供iterator【编译错误】。

  • stack和queue都可以选择list或deque为底层结构。(但是deque作为底层结构比较快!)
  • stack可以选择vector作为底层结构,queue 不可以选择vector作为底层结构(pop()会调用pop_front() 发生编译错误!)
  • stack和queue都 不可以选择set或map作为底层结构。

image-20220303215249985

image-20220303215338742

image-20220303215800847

[关联式容器]容器rb_tree

前面讨论的list、vector、array、forward_list、deque、queue和stack都是序列式容器。 关联式容器查找非常快,类似一个小型数据库。

rb_tree的两个底层结构:红黑树(高度平衡的二叉搜索树,有利于查找),哈希表

rb_tree提供“遍历”操作 及 iterators。

我们不应使用rb_tree的iterators改变元素值(因为元素其有其最严谨排列规则)。 map允许 元素的data被改变,只有 元素的key才是不可被改变的。

image-20220303220203244

image-20220304092217256

key和data 组成 value。

image-20220304092249424

identity仿函数:实现获取自身元素。

容器rb_tree用例

image-20220304092314104

image-20220304092326111

image-20220304092533802

image-20220304092555251

容器set,multiset

  • set和multiset以rb_tree为底层结构,因此 有 元素自动排序特性,排序的依据是key(key就是value)。

  • 提供遍历操作及iterators,按 ++iter遍历 就能得到排序状态值。

  • 无法使用set/multiset的iterators(实际实现中的iterator是 const_iterator,见下图)改变元素值(因为key就是value);

  • set和multiset的区别在于 multiset的key可以重复。

    • set元素的key必须独一无二,其insert()用的是rb_tree的insert_unique();
    • multiset元素的key可以重复,其insert()用的是rb_tree的insert_equal()。

image-20220304092642231

set底层实现中的后两个模板参数有默认值 less<Key> 和 alloc。

image-20220304093357549

set的所有操作,都是让底层t操作,从这个意义上来看,set是个container adapter。

容器set in VC6

上面讲述的identity()是 GNUC特有的。VC6不提供identity(),set和map使用RB-tree的时候 则自己实现identity()即下图中的_Kfn

image-20220304094057492

使用容器multiset

image-20220304094327370

容器map, multimap

  • map和multimap以rb_tree为底层结构,因此 有 元素自动排序特性,与set/multiset不同的是,其key和data合成value,而不是(key就是value,value就是key)

  • 提供遍历操作及iterators,按 ++iter遍历 就能得到排序状态值。

  • 无法使用map/multimap的iterators改变key,但是能改data

  • map和multimap的区别在于 multimap的key可以重复。

    • map元素的key必须独一无二,其insert()用的是rb_tree的insert_unique();
    • multimap元素的key可以重复,其insert()用的是rb_tree的insert_equal()。

image-20220304094630924

map的底层实现中后两个模板参数是默认的 即 less<Key>和alloc。 下图例子中key的类型是int,data的类型是string。

通过map的iterator可以修改data,不能修改key。下图实现中的iterator就是rb_tree的data。pair<const Key, T> value_type 中的Key是const类型,所以不能改变key。

select1st() 是GNUC独有的。

image-20220304095354385

容器map,in VC6

VC6通过**自己实现 _Kn(G2.9中的select1st()功能)**来使用RB-tree。

image-20220304095601872

使用multimap

小技巧:每个测试用例包括在一个不同的namespace里。

下图用例的 key是[0, value), 不会重复,虽然multimap允许key重复。

multimap不可使用[] 做 insertion。

image-20220304100243096

容器map独有的operator[]

返回与key对应的value。如果key不存在,将创建一个具有默认值的key 并返回。

image-20220304100609232

使用容器map

image-20220304101337077

红黑树:set和map的底层结构。

容器hashtable

image-20220304101453611

发生碰撞时,使用链表来处理冲突,称为链式哈希。

bucket(大小一般是质数, 在GUNC是53),如果一个链表的长度大于buckets vector的长度,那么一般将 bucket vector长度翻倍(最近的一个质数,如97)(经验)。 这样的操作称 rehash时间消耗花费在重新计算位置并拷贝移动上

image-20220304101505190

下图的HashFun用于获取哈希编号。 ExtractKey用于从pair中提取处key,EqualKey用于Key的判断是否相等。

三个对象hash、equals和get_key的内部没有元素,但其实际大小为1字节,所以总共3字节。buckets是一个vector,里面有三个指针,12个字节。size_type 4个字节。因此 sizeof() = 1 * 3 + 4 * 3 + 4 + 1(对齐)= 20.

GNUC中链表是单向链表,VC6的实现是双向链表,确保能够进入下一个bucket。

image-20220304101523997

使用hashtable

hashtable<const char*, const char*, hash<const char*>, identity<const char*>, **eqstr**, alloc> 中 为什么是eqstr, 而不能是strcp(),这是因为 **hashtable中要求其返回值是bool类型,而不能传回的是-1, 0, 1**,所以必须加一层外套。

hashtable使用的关键:HashFun如何设计(即 将一个对象或字符串转换成一个编号)? —— 没有统一的计算方法,只要编号够乱,不发生碰撞即可。

image-20220304101536566

hash-function,hash-code

数值就作为编号。

image-20220304101548054

hash function的目的:希望根据元素值计算出一个 hash code一个可以进行modulus运算的值),使得元素经hash code映射之后能够 【够乱】地被置于hashtable内。 越乱越不容易发生碰撞

image-20220304101558674

modules运算

image-20220304105503797

hash(key) % n中的hash是 class hashtable中的hashser hash

image-20220304105854315

hashtable用例

image-20220304110017861

image-20220304110127524

容器unordered_set, unordered_multiset, unordered_map, unordered_multimap

C++11开始改名为 unordered_set, unordered_multiset, unordered_map, unordered_multimap

image-20220304110308491

使用unordered_set

image-20220304120245941

第三讲 体系结构与内核分析

C++标准库的算法 是什么?

从语言层面讲:

  • 容器Container是个class template;
  • 算法Algorithm 是个function template
  • 迭代器Iterator是个class template;
  • 仿函数Function是个class template;
  • 适配器Adapter是个class template;
  • 分配器Allocator是个class template。

image-20220304120635893

迭代器的分类(category)

移动能力的分类:

  • input_iterator_tag
  • output_iterator_tag
  • forward_iterator_tag: forward_list
  • bidirectional_iterator_tag:不能跳
  • random_access_iterator_tag :可以跳

image-20220304121450053

image-20220304121833735

各种容器的iterators的iterator_category

  • array => random_access_iterator
  • vector => random_access_iterator
  • list => bidirectional_iterator
  • forward_list => forward_iterator
  • deque => random_access_iterator

set, map, multiset, multimap都是以红黑树为底层结构

  • set => bidirectional_iterator
  • map => bidirectional_iterator
  • multiset => bidirectional_iterator
  • multimap => bidirectional_iterator
  • unordered_set => forward_iterator
  • unordered_map => forward_iterator
  • unordered_multiset => forward_iterator
  • unordered_multimap => forward_iterator

特殊的两个iterator

  • istream_iterator => input_iterator
  • ostream_iterator => output_iterator

各种容器的iterators的iterator_category的typeid

可以将typeid()视作C++的一种操作符,获得一个对象, typeid().name()获得对象的名称。

返回值由实现所定义,实际输出取决于库的实现(但具体哪种iterator是相同的)。

image-20220304122917946

虽然G2.9, G3.3, G4.9每个版本的具体实现不太相同,但是不影响接口的使用(接口一样)。比如 其模板参数都只需要传入一个参数,其他的都有默认值。

image-20220304123344593

image-20220304123551353

iterator_category对算法的影响

distance() 这个函数模板用于 求两个iterator直接的距离。

  • 如果是连续空间(random_access_iterator_tag表示),则调用__distance(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag) 直接对两个iterator做减法;
  • 如果是 非连续空间(input_iterator_tag表示),则调用__distance(InputIterator first, InputIterator last, input_iterator_tag),遍历iterator并计数返回

第一种的效率更高。

distance()的返回类型是 iterator_traits<InputIterator>::difference_type是考虑到了不同情况下返回类型可能不同。

image-20220304124158224

__advance()

  • 可以跳,则调用 __advance(RandomAccessIterator& i, Distance n, random_access_iterator_tag)
  • 双向,则调用__advance(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag)
  • 否则 调用__advance(InputIterator& i, Distance n, input_iterator_tag)。

iterator_category()是一个辅助函数,功能与前面的iterator_traits<InputIterator>::iterator_category相同。

image-20220304130057229

iterator_category和type traits对算法的影响

Type Traits在第四讲。

image-20220304131246326

image-20220304132804461

destroy()的源代码:

image-20220304132937866

output_iterator(比如 ostream_iterator)是 write-only(只可写的),无法像forward iterator那样可以 read,因此不能有类似(右侧)*result != *first的动作,因此设计出(左侧)的版本

image-20220304133038932

算法源码中对 iterator_category的“暗示”

模板参数类的命名提示 开发者需要传入的iterator需要满足该类型,否则后面代码可能会编译不通过

image-20220304133729807

算法源代码剖析(11个例子)

先前示例中出现的算法:

image-20220304134100105

1. 算法accumulate

接受三个参数,first和last迭代器,初始值init

image-20220304134153389

2. 算法for_each

接受三个参数,first和last迭代器,函数f

C++11开始

1
for(decl : coll) {	statement}

image-20220304134559579

3. 算法replace, replace_if, replace_copy

image-20220304134955349

4. 算法count, count_if

左边是全局函数,右边是容器的成员函数。

image-20220304135156576

5. 算法find,find_if

image-20220304135345776

6.算法sort

image-20220304135401426

image-20220304135413041

必须先排序后再进行二分。

image-20220304140448465

仿函数functors

仿函数只为算法服务。 Algorithm(Iterator itr1, Iterator itr2, Cmp comp)里的Cmp即为仿函数类。

  • 算术类
  • 逻辑运算类
  • 相对关系类

image-20220304141115909

image-20220304141600985

算法sort()的Cmp可以是 函数myfunc,也可以是函数对象myobj。 传入的less<int>()和greater<int>()表示一个对象。

image-20220304141809876

仿函数functors的可适配(adaptable)条件

image-20220304142219085

less继承了binary_function中的三个typedef。

适配器Adapters

想要A拥有B的功能,有两种方法:继承 或 复合(composition,拥有)。

Adapters中使用 复合的方法。 Functor Adapters内含了Functors,其他适配器类似。

image-20220304143248121

容器适配器stack,queue

如下图:stack和queue都内含了一个deque

image-20220304143258642

函数适配器binder2nd

typedef typename Operation::second_argument_type arg2_type;

编译过程中不知道Operation是什么, 使用typename保证Operation是一个类型,保证编译能通过

image-20220304143312106

image-20220304143322383

下图中用左侧的 代替 右侧的。

image-20220304143334674

新型适配器bind(Since C++11)
1
#include <functional> // std::bind

std::bind 可以绑定:

  1. functions
  2. function objects
  3. member functions, **_1(表示占位符)**必须是某个object地址;
  4. data_members,_1必须是某个object地址

image-20220304143348035

迭代器适配器reverse_iterator

rend()对应的元素取begin()的前一个,rbegin()对应的元素取end()的前一个。

image-20220304143359754

迭代器适配器:inserter

inserter在list中直接插入另一个list(可能插入位置后面的空间不够copy插入的,具体如下例:)

image-20220304143410760

X(未知)适配器

ostream_iterator

image-20220304143423208

istream_iterator

istream_iterator中的 数据成员in_stream使得 下面的例子能够实现 cin»value,然后进行乘法操作的功能。

当创建对象iit时(std::istream_iterator<double> iit(std::cin); ),立即调用了++*this,然后读入了数据value。

image-20220304143435280

image-20220304143455593

使用一个东西,却不明白它的道理,不高明!

image-20220304162827374

上图是使用模板 写错一行代码(但是报了一堆的错误)的示例。

第四讲 体系结构与内核分析

勿在浮沙筑高台。

一个万用的Hash Function

左边的CustomerHash在unordered_set<Customer, CustomerHash> custset; 中被当作函数调用,调用的是CustomerHash类中的成员函数operator().

image-20220304163033532

1
// 辅助函数template <typename... Types> 	// typename... 接受任意个模板参数 (称为variadic templates 即 可变化的模板)inlinesize_t hash_val(const Types&... args) {    size_t seed = 0;    hash_val(seed, args...);    return seed;}

下图中1,2,3对应的hash_val()函数由于函数参数的不同,所以是不同的函数(重载的一种)。根据具体参数调用不同的函数。

image-20220304163621029

关于0x9e3779b9的来源:

image-20220304170535403

实际实现

image-20220304170655575

image-20220304170706636

struct hash偏特化形式实现Hash Function

image-20220304182025366

image-20220304182134955

tuple用例

image-20220304183145646

tie(i1, f1, s1) = t3; 将t3的数据取出来赋值

下图中

1
template<typename... Values> class tuple;template<> class tuple<> {};	// 终止条件// 接受任意个Tail类template<typename Head, typename... Tail>class tuple<Head, Tail...> : private tuple<Tail...> {	// 继承它自己,形成递归	typedef tuple<Tail...> inherited;public:    tuple() {}    tuple(Head v, Tail... vtail) : m_head(v), inherited(vtail...) {}        typename Head::type head() { return m_head; }    inherited& tail() { return *this; }protected:     Head m_head;}; 

Tuple,元之组合,数之组合

Modern C++ Design

image-20220304183232556

type traits

image-20220304183242063

image-20220304183252272

image-20220304183309685

image-20220304183328527

image-20220304183338971

image-20220304183351806

image-20220304183409114

1
Zoo(const Zoo&) = delete;	// 拷贝构造函数,delete表示写出来就delete了Zoo(Zoo&&)  = default; 	// && move, 这是move constructorZoo& operator=(const Zoo&) = default;	// 拷贝赋值Zoo& operator=(const Zoo&&) = delete;

image-20220304183420862

复数只有实部和虚部。 不用写析构函数,默认生成,不重要,所以**__has_trivial_destructor = 1。**

image-20220304183431224

image-20220304183440134

type traits,实现 is_void

特化是泛化的范围缩小。

image-20220304183450297

type traits,实现 is_integral

image-20220304183503000

type traits,实现is_class, is_union, is_enum, is_pod

这些在 C++标准库源代码中没有出现,可能是编译器做的这些事。

image-20220304183516673

type traits,实现is_move_assignable

image-20220304183528895

cout

1
extern _IO_ostream_withassign cout; 	// extern表示可以被外部文件看见

image-20220304183540863

image-20220304183549041

moveable元素对各种容器速度效能的影响

速度跟机器实际内存也有关系。

1
M c1;...M c11(c1);	// copyM c12(std::move(c1)); // move copy  c11.swap(c12);

image-20220304183600752

image-20220304183616768

image-20220304183628442

image-20220304183641622

image-20220304183705185

写一个moveable class

image-20220304183809785

image-20220304183820200

move copy浅拷贝,只是将新的指针指向原来的数据。

copy深拷贝,将数据和指针都移动到新的位置。(必须保证原来的不会被用到

image-20220304183832184

vector本身的拷贝copy ctor是深拷贝

image-20220304183843324

vector的move ctor是浅拷贝,只是swap三根指针。

image-20220304183852112

image-20220304202553363

参考资料