读书笔记-STL空间配置器(1)

c++一大重要组成部件就是STL(标准模板库),STL提供了六大组件,分别是:

1、  容器(Containers):各种数据结构,如:vector、list、deque、set、map。用来存放数据。从实现的角度来看,STL容器是一种class template。

2、  算法(algorithms):各种常用算法,如:sort、search、copy、erase。从实现的角度来看,STL算法是一种 function template。

3、  迭代器(iterators):容器与算法之间的胶合剂,是所谓的“泛型指针”。共有五种类型,以及其他衍生变化。从实现的角度来看,迭代器是一种将 operator*、operator->、operator++、operator- - 等指针相关操作进行重载的class template。所有STL容器都有自己专属的迭代器,只有容器本身才知道如何遍历自己的元素。原生指针(native pointer)也是一种迭代器。

4、  仿函数(functors):行为类似函数,可作为算法的某种策略(policy)。从实现的角度来看,仿函数是一种重载了operator()的class或class template。一般的函数指针也可视为狭义的仿函数。

5、  配接器(adapters);一种用来修饰容器、仿函数、迭代器接口的东西。例如:STL提供的queue 和 stack,虽然看似容器,但其实只能算是一种容器配接器,因为它们的底部完全借助deque,所有操作都由底层的deque供应。改变 functors接口者,称为function adapter;改变 container 接口者,称为container adapter;改变iterator接口者,称为iterator adapter。

6、  配置器(allocators):负责空间配置与管理。从实现的角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的class template。

这六大组件的交互关系:container(容器) 通过 allocator(配置器) 取得数据储存空间,algorithm(算法)通过 iterator(迭代器)存取 container(容器) 内容,functor(仿函

数) 可以协助 algorithm(算法) 完成不同的策略变化,adapter(配接器) 可以修饰或套接 functor(仿函数)。

下面我们就从《STL源码剖析》中的一个简单例子开始讲述空间适配器:

#include <cstddef>  
#include <cstdlib>  
#include <climits>  
#include <iostream>  
#include <memory>  
#include <vector>  

using namespace std;

namespace cy{ //空间配置器所属的命名空间
	template <class T>
	inline T* _allocate(ptrdiff_t size, void* hint = 0){
		set_new_handler(0);//设置内存分配失败的处理函数,0表示没有设置处理函数
		T* tmp = (T*)(malloc(size * sizeof(T)));//调用malloc分配空间
		if (NULL == tmp){
			cout << "out of memory\n";
			exit(1);
		}
		return tmp;
	}

	template <class T>
	inline void _deallocate(T* buffer){
		free(buffer);
	}

	template <class T1, class T2>
	inline void _construct(T1* p, const T2& value){ //placement new操作,在已经分配的空间上调用
		new(p)T1(value);
	}

	template <class T>
	inline void _destory(T* ptr){//执行析构函数
		ptr->~T();
	}

	template <class T>
	class allocator{
	public:
		typedef T value_type;//元素类型
		typedef T* pointer;//元素的指针,可以把T换成int便于理解,那么就是typedef int* pointer,那么pointer是指向Int型的
		//指针;如果T是结构体类型的元素,那么pointer就是指向该结构体类型的指针
		typedef const T* const_pointer;//该类型的常指针
		typedef T& reference;//该类型的引用
		typedef const T& const_reference;//常引用
		typedef size_t size_type;//size_t和size_type都是unsigned int的别名,用来保存容器元素的个数
		typedef ptrdiff_t difference_type;//用来保存两个指针想减的结果,是带符号的整形

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

		pointer allocate(size_type n, const void* hint = 0){
			return _allocate<T>(n, 0);
		}

		void deallocate(pointer p, size_t n){
			_deallocate(p);
		}

		void construct(pointer p, const_reference value){
			_construct(p, value);
		}

		void destroy(pointer p){
			return _destory(p);
		}

		pointer address(reference x){
			return &x;
		}

		const_pointer const_address(const_reference x){
			return &x;
		}

		size_t max_size()const{
			return UINT_MAX / sizeof(T);
		}
	};

}

int main(){
	int arr[5] = { 0, 1, 2, 3, 4 };

	unsigned int i = 0;
	vector<int, cy::allocator<int>> vec(arr, arr + 5);//在vector类的内部实现的时候回申请5个int大小的空间,如果是int型的话
	//vector<int>型的迭代器就是int *类型,和arr是一样都是int *类型,所以可以直接使用数组名;<strong><span style="color:#ff0000;">至于为什么是arr+5而不是arr+4
	//是因为迭代器采用的是前闭后开的结构[ ),所以如果想要把数组全部复制,必须要加到最后一个元素的下一个位置</span></strong>
	vec.push_back(6);
	for (size_t i = 0; i < vec.size(); ++i){
		printf("%d ", vec[i]);
	}
	printf("\n");

}


空间配置器的类型:

SGI STL提供了两种空间配置器类型,一种是std::allocator,另外一种是std::alloc,前一种因为效率不佳,虽然SGI自己定义过它,但是也不建议我们使用。在默认情况、

下,如果我们没有指明空间配置器的类型,那么缺省使用的是std::alloc类型的空间配置器。STL空间配置器主要分三个文件实现:

上去所述的五种全局函数可以参考《STL源码剖析》,这里不再赘述。下面来看看一二级空间配置器的逻辑关系,刚开始的时候看得非常晕,现在把它的思路捋一捋,其实发现没那么难:

第一步:一二级空间配置器的定义:


那么这个_malloc_alloc_template和_default_alloc_template都是个什么东西,别急我们来看看它的定义:

_malloc_alloc_template:


_default_alloc_template:


看到它们的定义我们才发现,原来只是两个无template型别的类模板,说白了就是一个类类型。那么从定义可以看出如果定了_USE_MALLOC,就将alloc指明为_malloc_alloc_template的别名,对于_malloc_alloc_template有的东西,alloc同样拥有,同样可以调用,同理,如果没有定义_USE_MALLOC的话,alloc的别名就是_default_alloc_template。总结一下,alloc就是两个无template型别的类模板的别名,记住是别名,下面我们再来看看这个simple_alloc是个什么鬼???

图一:


要想了解上面的东东怎么使用,我们要先看看它在vector类中的定义:

图二:


图三:


看到这里,也许大家会晕掉,不要慌,下面我来为大家层层解读!!!先看图二,原来data_allocator是simple_alloc模板类的一个别名,我们再来看看它的调用语句:

data_allocator::deallocate,那么我们可能会问这个deallocate到底是谁的????因为data_allocate是simple_alloc类型的一个别名,那么deallocate肯定是类

simple_alloc的,我们再来看看图一,果然simple_alloc果然定义了deallocate函数;再让我们来看看图二的第一条语句template<class T,class Alloc=alloc>我们知道,在

vector类中,Alloc被实例化为alloc,那么simple_alloc<value_type,Alloc>中的Alloc也被实例化成alloc类,那么在simple_alloc类中的class Alloc也被实例化成alloc,所以

Alloc::allocate调用的allocate函数是alloc的,而alloc又是_malloc_alloc_template的别名,所以归根到底,Alloc::allocate调用的是_malloc_alloc_template中的allocate,

这个时候由于_malloc_alloc_template是一级配置器,所以allocate调用实质就是malloc函数!!!!所以不管是alloc被定义成一级配置器还是二级配置器,simple_alloc都对

其进行了再包装,使配置器的接口符合STL规格,为什么???因为alloc是_malloc_alloc_template或是_default_malloc_template的别名,而这两个东东又是两个无

template型别的类模板,这样就不符合STL的模板泛化的规格,所以进行一层包装,使之能够接受泛化的参数,说白了,就是使class关键字起作用!!!!

下面我用一张图来解释整个vector类被实例化的过程:


经过这么多分析,相信大家大概了解了STL空间配置器的一些套路了!!!!!!!本博客乃个人理解,欢迎大家指正!!!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值