侯捷STL课程及源码剖析学习2: allocator
以STL 的运用角度而言,空间配置器是最不需要介绍的东西,它总是隐藏在一切组件(更具体地说是指容器,container)的背后,默默工作默默付出。
一、分配器测试
测试代码
#include <list>
#include <stdexcept>
#include <string>
#include <cstdlib> //abort()
#include <cstdio> //snprintf()
#include <algorithm> //find()
#include <iostream>
#include <ctime> #include <cstddef>
#include <memory> //内含 std::allocator
//欲使用 std::allocator 以外的 allocator, 得自行 #include <ext\...>
#ifdef __GNUC__
#include <ext\array_allocator.h>
#include <ext\mt_allocator.h>
#include <ext\debug_allocator.h>
#include <ext\pool_allocator.h>
#include <ext\bitmap_allocator.h>
#include <ext\malloc_allocator.h>
#include <ext\new_allocator.h>
#endif namespace jj20
{
//pass A object to function template impl(),
//而 A 本身是个 class template, 带有 type parameter T,
//那么有无可能在 impl() 中抓出 T, 创建一个 list<T, A<T>> object?
//以下先暂时回避上述疑问. void test_list_with_special_allocator()
{
#ifdef __GNUC__
cout << "\ntest_list_with_special_allocator().......... \n"; //不能在 switch case 中宣告,只好下面这样. //1000000次
list<string, allocator<string>> c1; //
list<string, __gnu_cxx::malloc_allocator<string>> c2; //
list<string, __gnu_cxx::new_allocator<string>> c3; //
list<string, __gnu_cxx::__pool_alloc<string>> c4; //
list<string, __gnu_cxx::__mt_alloc<string>> c5; //
list<string, __gnu_cxx::bitmap_allocator<string>> c6; //4781 int choice;
long value; cout << "select: "
<< " (1) std::allocator "
<< " (2) malloc_allocator "
<< " (3) new_allocator "
<< " (4) __pool_alloc "
<< " (5) __mt_alloc "
<< " (6) bitmap_allocator "; cin >> choice;
if (choice != ) {
cout << "how many elements: ";
cin >> value;
} char buf[];
clock_t timeStart = clock();
for (long i = ; i< value; ++i)
{
try {
snprintf(buf, , "%d", i);
switch (choice)
{
case : c1.push_back(string(buf));
break;
case : c2.push_back(string(buf));
break;
case : c3.push_back(string(buf));
break;
case : c4.push_back(string(buf));
break;
case : c5.push_back(string(buf));
break;
case : c6.push_back(string(buf));
break;
default:
break;
}
}
catch (exception& p) {
cout << "i=" << i << " " << p.what() << endl;
abort();
}
}
cout << "a lot of push_back(), milli-seconds : " << (clock() - timeStart) << endl; //test all allocators' allocate() & deallocate();
int* p;
allocator<int> alloc1;
p = alloc1.allocate();
alloc1.deallocate(p, ); __gnu_cxx::malloc_allocator<int> alloc2;
p = alloc2.allocate();
alloc2.deallocate(p, ); __gnu_cxx::new_allocator<int> alloc3;
p = alloc3.allocate();
alloc3.deallocate(p, ); __gnu_cxx::__pool_alloc<int> alloc4;
p = alloc4.allocate();
alloc4.deallocate(p, ); //我刻意令参数为 2, 但这有何意义!! 一次要 2 个 ints? __gnu_cxx::__mt_alloc<int> alloc5;
p = alloc5.allocate();
alloc5.deallocate(p, ); __gnu_cxx::bitmap_allocator<int> alloc6;
p = alloc6.allocate();
alloc6.deallocate(p, ); //我刻意令参数为 3, 但这有何意义!! 一次要 3 个 ints?
#endif
}
}
注:直接使用分配器allocate时,释放时须指明释放内存大小。
二、简单的内存分配器
一般而言,c++的内存分配和释放是这样操作的
class Foo{ //...};
Foo* pf = new Foo;//配置内存,然后建构对象
delete pf; //将对象解构,然后释放内存
其中的 new操作内含两阶段动作:(1)调用::operator new配置内存,(2) 调用Foo::Foo()建构对象内容。delete操作也内含两阶段动作: (1)调用Foo::~Foo()将对象解构,(2)调用::operator delete释放内存。
为了精密分工,STL allocator决定将这两阶段区分开来。内存配置由alloc:allocate()负责,内存释放由alloc::deallocate()负责; 对象建构由::construct()负责,对象析构由::destroy()负责。
简单内存分配器代码
#ifndef SIMPLE_JJALLOC_H
#define SIMPLE_JJALLOC_H
#include<new> //for placement new
#include<cstddef> //for ptrdiff_t,size_t
#include<cstdlib> //for exit()
#include<climits> //for UINT_MAX十进制的最大值
#include<iostream> //for cerr namespace JJ
{
/*****************ptrdiff_t与size_t类型*****size_type与difference_type类型********************
****ptrdiff_t:signed类型,通常用于两指针减法操作的结果,它可以是负数。(因为指针相减有正有负)
****size_t:unsigned类型,用于指明数组长度,它是非负整数。
****size_type:unsigned类型,容器中元素长度或下表,vector<int>::size_type i=0;
****difference_type:signed类型,表示迭代器差距,vector<int>::difference_type=iter1-iter2
****前两者位于标准类库std内,后两者为stl对象所有
*********************************************************************************************/
template<class T>
inline T* _allocate(ptrdiff_t size, T*)
{
std::cout<<"I'm _allocate in simple_jjalloc!"<<std::endl;
/**************************new_handler与set_new_handler***********************************
****new_handler:内存分配失败后,调用的处理函数。
****set_new_handler:参数是被指定的new_handler函数指针,返回参数也是new_handler是被替换掉的new_handler
*****************************************************************************************/
std::set_new_handler();
/****************::***********************************************************************
****"::":全局作用。比如::luo这就是个全局变量,而luo这是个局部变量
****"::":类作用。比如Node::function()
****"::":名字空间。比如std::size_t
*****************************************************************************************/
T *tmp=(T*)(::operator new((size_t)(size*sizeof(T))));
if(tmp == )//没有前面的std::set_new_handler(0);把内存分配失败后的异常调用函数给替换掉,就执行不到这儿
{
std::cout<<"failed!"<<std::endl;
std::cerr<<"out of memory"<<std::endl;
exit();
}
return tmp;
} template<class T>
inline void _deallocate(T* buffer)
{
::operator delete(buffer);
}
/************************************new的三种形态*******************************************
****new operator:就是平常用的new,通常做三件事,1.用operator new分配内存给对象,2.调用构造函数初始化那块内存,3.将地址转给对象指针
如果仅仅是在堆上建立对象,那么应该使用new operator,它会提供周全的服务
****operator new:在默认情况下首先会调用分配内存的代码,尝试从堆上得到一段空间,成功就返回,失败就调用new_hander,重复前面过程,直到抛出异常
如果仅仅是分配内存,那么应该调用operator new,但初始化不在它的职责之内。若对默认的内存分配过程不满意,那就重载它
****placement new:用来实现定位构造,可以通过它来选择合适的构造函数。
如果想在一块已获得的内存里建立一个对象,那就改用placement new
********************************************************************************************/
template<class T1,class T2>
inline void _construct(T1* p,const T2& val)
{
new(p) T1(val);//p为那块内存地址,T1()为指定构造函数;此句为p->T1::T1(val);
std::cout<<"I'm _construct!"<<std::endl;
} template<class T>
inline void _destroy(T* ptr)
{
std::cout<<"I'm _destroy!"<<std::endl;
ptr->~T();
} template<class T>
class mallocator
{
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; template<class U>
struct rebind//干吗用?见下
{
typedef mallocator<U> mother;
}; pointer allocate(size_type n,const void* hint=)
{
return _allocate((difference_type)n,(pointer));
} void deallocate(pointer p,size_type n)
{
_deallocate(p);
} void construct(pointer p,const_reference val)
{
_construct(p,val);
} 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(value_type));
}
};
}
#endif
内存分配测试代码:
int ia[5] = {1,2,3,4,5};
vector<int,JJ::mallocator<int>> iv(ia,ia+3);
for (auto i : iv)
cout<<i<<" ";
cout<<endl;
测试结果
I'm _allocate in simple_jjalloc!
I'm _construct!
I'm _construct!
I'm _construct!
1 2 3
I'm _destroy!
I'm _destroy!
I'm _destroy!
直接使用allocate, 不用new. 代码环境GNU 2.9 ,高版本头文件找不到。
#include<iostream>
#include<defalloc.h>
#include<stl_alloc.h>
using namespace std;
void main()
{
void* p = allocator<int>().allocate();
allocator<int>().deallocate((int*)p);
cout << "allocator<int>().allocate(512);"<<endl; void* p2 = alloc::allocate();
alloc::deallocate(p2, ); cout << "allocate(512, 0);" << endl;
}
二、 OOP(Object-Oriented Programming) vs GP(Generic Programming)
OOP是将数据和方法封装在一起,多采用继承和多态。
GP却是将datas 和 methods 分开来。如STL中,Container 和 Algorithms 可以各自独立进行开发,两者使用迭代器进行关联。Algorithms通过iterators 确定操作范围,并获取容器元素。
深入理解留待后续。。。
内容参考:
侯捷STL课程, 《STL源码剖析》
侯捷STL课程及源码剖析学习2: allocator的更多相关文章
- 侯捷STL课程及源码剖析学习3: 深度探索容器list
一.容器概览 上图为 GI STL 2.9的各种容器.图中以内缩方式来表达基层与衍生层的关系.所谓的衍生,并非继承(inheritance)关系,而是内含(containment)关系.例如 heap ...
- 侯捷STL课程及源码剖析学习1
1.C++标准库和STL C++标准库以header files形式呈现: C++标准库的header files不带后缀名(.h),例如#include <vector> 新式C hea ...
- c++ stl源码剖析学习笔记(一)uninitialized_copy()函数
template <class InputIterator, class ForwardIterator>inline ForwardIterator uninitialized_copy ...
- python源码剖析学习记录-01
学习<Python源码剖析-深度探索动态语言核心技术>教程 Python总体架构,运行流程 File Group: 1.Core Modules 内部模块,例如:imp ...
- c++ stl源码剖析学习笔记(二)iterator
ITERATOR 迭代器 template<class InputIterator,class T> InputIterator find(InputIterator first,Inpu ...
- c++ stl源码剖析学习笔记(三)容器 vector
stl中容器有很多种 最简单的应该算是vector 一个空间连续的数组 他的构造函数有多个 以其中 template<typename T> vector(size_type n,cons ...
- STL源码剖析 学习笔记 MiniSTL
https://github.com/joeyleeeeeee97 目录: 第二章 空间适配器 第三章 迭代器 第四章 序列式容器(vector,list,deque,stack,heap,prior ...
- STL源码剖析-学习笔记
1.模板是一个公式或是蓝图,本身不是类或是函数,需进行实例化的过程.这个过程是在编译期完成的,编译器根据传递的实参,推断出形参的类型,从而实例化相应的函数 2. 后续补充-.
- linux0.11内核源码剖析:第一篇 内存管理、memory.c【转】
转自:http://www.cnblogs.com/v-July-v/archive/2011/01/06/1983695.html linux0.11内核源码剖析第一篇:memory.c July ...
随机推荐
- 编写优秀Bug报告的艺术及案例分析
编写优秀Bug报告的艺术及案例分析 ---Rex Black原著<Fine art of writing a good bug report > ---Kiki翻译于2005/5/28 前 ...
- Node.js之process模块
注意⚠️:process为nodejs内置对象,不需要实例化,改模块用来与当前进程进行互动,可以通过全局变量process访问,它是一个EventEmitter对象的实例. process对象提供一系 ...
- leetcode1012
# given number n, see whether n has repeated number def has_repeated(n): str_n = str(n) return len(s ...
- Git安装和基本使用(1)
参考廖雪峰GIt教程 https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/00137 ...
- 查看进程中的socket状态和数量
程序运行时查看,结果是这样子的 C:\Users\Administrator>netstat -ano|findstr TCP TIME_WAIT TCP TIME_WAIT TCP TIME_ ...
- 01-css的引入方式和常用选择器
一.css介绍 现在的互联网前端分三层: HTML:超文本标记语言.从语义的角度描述页面结构. CSS:层叠样式表.从审美的角度负责页面样式. JS:JavaScript .从交互的角度描述页面行为 ...
- HBASE小结--待续使用
构建在HDFS之上的分布式,面向列的存储系统,使用zookeeper做协同服务,在需要实时读写和随机访问超大规模数据集的时候使用 缺点:非关系型,不支持SQL,数据类型单一(字符串,无类型),之支持单 ...
- python import package at different path
1.导入上一级目录的package import sys sys.path.append('..') import <package> # import package at ../ 2. ...
- Examples: How to Pronounce T
Examples: How to Pronounce T Share Tweet Share Tagged With: Flap T, Stop T The [t] sound is not alwa ...
- Android内存优化相关
Android的内存管理方式 Android系统内存分配与回收方式 一个APP通常就是一个进程对应一个虚拟机 GC只在Heap剩余空间不够时才去垃圾回收 GC触发时,所有线程都会被暂停!!! APP内 ...