饭宝 发表于 2024-12-6 13:08:27

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]
查看完整版本: c++源码阅读__MyTinySTL__allocator.h__正文阅读