MyTinySTL源码阅读目录
c++源码阅读__MyTinySTL__目录
allocator.h
看本篇内容之前, 最悦目完上一篇内容比较好, 在上面MyTinySTL源码阅读目录里可跳转
一. 简介
这个文件做的事情非常简单
在上一篇c++源码阅读__MyTinySTL__construct.h__正文阅读中我们可以相识到, construct.h只是做对象的构造和析构, 那么本文件allocator.h其实就是创建和销毁一个对象, 流程如下:
创建: 分配内存, 调用construct实行构造函数
销毁: 调用destroy实行析构函数, 销毁内存
二. 源码
- #pragma once
- // 这个头文件包含一个模板类 allocator,用于管理内存的分配、释放,对象的构造、析构
- #include "construct.h"
- #include "util.h"
- namespace mystl
- {
- // 模板类:allocator
- // 模板函数代表数据类型
- template <class T>
- class allocator
- {
- public:
- typedef T value_type;
- typedef T* pointer;
- typedef const T* const_pointer;
- typedef T& reference;
- typedef const T& const_reference;
- typedef size_t size_type;
- typedef ptrdiff_t difference_type;
- public:
- // 全是静态的方法
- static T* allocate();
- static T* allocate(size_type n);
- static void deallocate(T* ptr);
- static void deallocate(T* ptr, size_type n);
- static void construct(T* ptr);
- static void construct(T* ptr, const T& value);
- static void construct(T* ptr, T&& value);
- template <class... Args>
- static void construct(T* ptr, Args&& ...args);
- static void destroy(T* ptr);
- static void destroy(T* first, T* last);
- };
- template <class T>
- T* allocator<T>::allocate()
- {
- return static_cast<T*>(::operator new(sizeof(T)));
- }
- template <class T>
- T* allocator<T>::allocate(size_type n)
- {
- if (n == 0)
- return nullptr;
- return static_cast<T*>(::operator new(n * sizeof(T)));
- }
- template <class T>
- void allocator<T>::deallocate(T* ptr)
- {
- if (ptr == nullptr)
- return;
- ::operator delete(ptr);
- }
- template <class T>
- void allocator<T>::deallocate(T* ptr, size_type /*size*/)
- {
- if (ptr == nullptr)
- return;
- ::operator delete(ptr);
- }
- template <class T>
- void allocator<T>::construct(T* ptr)
- {
- mystl::construct(ptr);
- }
- template <class T>
- void allocator<T>::construct(T* ptr, const T& value)
- {
- mystl::construct(ptr, value);
- }
- template <class T>
- void allocator<T>::construct(T* ptr, T&& value)
- {
- mystl::construct(ptr, mystl::move(value));
- }
- template <class T>
- template <class ...Args>
- void allocator<T>::construct(T* ptr, Args&& ...args)
- {
- mystl::construct(ptr, mystl::forward<Args>(args)...);
- }
- template <class T>
- void allocator<T>::destroy(T* ptr)
- {
- mystl::destroy(ptr);
- }
- template <class T>
- void allocator<T>::destroy(T* first, T* last)
- {
- mystl::destroy(first, last);
- }
- }
复制代码 三. 逐行解析
3.1 allocate分配内存
- template <class T>
- T* allocator<T>::allocate()
- {
- return static_cast<T*>(::operator new(sizeof(T)));
- }
- template <class T>
- T* allocator<T>::allocate(size_type n)
- {
- if (n == 0)
- return nullptr;
- return static_cast<T*>(::operator new(n * sizeof(T)));
- }
复制代码 表明:
可以看到一共两个重载, 第一个函数直接分配类型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呢? 当然可以了, 只要保证安全
例子:
- class MyStruct
- {
- public:
- int a;
- double b;
- MyStruct(int _a, int _b) : a(_a), b(_b) { cout << "MyStruct" << "(" << a << "," << b << ")" << endl; };
- ~MyStruct() { cout << "~MyStruct" << "(" << a << "," << b << ")" << endl; };
- };
- int main()
- {
- size_t vector_size = 10;
- MyStruct* ms_vector = mystl::allocator<MyStruct>::allocate(vector_size);
- for (size_t i = 0; i < vector_size; i++)
- {
- (ms_vector + i)->a = i;
- (ms_vector + i)->b = i;
- }
- for (size_t i = 0; i < vector_size; i++)
- cout << "(" << (*(ms_vector + i)).a << "," << (*(ms_vector + i)).b << ")" << endl;
- return 0;
- }
复制代码 实行结果
3.2 deallocate释放内存
- template <class T>
- class allocator
- {
- public:
- typedef size_t size_type;
- }
- template <class T>
- void allocator<T>::deallocate(T* ptr)
- {
- if (ptr == nullptr)
- return;
- ::operator delete(ptr);
- }
- template <class T>
- void allocator<T>::deallocate(T* ptr, size_type)
- {
- if (ptr == nullptr)
- return;
- ::operator delete(ptr);
- }
复制代码 表明:
可以看到deallocate也是两个重载, 其中size_type是类中size_t的别名, 指的是类型的个数, 这两个重载有个特点, 就是方法的内容都是一样的, 缘故原由如下:
因为空间的申请是通过上面的allocate申请的, 是一段连续的内存, 至于个数的分别, 每一段怎么表明是步调自己界说的, 在内存里, 就是一段连续的内存, 大小也是c++运行时记录地, 以是销毁时直接通过: perator delete()就能整段销毁
思考:
: perator delete和delete的区别, 前者只会释放内存, 并不会实行析构函数, 而后者会实行析构函数, 并释放内存, 以是delete内部应该是调用了: perator delete来实行内存释放的部门
例子:
- class MyStruct
- {
- public:
- int a;
- double b;
- MyStruct(int _a, int _b) : a(_a), b(_b) { cout << "MyStruct" << "(" << a << "," << b << ")" << endl; };
- ~MyStruct() { cout << "~MyStruct" << "(" << a << "," << b << ")" << endl; };
- };
- int main()
- {
- MyStruct* s1 = mystl::allocator<MyStruct>::allocate();
- cout << "1" << endl;
- mystl::allocator<MyStruct>::deallocate(s1); // 只申请空间然后释放空间
- cout << "2" << endl;
- MyStruct* s2 = mystl::allocator<MyStruct>::allocate();
- cout << "3" << endl;
- delete s2; // 申请空间然后,执行delete, 不仅释放空间, 而且调用析构函数
- cout << "4" << endl;
- return 0;
- }
复制代码 实行结果
3.2 construct 将分配好的内存空间实行构造函数
- template <class T>
- void allocator<T>::construct(T* ptr)
- {
- mystl::construct(ptr);
- }
- template <class T>
- void allocator<T>::construct(T* ptr, const T& value)
- {
- mystl::construct(ptr, value);
- }
- template <class T>
- void allocator<T>::construct(T* ptr, T&& value)
- {
- mystl::construct(ptr, mystl::move(value));
- }
- template <class T>
- template <class ...Args>
- void allocator<T>::construct(T* ptr, Args&& ...args)
- {
- mystl::construct(ptr, mystl::forward<Args>(args)...);
- }
复制代码 表明:
这里四种重载, 分别是无参, const左值引用, 右值引用,多参数
里面就是包了一层construct.h文件中的construct函数, 在原指针上实行构造函数, 按照我目前的理解, 这个函数中, 不应该直接申请空间, 实行构造函数一整套实行吗? 但是这里申请空间和实行构造函数, 完全分开了, 我个人感觉就失去了本文件的目的管理内存的分配、释放,对象的构造、析构了, 但是作者应该有更深层的考虑, 这点我们之后在举行考虑
思考:
末了一个方法为什么两个template要分开写呢?
假云云函数不是模板类的成员函数时, 我们确实可以写成template <class T, class ...Args>, 但是在这里template<T>是模板类allocator的模板声明, 而template<class ...Args>是他的成员函数construct的模板声明, 用来声明此函数是模板函数. 以是两个模板声明意义不同, 是不能写在一起的
例子:
- class MyStruct
- {
- public:
- int a;
- double b;
- MyStruct(int _a, int _b) : a(_a), b(_b) { cout << "MyStruct" << "(" << a << "," << b << ")" << endl; };
- ~MyStruct() { cout << "~MyStruct" << "(" << a << "," << b << ")" << endl; };
- };
- int main()
- {
- MyStruct* s = mystl::allocator<MyStruct>::allocate();
- mystl::allocator<MyStruct>::construct(s, 1, 2);
- //mystl::allocator<MyStruct>::construct<int, int>(s, 1, 2);
- // 这样写也是对的
- return 0;
- }
复制代码 实行结果
3.4 destroy 实行对象的析构函数
- template <class T>
- void allocator<T>::destroy(T* ptr)
- {
- mystl::destroy(ptr);
- }
- template <class T>
- void allocator<T>::destroy(T* first, T* last)
- {
- mystl::destroy(first, last);
- }
复制代码 表明:
这里也有两个重载, 第二个重载跟迭代器相干, 目前先不讲解, 里面也只是包了一层construct.h中的destroy函数, 用来实行对象的析构函数, 并不会销毁对象本身, 更具体的讲解, 上一篇文章中已经讲解过, 这里不再赘述.
例子:
- class MyStruct
- {
- public:
- int a;
- double b;
- MyStruct(int _a, int _b) : a(_a), b(_b) { cout << "MyStruct" << "(" << a << "," << b << ")" << endl; };
- ~MyStruct() { cout << "~MyStruct" << "(" << a << "," << b << ")" << endl; };
- };
- int main()
- {
- size_t vector_size = 10;
- // 申请大小为 vector_size * sizeof(MyStruct)大小的空间, 并转为MyStruct*
- MyStruct* s = mystl::allocator<MyStruct>::allocate(vector_size);
- cout << "==============" << endl;
- for (size_t i = 0; i < vector_size; i++)
- // 循环执行构造函数
- mystl::allocator<MyStruct>::construct(s + i, i, i);
- cout << "==============" << endl;
- // 循环执行析构函数
- for (size_t i = 0; i < vector_size; i++)
- mystl::allocator<MyStruct>::destroy(s + i);
- // 删除内存 和 ::operator delete(s);一样, vector_size只是调用时清晰逻辑, 并没有实质作用
- mystl::allocator<MyStruct>::deallocate(s, vector_size);
- s = nullptr;
- return 0;
- }
复制代码 实行结果
四. 总结
allocator.h里共有四类函数申请空间, 销毁空间, 实行构造函数,实行析构函数
其中 实行构造函数,实行析构函数都是在construct.h中对应的函数外包装了一层. 至于为什么不把申请空间, 实行构造函数, 实行析构函数, 销毁空间做成一条龙的, 这个大概我们在之后的代码阅读中, 能发现缘故原由.
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |