c++源码阅读__MyTinySTL__allocator.h__正文阅读

饭宝  论坛元老 | 2024-12-6 13:08:27 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1041|帖子 1041|积分 3123

MyTinySTL源码阅读目录

c++源码阅读__MyTinySTL__目录

  
allocator.h

看本篇内容之前, 最悦目完上一篇内容比较好, 在上面MyTinySTL源码阅读目录里可跳转
一. 简介

这个文件做的事情非常简单

  • 管理内存的分配, 释放
  • 对象的构造 和 析构
在上一篇c++源码阅读__MyTinySTL__construct.h__正文阅读中我们可以相识到, construct.h只是做对象的构造和析构, 那么本文件allocator.h其实就是创建和销毁一个对象, 流程如下:
创建: 分配内存, 调用construct实行构造函数
销毁: 调用destroy实行析构函数, 销毁内存
二. 源码

  1. #pragma once
  2. // 这个头文件包含一个模板类 allocator,用于管理内存的分配、释放,对象的构造、析构
  3. #include "construct.h"
  4. #include "util.h"
  5. namespace mystl
  6. {
  7.     // 模板类:allocator
  8.     // 模板函数代表数据类型
  9.     template <class T>
  10.     class allocator
  11.     {
  12.     public:
  13.         typedef T            value_type;
  14.         typedef T* pointer;
  15.         typedef const T* const_pointer;
  16.         typedef T& reference;
  17.         typedef const T& const_reference;
  18.         typedef size_t       size_type;
  19.         typedef ptrdiff_t    difference_type;
  20.     public:
  21.         // 全是静态的方法
  22.         static T* allocate();
  23.         static T* allocate(size_type n);
  24.         static void deallocate(T* ptr);
  25.         static void deallocate(T* ptr, size_type n);
  26.         static void construct(T* ptr);
  27.         static void construct(T* ptr, const T& value);
  28.         static void construct(T* ptr, T&& value);
  29.         template <class... Args>
  30.         static void construct(T* ptr, Args&& ...args);
  31.         static void destroy(T* ptr);
  32.         static void destroy(T* first, T* last);
  33.     };
  34.     template <class T>
  35.     T* allocator<T>::allocate()
  36.     {
  37.         return static_cast<T*>(::operator new(sizeof(T)));
  38.     }
  39.     template <class T>
  40.     T* allocator<T>::allocate(size_type n)
  41.     {
  42.         if (n == 0)
  43.             return nullptr;
  44.         return static_cast<T*>(::operator new(n * sizeof(T)));
  45.     }
  46.     template <class T>
  47.     void allocator<T>::deallocate(T* ptr)
  48.     {
  49.         if (ptr == nullptr)
  50.             return;
  51.         ::operator delete(ptr);
  52.     }
  53.     template <class T>
  54.     void allocator<T>::deallocate(T* ptr, size_type /*size*/)
  55.     {
  56.         if (ptr == nullptr)
  57.             return;
  58.         ::operator delete(ptr);
  59.     }
  60.     template <class T>
  61.     void allocator<T>::construct(T* ptr)
  62.     {
  63.         mystl::construct(ptr);
  64.     }
  65.     template <class T>
  66.     void allocator<T>::construct(T* ptr, const T& value)
  67.     {
  68.         mystl::construct(ptr, value);
  69.     }
  70.     template <class T>
  71.     void allocator<T>::construct(T* ptr, T&& value)
  72.     {
  73.         mystl::construct(ptr, mystl::move(value));
  74.     }
  75.     template <class T>
  76.     template <class ...Args>
  77.     void allocator<T>::construct(T* ptr, Args&& ...args)
  78.     {
  79.         mystl::construct(ptr, mystl::forward<Args>(args)...);
  80.     }
  81.     template <class T>
  82.     void allocator<T>::destroy(T* ptr)
  83.     {
  84.         mystl::destroy(ptr);
  85.     }
  86.     template <class T>
  87.     void allocator<T>::destroy(T* first, T* last)
  88.     {
  89.         mystl::destroy(first, last);
  90.     }
  91. }
复制代码
三. 逐行解析

3.1 allocate分配内存

  1. template <class T>
  2. T* allocator<T>::allocate()
  3. {
  4.     return static_cast<T*>(::operator new(sizeof(T)));
  5. }
  6. template <class T>
  7. T* allocator<T>::allocate(size_type n)
  8. {
  9.     if (n == 0)
  10.         return nullptr;
  11.     return static_cast<T*>(::operator new(n * sizeof(T)));
  12. }
