首页 > 编程语言 >C++ STL源码个人学习与分析记录 ——Construct()与Destroy()

C++ STL源码个人学习与分析记录 ——Construct()与Destroy()

时间:2024-08-23 18:57:31浏览次数:12  
标签:__ 函数 STL C++ 源码 Construct Destroy

STL源码个人学习与分析记录 ——Construct()与destroy()

  作为一位C++程序员,直接使用STL标准中的各类容器(vector,list,map,hash)以及各种泛型算法完成一些任务是十分正常而又便捷的。但是,只会使用工具而不知道工具的原理和构造方法,那肯定是不可能完全搞懂和掌握手中这些强力的工具的。

  为了搞懂这些STL容器的底层逻辑和代码结构,想找找有没有相关的介绍书籍,找来找去只有这本《STL源码分析》,一看作者,侯捷老师,好嘛,这不是《Effective C++》的中文译者,看来又可以享受和领略侯老师独特的构词造句了,哈哈哈。

1. 目前所使用的编译器

1.1 编译器:MinGW Version:13.2.0

  MinGW(Minimalist GNU for Windows) 是一个用于 Windows 平台的开发工具集,它提供了一组 GNU 工具和库,可以用于编译和构建本地的 Windows 应用程序。MinGW 的目标是在 Windows 环境下提供类似于 Unix/Linux 环境下的开发工具,使开发者能够轻松地在 Windows 上编写和编译 C、C++ 等程序。

1.2 MinGW的主要组件

  • 1) GCC(GNU Compiler Collection): GCC 是一个开源的编译器套件,支持多种编程语言,包括 C、C++、Fortran 等。在 MinGW 中,GCC 被用来编译和生成 Windows 平台下的可执行文件。

  • 2) Binutils:Binutils 是一组用于处理二进制文件的工具,包括汇编器、链接器、目标文件处理器等。在 MinGW 中,Binutils 用于将编译后的源代码转换为可执行文件。

  • 3 )运行时库(Runtime Libraries): MinGW 提供了 Windows 下所需的 C 和 C++ 运行时库,这些库是在编译和链接时所需要的,以便在 Windows 环境下运行程序。

  • 4)MSYS(Minimal SYStem): MSYS 是一个轻量级的 Unix-like 环境,它在 Windows 上提供了一些基本的 Unix 命令行工具,使开发者能够更方便地使用命令行进行开发和构建。

  MinGW 可以与其他开发工具集(如 Visual Studio)一起使用,但它的重点是提供一个简单的方式来在 Windows 上进行开发,无需依赖复杂的集成开发环境(IDE)。MinGW 的使用可以让开发者更接近标准的开发环境,同时也方便了跨平台的开发。

1.3 写文初衷

  在阅读这本书之前其实存在一定的疑虑,最主要的原因为该书的发行时期,2002年,距我目前已有22年之隔,某呼上对该书的评论中也主要指出该书中的代码已经过时,不应当再阅读此书。但思索再三,目前该本教材的内容正是我所需求的,而书中的那句“源码之下,毫无秘密”也提醒了我,既然书中代码已经过时,那为何不直接阅读已经安装的编译器GCC中的STL源代码文件呢?因此,顺手写一篇博客记录一下阅读源码的过程。

2.构造与析构工具:Construct()与Destory()函数的定义

  书中提醒到,SGI STL版本的Construct()与Destory()函数定义在<stl_construct.h>文件中,搜寻了MinGW的安装空间,也找到同名的文件(相对路径为 “…\MinGW\include\c++\13.2.0\bits”,其中“13.2.0”为我所使用的编译器版本),以下是对应于书中源码的部分代码截取。

2.1 Construct()函数的定义

#if __cplusplus >= 201103L //如果当前使用的C++标准为C++11以及更高标准的话,则可以定义一个可变参数的函数模版
  template<typename _Tp, typename... _Args>
    _GLIBCXX20_CONSTEXPR
    inline void
    _Construct(_Tp* __p, _Args&&... __args) //传入一个指针__p指向已经分配的内存空间,以及一个可变参数_Args
    {
#if __cplusplus >= 202002L
      if (std::__is_constant_evaluated())
	{
	  // Allow std::_Construct to be used in constant expressions.
	  std::construct_at(__p, std::forward<_Args>(__args)...); //如果C++标准为C++20及其以上且当前函数调用的上下文环境为常量时,调用std::construct_at()完成构建工作
	  return;
	}
#endif
      ::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
      //否则就调用全局定位new函数,在指针__p所指内存空间调用类型_Tp的带有初始化器的构造函数实现对象的构造。
    }
#else //如果所使用的C++标准低于C++11,则定义下述的Construct函数,因为可变参数是C++11引入的新特性!!
  template<typename _T1, typename _T2>
    inline void
    _Construct(_T1* __p, const _T2& __value)
    {
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // 402. wrong new expression in [some_]allocator::construct
      ::new(static_cast<void*>(__p)) _T1(__value);
    }
#endif

2.1.1 “__cplusplus”的含义

__cplusplus是一个预定义宏,用来指明当前编译器在使用的C++标准版本,它由编译器自动定义。以下是常见的“__cplusplus”的值

  • C++98/03: 199711L
    该数值对应于原始的 C++98 standard (ISO/IEC 14882:1998) 和它的 2003 revision (C++03).

  • C++11: 201103L
    该值对应于 C++11 standard (ISO/IEC 14882:2011).

  • C++14: 201402L
    该值对应于 C++14 standard (ISO/IEC 14882:2014).

  • C++17: 201703L
    该值对应于 C++17 standard (ISO/IEC 14882:2017).

  • C++20: 202002L
    该值对应于 C++20 standard (ISO/IEC 14882:2020).

  • C++23: 202302L
    该值对应于 C++23 standard ( ISO/IEC 14882:2023).

2.1.2 编译组态

  由于不同的编译器厂商对C++语言特性的支持程度不同,为了使得程序库能够拥有较强的移植能力,不同版本的STL标准库都会准备一个环境组态文件来定义一系列的常量,以表示该组态能否成立。书中给出了具体的案例说明和示例代码,此处就不再赘述。(SGI STL版本的环境组态文件为<stl_config.h>,GCC的组态文件为<c++config.h>,可以在…/bits目录下找到)

例如,像上述代码中的第二行出现了“_GLIBCXX20_CONSTEXPR”,它的含义如下:

  • 1)_GLIBCXX:这个前缀通常在 libstdc++ 中使用,用于定义特定于 GNU C++ 标准库实现的宏和标识符。它有助于防止与用户代码发生命名冲突。

  • 2)20:这个数字指的是 C++20 标准。宏 _GLIBCXX20_CONSTEXPR 专门用于 C++20 中可用的功能。

  • 3)CONSTEXPR: 这是 C++ 11 中引入的 C++ 关键字,代表 “常量表达式”,用于定义可在编译时求值的函数或变量。

2.1.3 (void*)__p

  代码中但凡涉及到调用全局new函数的时候,都会强制将指针的类型转换为(void*),目的是为了显式地指出当前该指针p所指向的内存空间是原始内存空间,与任何数值类型都无关。

2.2 Destroy()函数的定义

Destroy()函数的版本一(比较简单,不多解释)

  /**
   * Destroy the object pointed to by a pointer type.
   */
  template<typename _Tp>
    _GLIBCXX14_CONSTEXPR inline void
    _Destroy(_Tp* __pointer)
    {
#if __cplusplus > 201703L
      std::destroy_at(__pointer);
#else
      __pointer->~_Tp();
#endif
    }

Destroy()函数的版本二: 接受两个迭代器,并析构[__first,__last)范围内的对象

//SGI STL与GNU STL在_Destroy_aux的实现上存在了分歧,
//SGI STL采用的是对函数模版的重载,
//而GNU则是通过定义类模版和对模版进行特化的方式实现了两种版本的Destroy_aux,并且正在发挥作用的是嵌套在Destroy_aux模版中的__destroy的成员函数模板。
  template<bool>
    struct _Destroy_aux
    {
      template<typename _ForwardIterator>
	static _GLIBCXX20_CONSTEXPR void
	__destroy(_ForwardIterator __first, _ForwardIterator __last)
	{
	  for (; __first != __last; ++__first)
	    std::_Destroy(std::__addressof(*__first)); //如果迭代器所指对象拥有非trivial析构函数,则调用版本一的Destroy()函数。
	    //
	}
    };

  template<>
    struct _Destroy_aux<true>
    {
      template<typename _ForwardIterator>
        static void
        __destroy(_ForwardIterator, _ForwardIterator) { }
    };

  /**
   * Destroy a range of objects.  If the value_type of the object has
   * a trivial destructor, the compiler should optimize all of this
   * away, otherwise the objects' destructors must be invoked.
   */
  template<typename _ForwardIterator>
    _GLIBCXX20_CONSTEXPR inline void
    _Destroy(_ForwardIterator __first, _ForwardIterator __last)
    {
      typedef typename iterator_traits<_ForwardIterator>::value_type
                       _Value_type; //利用迭代器的trait模版,编译期间获得当前迭代器所指对象的类型
#if __cplusplus >= 201103L
      // A deleted destructor is trivial, this ensures we reject such types: 确保迭代器所指对象的析构函数没有被delete
      static_assert(is_destructible<_Value_type>::value,
		    "value type is destructible");
#endif
#if __cplusplus >= 202002L
      if (std::__is_constant_evaluated())
	return std::_Destroy_aux<false>::__destroy(__first, __last);
#endif
//根据当前迭代器所指对象类型是否具有trivial析构函数来抉择是调用普通版本的_Destroy_aux类的成员函数__destroy,还是调用特化版本的
//_Destroy_aux<true>类的__destroy()成员函数。
      std::_Destroy_aux<__has_trivial_destructor(_Value_type)>:: 
	__destroy(__first, __last);
    }

3. 总结

  从上述代码的分析中可以看出,两个版本的STL的Construct()与Destory()基本工具的定义在底层实现上大差不差,Construct()工具主要是在已分配的空间中利用定位New运算符,结合所给初值调用数值类型的构造函数完成对象的构造。由于C++11引入了可变参数模版,所以Construct()的函数参数做了一些调整,原本的左值引用变为了模版参数包(Template Parameter Pack)。Destory()工具则是根据迭代器所指对象是否拥有trivial析构函数来决定是否通过调用对象的析构函数完成销毁任务,或是什么也不做。

标签:__,函数,STL,C++,源码,Construct,Destroy
From: https://blog.csdn.net/NORthernlll/article/details/141404445

相关文章

  • STL map、set、multi_map、multi_set 基本概念与用法
    目录基本概念关联式容器键值对树形结构的关联式容器set描述set的使用map描述map的使用multiset描述multiset简单使用multimap描述底层结构基本概念关联式容器在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容......
  • 【OpenHarmony】openharmony移植到RK3568------获取源码编译OpenHarmony源码
    一、源码获取源码获取有好几种方式,在这里直接在镜像网站下载源码,点击下面连接下载全量版本的OpenHarmony4.1https://repo.huaweicloud.com/openharmony/os/4.1-Release/code-v4.1-Release.tar.gz将源码放到自己建立的目录下解压,我放入的是这个目录/home/wzz/work/0H4.1......
  • C++(time_t)
    目录1.数据类型:2.使用场景:3.常见函数:4.时间的存储和表示:5.示例:6.注意事项:在C++中,time_t是一种数据类型,用于存储日历时间。它定义在<ctime>头文件中。time_t本质上是一个算术类型,通常是整数或浮点数,用于表示从标准纪元(通常是1970年1月1日00:00:00UTC,称为“Unixepo......
  • 小白学习c++{复习篇}P11【2066】买图书(c++)详细讲解
    目录EXTRA##PT1-代码呈现awaPT2-问题填空qwqPT3-课后小结......
  • 基于springboot的免费体育馆场地预约系统(源码+文档+调试+讲解)
    收藏关注不迷路!!......
  • Android车载蓝牙音乐实例(附Demo源码):实现手机播放音乐后车机应用显示音乐名称,歌手,专辑
    一、功能需求功能需求是在Android10以上设备上实现蓝牙音乐功能,细分为两个功能点:1、手机和车载设备实现蓝牙连接(本Demo文只做监听蓝牙连接状态,需手动到设置中连接蓝牙)2、连接蓝牙成功后手机播放音乐时车载设备也能播放音乐,并且在车机应用上显示音乐名称,歌手,专辑名。可在车......
  • C++的动态数组vector番外之capacity
    今日诗词:爱他明月好,憔悴也相关。西风多少恨,吹不散眉弯。                    ——《临江仙·寒柳》【清】纳兰容若目录引言正文string中的和vector中的capacity有什么区别 vector扩容时内存分配的策略是什么?capacity在vect......
  • 帝国cms源码怎么查看
    如何查看帝国CMS源码帝国CMS是一个开源的网站管理系统,你可以通过以下步骤查看其源码:步骤1:连接到FTP服务器使用FTP客户端(例如FileZilla)连接到你的网站托管服务器。输入你的FTP凭据(用户名、密码和服务器地址)。步骤2:导航到帝国CMS目录通常,帝国CMS文件存储在/w......
  • 海外商城海跨境电商APP定制开发多国语音定制app源码搭建
    海外商城App的开发是一个复杂而系统的工程,‌涉及多个阶段和注意事项。‌以下是一个详细的海外商城App开发流程和注意事项:‌开发流程1.‌需求确定‌:‌明确应用程序的目标,‌确定App要解决的具体问题,‌以及面向的用户群体。‌与客户进行深入的洽谈沟通,‌了解App的开发内容......
  • Go 互斥锁 Mutex 源码分析 (一)
    原创文章,欢迎转载,转载请注明出处,谢谢。0.前言锁作为并发编程中的关键一环,是应该要深入掌握的。1.锁1.1示例实现锁很简单,示例如下:varglobalintfuncmain(){ varmusync.Mutex varwgsync.WaitGroup fori:=0;i<2;i++{ wg.Add(1) gofunc(iint){......