此篇是 配合cppreference-zh-20200816.chm学习

<vector>, <stl_vector.h>和<vector.tcc>源码的笔记


环境准备

  1. 下载MinGW
  2. MinGWlibgccmingw326.3.0文件夹下面有静态链接库和头文件
  3. 在MinGWlibgccmingw326.3.0includec++中的目录结构如下:
  • gnu对C++ standard library的扩展的头文件路径 profile, parallel, mingw32, ext
  • C++ std lib 内部用头文件路径 backward, bits(stl_vector.h, vector.tss)
  • C++ std lib 外部用头文件路径 experimental (在cppreference中可以看到该部分TS划分出来了一整个模块,其中实例都是需要包含该文件夹), 以及根目录下<vector>等是默认引用的路径的头文件

根目录下<vector>包含如下内容。下面笔记中包含代码行号,因此最好用vscode等编辑器打开,方便快速定位

/** @file include/vector*  This is a Standard C++ Library header.*/#ifndef _GLIBCXX_VECTOR
#define _GLIBCXX_VECTOR 1#pragma GCC system_header#include <bits/stl_algobase.h>
#include <bits/allocator.h>
#include <bits/stl_construct.h>
#include <bits/stl_uninitialized.h>
#include <bits/stl_vector.h>
#include <bits/stl_bvector.h>
#include <bits/range_access.h>#ifndef _GLIBCXX_EXPORT_TEMPLATE
# include <bits/vector.tcc>
#endif#ifdef _GLIBCXX_DEBUG
# include <debug/vector>
#endif#ifdef _GLIBCXX_PROFILE
# include <profile/vector>
#endif#endif /* _GLIBCXX_VECTOR */

从其中的bits/stl_vector.h(成员函数声明), bits/vector.tcc入手

库文件读起来和普通程序相比有些吃力,所以下面有意识适应以下两个细节

  • 内部变量用__作为前缀
  • 有很多用于条件编译的宏,多与平台移植性,版本兼容性有关,暂时不深究其细节

stl_vector.h中对关键变量的声明

在名称空间

namespace std _GLIBCXX_VISIBILITY(default)

中,有两个结构体,首先是

  template<typename _Tp, typename _Alloc>struct _Vector_base

定义体的开始,74-77行声明两个类型别名

      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::templaterebind<_Tp>::other _Tp_alloc_type;typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointerpointer;

其中__alloc_traits和_Alloc = std::allocator<_Tp>涉及内存管理,为了控制内容不至于太琐碎,新开了篇

No.9:C++ vector成员函数实现-alloc相关​zhuanlan.zhihu.com

深挖下关于内存部分的细节。

继续,其中164行有个十分重要的成员,

_Vector_impl _M_impl;

定义在79-107行,三个指针 成员变量十分重要,分别指向首个成员,尾个成员,以及分配最后一块内存

struct _Vector_impl : public _Tp_alloc_type{pointer _M_start;pointer _M_finish;pointer _M_end_of_storage;_Vector_impl(): _Tp_alloc_type(), _M_start(), _M_finish(), _M_end_of_storage(){ }_Vector_impl(_Tp_alloc_type const& __a) _GLIBCXX_NOEXCEPT: _Tp_alloc_type(__a), _M_start(), _M_finish(), _M_end_of_storage(){ }#if __cplusplus >= 201103L_Vector_impl(_Tp_alloc_type&& __a) noexcept: _Tp_alloc_type(std::move(__a)),_M_start(), _M_finish(), _M_end_of_storage(){ }
#endifvoid _M_swap_data(_Vector_impl& __x) _GLIBCXX_NOEXCEPT{std::swap(_M_start, __x._M_start);std::swap(_M_finish, __x._M_finish);std::swap(_M_end_of_storage, __x._M_end_of_storage);}};

189行~1494行 为继承自它的vector

  template<typename _Tp, typename _Alloc = std::allocator<_Tp> >class vector : protected _Vector_base<_Tp, _Alloc>

其中_Alloc默认提供了std::allocator<_Tp>,下面用到会对此分析


vector.tss中对vector成员函数的实现