复制代码
表明:
可以看到一共两个重载, 第一个函数直接分配类型T的空间, 第二个函数分配类型T的空间的n倍
使用的逻辑也非常简单
static_cast<T*>(:perator new(sizeof(T)))
通过sizeof(T)拿到类型T的大小, 然后通过:perator new来申请空间, 末了通过static_cast<T*>转为T*的指针类型
思考:
那么能否使用dynamic_cast呢? 请查看static_cast 和 dynamic_cast的区别
能否使用reinterpret_cast呢? 当然可以了, 只要保证安全
例子:
  1. class MyStruct
  2. {
  3. public:
  4.         int a;
  5.         double b;
  6.         MyStruct(int _a, int _b) : a(_a), b(_b) { cout << "MyStruct" << "(" << a << "," << b << ")" << endl; };
  7.         ~MyStruct() { cout << "~MyStruct" << "(" << a << "," << b << ")" << endl; };
  8. };
  9. int main()
  10. {
  11.         size_t vector_size = 10;
  12.         MyStruct* ms_vector = mystl::allocator<MyStruct>::allocate(vector_size);
  13.         for (size_t i = 0; i < vector_size; i++)
  14.         {
  15.                 (ms_vector + i)->a = i;
  16.                 (ms_vector + i)->b = i;
  17.         }
  18.         for (size_t i = 0; i < vector_size; i++)
  19.                 cout << "(" << (*(ms_vector + i)).a << "," << (*(ms_vector + i)).b << ")" << endl;
  20.         return 0;
  21. }
复制代码
实行结果

3.2 deallocate释放内存

  1. template <class T>
  2. class allocator
  3. {
  4. public:
  5.         typedef size_t       size_type;
  6. }
  7. template <class T>
  8. void allocator<T>::deallocate(T* ptr)
  9. {
  10.     if (ptr == nullptr)
  11.         return;
  12.     ::operator delete(ptr);
  13. }
  14. template <class T>
  15. void allocator<T>::deallocate(T* ptr, size_type)
  16. {
  17.     if (ptr == nullptr)
  18.         return;
  19.     ::operator delete(ptr);
  20. }
复制代码
表明:
可以看到deallocate也是两个重载, 其中size_type是类中size_t的别名, 指的是类型的个数, 这两个重载有个特点, 就是方法的内容都是一样的, 缘故原由如下:
因为空间的申请是通过上面的allocate申请的, 是一段连续的内存, 至于个数的分别, 每一段怎么表明是步调自己界说的, 在内存里, 就是一段连续的内存, 大小也是c++运行时记录地, 以是销毁时直接通过:perator delete()就能整段销毁
思考:
:perator delete和delete的区别, 前者只会释放内存, 并不会实行析构函数, 而后者会实行析构函数, 并释放内存, 以是delete内部应该是调用了:perator delete来实行内存释放的部门
例子:
  1. class MyStruct
  2. {
  3. public:
  4.         int a;
  5.         double b;
  6.         MyStruct(int _a, int _b) : a(_a), b(_b) { cout << "MyStruct" << "(" << a << "," << b << ")" << endl; };
  7.         ~MyStruct() { cout << "~MyStruct" << "(" << a << "," << b << ")" << endl; };
  8. };
  9. int main()
  10. {
  11.     MyStruct* s1 = mystl::allocator<MyStruct>::allocate();
  12.     cout << "1" << endl;
  13.     mystl::allocator<MyStruct>::deallocate(s1);                // 只申请空间然后释放空间
  14.     cout << "2" << endl;
  15.     MyStruct* s2 = mystl::allocator<MyStruct>::allocate();
  16.     cout << "3" << endl;
  17.     delete s2;                                                                                // 申请空间然后,执行delete, 不仅释放空间, 而且调用析构函数
  18.     cout << "4" << endl;
  19.         return 0;
  20. }
复制代码
实行结果

3.2 construct 将分配好的内存空间实行构造函数

  1. template <class T>
  2. void allocator<T>::construct(T* ptr)
  3. {
  4.     mystl::construct(ptr);
  5. }
  6. template <class T>
  7. void allocator<T>::construct(T* ptr, const T& value)
  8. {
  9.     mystl::construct(ptr, value);
  10. }
  11. template <class T>
  12. void allocator<T>::construct(T* ptr, T&& value)
  13. {
  14.     mystl::construct(ptr, mystl::move(value));
  15. }
  16. template <class T>
  17. template <class ...Args>
  18. void allocator<T>::construct(T* ptr, Args&& ...args)
  19. {
  20.     mystl::construct(ptr, mystl::forward<Args>(args)...);
  21. }
复制代码
表明:
这里四种重载, 分别是无参, const左值引用, 右值引用,多参数
里面就是包了一层construct.h文件中的construct函数, 在原指针上实行构造函数, 按照我目前的理解, 这个函数中, 不应该直接申请空间, 实行构造函数一整套实行吗? 但是这里申请空间和实行构造函数, 完全分开了, 我个人感觉就失去了本文件的目的管理内存的分配、释放,对象的构造、析构了, 但是作者应该有更深层的考虑, 这点我们之后在举行考虑
思考:
末了一个方法为什么两个template要分开写呢?
假云云函数不是模板类的成员函数时, 我们确实可以写成template <class T, class ...Args>, 但是在这里template<T>是模板类allocator的模板声明, 而template<class ...Args>是他的成员函数construct的模板声明, 用来声明此函数是模板函数. 以是两个模板声明意义不同, 是不能写在一起的
例子:
  1. class MyStruct
  2. {
  3. public:
  4.         int a;
  5.         double b;
  6.         MyStruct(int _a, int _b) : a(_a), b(_b) { cout << "MyStruct" << "(" << a << "," << b << ")" << endl; };
  7.         ~MyStruct() { cout << "~MyStruct" << "(" << a << "," << b << ")" << endl; };
  8. };
  9. int main()
  10. {
  11.         MyStruct* s = mystl::allocator<MyStruct>::allocate();
  12.         mystl::allocator<MyStruct>::construct(s, 1, 2);
  13.         //mystl::allocator<MyStruct>::construct<int, int>(s, 1, 2);
  14.         // 这样写也是对的
  15.         return 0;
  16. }
复制代码
实行结果

3.4 destroy 实行对象的析构函数

  1. template <class T>
  2. void allocator<T>::destroy(T* ptr)
  3. {
  4.     mystl::destroy(ptr);
  5. }
  6. template <class T>
  7. void allocator<T>::destroy(T* first, T* last)
  8. {
  9.     mystl::destroy(first, last);
  10. }
复制代码
表明:
这里也有两个重载, 第二个重载跟迭代器相干, 目前先不讲解, 里面也只是包了一层construct.h中的destroy函数, 用来实行对象的析构函数, 并不会销毁对象本身, 更具体的讲解, 上一篇文章中已经讲解过, 这里不再赘述.
例子:
  1. class MyStruct
  2. {
  3. public:
  4.         int a;
  5.         double b;
  6.         MyStruct(int _a, int _b) : a(_a), b(_b) { cout << "MyStruct" << "(" << a << "," << b << ")" << endl; };
  7.         ~MyStruct() { cout << "~MyStruct" << "(" << a << "," << b << ")" << endl; };
  8. };
  9. int main()
  10. {
  11.         size_t vector_size = 10;
  12.         // 申请大小为 vector_size * sizeof(MyStruct)大小的空间, 并转为MyStruct*
  13.         MyStruct* s = mystl::allocator<MyStruct>::allocate(vector_size);
  14.         cout << "==============" << endl;
  15.         for (size_t i = 0; i < vector_size; i++)
  16.                 // 循环执行构造函数
  17.                 mystl::allocator<MyStruct>::construct(s + i, i, i);
  18.         cout << "==============" << endl;
  19.         // 循环执行析构函数
  20.         for (size_t i = 0; i < vector_size; i++)
  21.                 mystl::allocator<MyStruct>::destroy(s + i);
  22.         // 删除内存 和 ::operator delete(s);一样, vector_size只是调用时清晰逻辑, 并没有实质作用
  23.         mystl::allocator<MyStruct>::deallocate(s, vector_size);
  24.         s = nullptr;
  25.         return 0;
  26. }
复制代码
实行结果

四. 总结

allocator.h里共有四类函数申请空间, 销毁空间, 实行构造函数,实行析构函数
其中 实行构造函数,实行析构函数都是在construct.h中对应的函数外包装了一层. 至于为什么不把申请空间, 实行构造函数, 实行析构函数, 销毁空间做成一条龙的, 这个大概我们在之后的代码阅读中, 能发现缘故原由.

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

饭宝

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表