《STL源码剖析》笔记——allocator

六大组件间关系

在这里插入图片描述

部分STL文件包含关系

在这里插入图片描述
allocator包含于中:
实际实现于三个文件 :
1.stl_construct.h :对象的构造和析构
2.stl_alloc.h空间配置和释放
3.stl_uninitialized.h

空间配置器(allocator)

1.什么是空间配置器?
为容器取得数据存储空间的类template从用户代码std::vector v;开始,vector的模板参数class T被替换为int,同时第二个模板参数因为没有指定,所以为默认模板参数,即allocator,这个vector对象v会在内部实例一个allocator的对象,用来管理内存。

2.空间配置器需要哪些功能?
负责空间配置与管理,从实现了动态空间配置、空间管理、空间释放的class template

空间配置器(allocator)标准接口

//traits 编程技法可以暂时不了解
allocator::value_type
allocator::pointer
allocator::const_pointer
allocator::reference
allocator::const_reference
allocator::size_type
allocator::difference

// 一个嵌套的(nested)class template,class rebind<U>拥有唯一成员other,那是一个typedef,代表allocator<U>
allocator::rebind

allocator::allocator() // 默认构造函数
allocator::allocator(const allocator&) // 拷贝构造函数
template <class U>allocator::allocator(const allocator<U>&) // 泛化的拷贝构造函数
allocator::~allocator() // 析构函数

// 返回某个对象的地址,a.address(x)等同于&x
pointer allocator::address(reference x) const
// 返回某个const对象的地址,a.address(x)等同于&x
const_pointer allocator::address(const_reference x) const

// 配置空间,足以存储n个T对象。第二个参数是个提示。实现上可能会利用它来增进区域性(locality),或完全忽略之
pointer allocator::allocate(size_type n, const void* = 0)
// 释放先前配置的空间
void allocator::deallocate(pointer p, size_type n)

// 返回可成功配置的最大量
size_type allocator:maxsize() const

// 调用对象的构造函数,等同于 new((void*)p) T(x)
void allocator::construct(pointer p, const T& x)
// 调用对象的析构函数,等同于 p->~T()
void allocator::destroy(pointer p)

一个最简单的allocator就可以理解为对new,delete的简单封装,以及对构造函数和析构函数的直接调用。

书中实现

#ifndef _MYALLOC_
#define _MYALLOC_

#include <new>
#include <cstddef>
#include <cstdlib>
#include <climits>
#include <iostream>

namespace my_alloc
{
    // allocate的实际实现,简单封装new,当无法获得内存时,报错并退出
    template <class T>
    inline T* _allocate(ptrdiff_t size, T*) {
        set_new_handler(0);
        T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
        if (tmp == 0) {
            cerr << "out of memory" << endl;
            exit(1);
        }
        return tmp;
    }

    // deallocate的实际实现,简单封装delete
    template <class T>
    inline void _deallocate(T* buffer) { ::operator delete(buffer); }

    // construct的实际实现,直接调用对象的构造函数
    template <class T1, class T2>
    inline void _construct(T1* p, const T2& value) { new(p) T1(value); }

    // destroy的实际实现,直接调用对象的析构函数
    template <class T>
    inline void _destroy(T* ptr) { ptr->~T(); }

    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;

        // 构造函数
        allocator(){ return; }
        template <class U>
        allocator(const allocator<U>& c){}

        // rebind allocator of type U
        template <class U>
        struct rebind { typedef allocator<U> other; };

        // allocate,deallocate,construct和destroy函数均调用上面的实际实现
        // hint used for locality. ref.[Austern],p189
        pointer allocate(size_type n, const void* hint = 0) {
            return _allocate((difference_type)n, (pointer)0);
        }
        void deallocate(pointer p, size_type n) { _deallocate(p); }
        void construct(pointer p, const T& value) { _construct(p, value); }
        void destroy(pointer p) { _destroy(p); }

        pointer address(reference x) { return (pointer)&x; }
        const_pointer const_address(const_reference x) { return (const_pointer)&x; }

        size_type max_size() const { return size_type(UINT_MAX / sizeof(T)); }   
    };
} // end of namespace myalloc

自己编写的allocator为vector分配空间

#include <vector>
int main()
{
    std::vector<int, my_alloc::allocator<int> > v;
    // Do something;
    return 0;
}

该实现符合STL标准,但是通过不了GCC编译器,原因是SGI STL的allocator并不完全符合STL规范。在gcc9.2.0(mingw-w64 on msys2)里,上面这个“最简单的allocator实现”微小改动(给set_new_handler、cerr、endl三处加上std::)以后是可以编译通过。
在这里插入图片描述

SGI STL 默认配置器:std::alloc 详解

上面实现的满足STL标准的allocator只是简单的实现内存配置/释放,即对new和delete的浅包装,SGI中拥有其独特的操作来优化alloc的性能

alloc总体架构

1.构造和析构基本工具construct()destory <stl_construct.h>
2.空间的配置与释放工具 一级配置器、二级配置器<stl_alloc.h>
3.调用系统内存函数<stl_uninitial.>

在这里插入图片描述

new操作

class Foo{....}
Foo *pf =new Foo;
delete pf

上述代码new操作主要分为两个阶段的操作
1.调用::operator new 配置内存
2.调用构造函数 Foo()构造对象内容
delete也包含两个操作:
1.调用Foo:~Foo()将对象析构
2.调用::operator delete 释放内存

STL 中 内存配置操作由 alloc:allocate 负责
内存释放由alloc::deallocate负责,对象构造由::construct 负责,对象析构由
::destroy负责

destroy

直接使用模板编程调用析构函数

template<class T>
inline void destroy(T *pointer)
{
 pointer->~T();
}

construct

template<class T1,class T2>
inline void construct (T1*p,const T2& value)
{
 new(p) T1(value);
}

在SGI源码中加入了第二版destroy,主要根据trivial来特化实例
在这里插入图片描述

allocate

在SGI中,alloc优化了申请内存和释放的性能。拿vector来举例,申请一个vector时,我们调用alloc为其申请内存空间。
在这里插入图片描述
在SGI中将alloc分为一级适配器和二级适配器,以应用于内存大小不同的场景。大于128bytes直接调用一级alloc,小于则使用二级alloc。二级适配器采用维护16个自由链表来分配内存,这主要是为了解决小型区块可能造成的内存碎片问题。
在这里插入图片描述

空间释放函数deallocate

该函数首先会判断区块大小,大于128bytes调用一级配置器,小于128bytes就找到对应的自由链表,将区块回收

另外的内存处理工具

STL 定义了5个全局函数,作用容器配置空间。
1.构造函数 construct
2.析构函数 destroy
3.uninitialized_copy ->copy()
4.uninitialized_fill->fill()
5.uninitialized_fill_n->filln()
在这里插入图片描述

总结

本文简单介绍了STL组件之一空间配置器,先从标准接口来简要的理解allocator的功能紧接着用templet编程写了一个简单的allocator,然后记录了SGI 中利用traits编程技法来特化提高函数效率,包括一二级适配器优化内存管理的过程,最后介绍了另外几个内存全局函数。本篇文章并未具体细节代码进行讲解是对《STL源码剖析》框架上的总结,若读者对具体代码有兴趣,可以直接读原书。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值