c++源码阅读__MyTinySTL__allocator.h__正文阅读
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*>(::operator new(sizeof(T)))
通过sizeof(T)拿到类型T的大小, 然后通过::operator 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;
}
实行结果
https://i-blog.csdnimg.cn/direct/a7f65bd7445b492f95d097e74ff80f34.png
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++运行时记录地, 以是销毁时直接通过::operator delete()就能整段销毁
思考:
::operator delete和delete的区别, 前者只会释放内存, 并不会实行析构函数, 而后者会实行析构函数, 并释放内存, 以是delete内部应该是调用了::operator 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;
}
实行结果
https://i-blog.csdnimg.cn/direct/6a608be4796f4b3a89043f28322dd5c8.png
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;
}
实行结果
https://i-blog.csdnimg.cn/direct/5d941311d8a64f4885c4286404dd82d6.png
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;
}
实行结果
https://i-blog.csdnimg.cn/direct/7b4a7c01c3ac463ebccb2ae99a15f350.png
四. 总结
allocator.h里共有四类函数申请空间, 销毁空间, 实行构造函数,实行析构函数
其中 实行构造函数,实行析构函数都是在construct.h中对应的函数外包装了一层. 至于为什么不把申请空间, 实行构造函数, 实行析构函数, 销毁空间做成一条龙的, 这个大概我们在之后的代码阅读中, 能发现缘故原由.
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]