  • reserve
template<typename _Tp, typename _Alloc>voidvector<_Tp, _Alloc>::reserve(size_type __n){if (__n > this->max_size())__throw_length_error(__N("vector::reserve"));if (this->capacity() < __n) // 当前容量比需要分配的小{const size_type __old_size = size();// 记录当前数据部分长度pointer __tmp = _M_allocate_and_copy(__n,_GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_start),_GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_finish));std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,_M_get_Tp_allocator());_M_deallocate(this->_M_impl._M_start,this->_M_impl._M_end_of_storage- this->_M_impl._M_start);this->_M_impl._M_start = __tmp;this->_M_impl._M_finish = __tmp + __old_size;this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;}}

有三个函数比较困惑,但是可以很容易推测其含义:

如下函数是新分配__n大小容量内存,并将原有数据拷过来,返回新的首地址

pointer __tmp = _M_allocate_and_copy(__n,_GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_start),_GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_finish));

下面函数用于销毁原有的分配器,并释放原有内存,这似乎暗示了vector不止分配了用于存储数据存储的内存,即CppReference中vector章节的

vector 通常占用多于静态数组的空间,因为要分配更多内存以管理将来的增长。

std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,_M_get_Tp_allocator());_M_deallocate(this->_M_impl._M_start,this->_M_impl._M_end_of_storage- this->_M_impl._M_start);


研究下其具体实现,在<stl_vector>中

/***  Memory expansion handler.  Uses the member allocation function to*  obtain @a n bytes of memory, and then copies [first,last) into it.*/template<typename _ForwardIterator>pointer_M_allocate_and_copy(size_type __n,_ForwardIterator __first, _ForwardIterator __last){pointer __result = this->_M_allocate(__n);__try{std::__uninitialized_copy_a(__first, __last, __result,_M_get_Tp_allocator());return __result;}__catch(...){_M_deallocate(__result, __n);__throw_exception_again;}}

进一步,在基类166-171行中如下函数实现,用于分配并返回最后一块内存指针

      pointer_M_allocate(size_t __n){typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();}

于是就到了__alloc_traits<Tp_alloc_type>::allocate()函数,传入两个参数分别是:继承自_Tp_alloc_type得到的结构体,预期容量大小。

其实读到这,就可以进一步把 _Vector_base的如下两个成员函数(173-188)也一并看下,因为除了此三个,剩下的都是用他们仨实现的构造或析构函数。

      void_M_deallocate(pointer __p, size_t __n){typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;if (__p)_Tr::deallocate(_M_impl, __p, __n);}private:void_M_create_storage(size_t __n){this->_M_impl._M_start = this->_M_allocate(__n);this->_M_impl._M_finish = this->_M_impl._M_start;this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;}

重点在__gnu_cxx::__alloc_traits<_Tp_alloc_type>::deallocate(_M_impl, __p, __n)

这个与上面分配的函数,一并在另一篇辅助理解的笔记中深挖。


关于_M_get_Tp_allocator如下,实际是获取M_impl地址并强制转换成其父类

      _Tp_alloc_type&_M_get_Tp_allocator() _GLIBCXX_NOEXCEPT{ return *static_cast<_Tp_alloc_type*>(&this->_M_impl); }const _Tp_alloc_type&_M_get_Tp_allocator() const _GLIBCXX_NOEXCEPT{ return *static_cast<const _Tp_alloc_type*>(&this->_M_impl); }allocator_typeget_allocator() const _GLIBCXX_NOEXCEPT{ return allocator_type(_M_get_Tp_allocator()); }

reserve函数实现就是这样,其他的后面再补充

swap函数_C++ vector成员函数实现[持续更新]相关推荐

  1. “vector”: 不是“std”的成员_C++ vector成员函数实现[持续更新]

    此篇是 配合cppreference-zh-20200816.chm学习 <vector>, <stl_vector.h>和<vector.tcc>源码的笔记 环境 ...

  2. 10.2 运算符重载函数作为类成员函数和友元函数

    Complex operator+(Complex &c2) { Complex c; c.real=real+c2.real; c.imag=imag+c2.imag; return c; ...

  3. C++运算符重载函数作为类成员函数和友元函数

    C++运算符重载函数作为类成员函数 1.1成员函数的重载运算符左侧是一个类对象,而且与运算符函数类型相同. 因为必须通过类的对象去调用该类的成员函数,而且只有运算符重载函数和返回值类型相同,运算结果才 ...

  4. C++类的成员函数(在类外定义成员函数、inline成员函数)

    类的成员函数(简称类函数)是函数的一种,它的用法和作用和前面介绍过的函数基本上是一样的,它也有返回值和函数类型,它与一般函数的区别只是:它是属于一个类的成员,出现在类体中.它可以被指定为private ...

  5. 类的成员函数指针和静态成员函数指针 调用成员函数***

    C++提供static这个关键词对静态成员进行声明,静态成员函数和类的实例化无关,对于同一类来说,静态成员函数是所有类的对象共享的.而普通成员函数需要实例化才能调用,对于每一个实例来说,普通成员函数是 ...

  6. 【C++】类和对象【中篇】--C++六个默认成员函数以及const成员函数

    文章目录 一.类的6个默认成员函数 二.构造函数 1.概念 2.特性 2.1特征分析--自动生成 2.2.特征分析--选择处理 2.3特征分析--默认构造 3.C++11补丁--缺省值 三.析构函数 ...

  7. 【C/C++学院】(8)全局函数和类成员函数转化/友元/操作符重载

    1.全局函数和类成员函数转化 全局函数和成员函数的相互转化:只需要修改一个指向本类的this指针: #include <iostream> using namespace std;clas ...

  8. [Reprint]C++普通函数指针与成员函数指针实例解析

    这篇文章主要介绍了C++普通函数指针与成员函数指针,很重要的知识点,需要的朋友可以参考下 C++的函数指针(function pointer)是通过指向函数的指针间接调用函数.相信很多人对指向一般函数 ...

  9. C++深度解析 类中的函数重载 -- 全局函数,普通成员函数,静态成员函数(28)

    C++深度解析 类中的函数重载 -- 全局函数,普通成员函数,静态成员函数(28) 函数重载的回顾 函数重载的本质为相互独立的不同函数 C++中通过函数名和函数参数确定函数调用 无法直接通过函数名得到 ...

最新文章

  1. 基于先验LiDAR点云地图的单目VIO定位(IROS 2020)
  2. 查看 mysql 占用的内存大小_mysql查看数据库和表的占用空间大小
  3. 国货之光业务增长背后的技术支持 - 完美日记的云原生实践
  4. Windows如何查看端口占用
  5. spring aop与strut2的拦截器冲突
  6. Oracle数据库连接、退出缓慢问题查询与处理
  7. django模型_Django模型
  8. 冲击IPO:达达的负“重”上市之路
  9. 各自然带代表植被_各气候带对应植被
  10. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP
  11. 数据仓库建设规范(文档版)
  12. win10怎么共享打印机_win10怎么连接局域网打印机
  13. Microsoft Excel 教程:如何在 Excel 中创建图表?
  14. 《视频解密》中文版(第四版) 第七章 数字视频处理(第一部分)
  15. Android软件TOP10排行榜
  16. web开发中前端页面是如何跟后端服务器数据交互的
  17. ucore - 第一章 - bootloader启动过程
  18. MBR和GUID分区表
  19. 驰骋BPM系统-表单引擎-流程引擎 页面更换
  20. wParam和lParam参数

热门文章

  1. mysql 8.0 postgresql_PostgreSQL8.0的安装和配置- -
  2. python3.6 try except,python中try except处理程序异常的三种常用方法
  3. P1546 最短网络 Agri-Net
  4. win10 Java JDK环境变量配置
  5. 【模板】线段树区间修改
  6. Oracle 拼音码函数
  7. OGNL、EL表达式——Struts
  8. OpenCV在Linux上的安装及初试
  9. Ptrace 一个Linux强大的工具
  10. mysqlreport