STL源码剖析 学习笔记 MiniSTL
https://github.com/joeyleeeeeee97
目录:
第二章 空间适配器
第三章 迭代器
第四章 序列式容器(vector,list,deque,stack,heap,priority_queue,slist)
第五章 关联式容器(树的算法 + RB_tree ,set,map,hashtable)
第六章 算法
第七章 仿函数
第八章 适配器(adapet)
第二章 空间适配器
具有次配置力的SGI空间适配器,STL allocator将对象的内存分配和初始化分离开,内存配置由alloc:allocate 负责,内存释放由deallocate 负责,对象构造操作 由 construct() 负责,对象析构操作由destroy() 负责。
构造和析构的基本工具:construct 和destory
trivial constructor ::
|
In simple words a "trivial" special member function literally means a member function that does its job in a very straightforward manner. The "straightforward manner" means different thing for different kinds of special member functions. For a default constructor and destructor being "trivial" means literally "do nothing at all". For copy-constructor and copy-assignment operator, being "trivial" means literally "be equivalent to simple raw memory copying" (like copy with If you define a constructor yourself, it is considered non-trivial, even if it doesn't do anything, so a trivial constructor must be implicitly defined by the compiler. In order for a special member function to satisfy the above requirements, the class must have a very simplistic structure, it must not require any hidden initializations when an object is being created or destroyed, or any hidden additional internal manipulations when it is being copied. For example, if class has virtual functions, it will require some extra hidden initializations when objects of this class are being created (initialize virtual method table and such), so the constructor for this class will not qualify as trivial. For another example, if a class has virtual base classes, then each object of this class might contain hidden pointers that point to other parts of the very same object. Such a self-referential object cannot be copied by a simple raw memory copy routine (like For obvious reasons, this requirement is recursive: all subobjects of the class (bases and non-static members) must also have trivial constructors. |
通过type_traits 判断是不是POD类型然后使用相应的destroy
#pragma once
#ifndef _CONSTRUCT_H
#define _CONSTRUCT_H #include<new.h> template<class T1, class T2>
inline void construct(T1* p, const T2& value)
{
new(p) T1(value);
} template<class T>
inline void destroy(T* ptr)
{
ptr->~T();
} //以下是destroy()第二版本 接受两个迭代器此函数设法找出元素的数值型别
//进而利用__type_traits<>求取最适当的措施
template<class FowardIterator>
inline void __destroy(ForwardIterator first, ForwardIterator last, __true_type)
{ } template<class FowardIteraot>
inline void __destroy(ForwardIterator first, ForwardIterator last, __false_type)
{
for (;; first < last; first++)
destroy(&*first);
} template<class FowardIterator,class T>
inline void destroy(ForwardIterator first, ForwardIterator last, T*)
{
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destroy(first, last, trivial_destructor());
}
#endif
空间的配置和释放 std::alloc
SGI对此的设计哲学如下: 向system heap 要求空间;考虑多线程状态;考虑内存不足时候的应变措施;考虑过多“小型区块”可能造成的碎片问题
双层配置器:第一层直接使用malloc 和free 第二级配置器在大于128bytes的请求下 转为一级配置器分配内存,在小于128bytes的内存请求下,内存池分配。
#ifndef _ALLOCATOR_H
#define _ALLOCATOR_H #include<new>
#include<cstddef>
#include<climits>
#include<iostream>
#include<cstdlib>
#include"construct.h"
namespace ministl
{
template<class T>
inline T* _allocate(size_t n, T*)
{
return static_cast<T*>(alloc::allocate(n));
} template<class T>
inline void _deallocate(T* buffer)
{
alloc::deallocate(static_cast<T*>(buffer), sizeof(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; template<class U>
struct rebind
{
typedef allocator<U> other;
}; pointer allocate(size_type n,const void* hint = )
{
return _allocate((difference_type)n, pointer());
} void deallocate(pointer p, size_type n)
{
_deallocate(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));
}
};
}
#endif
#pragma once
#ifndef _ALLOC_H__
#define _ALLOC_H__
#include <cstdlib>
/*
第二级适配器,当区块小于128bytes时,以内存池管理
*/
namespace ministl
{
class alloc
{
private:
enum {_ALIGN = };//小型区块
enum {_MAXBYTES = };
enum {_NFREELISTS = _MAXBYTES/_ALIGN};
enum { _NOBJS = };
private:
//可以看作确定地址也可以看作指向下一节点
union obj
{
union obj *next;
char client[];
}; static obj* freelist[_NFREELISTS]; private:
static char *start, *end;
static size_t heap_size; private:
static size_t ROUND_UP(size_t bytes)
{
return ((bytes + _ALIGN - )&~(_ALIGN - ));
}
static size_t FREELIST_INDEX(size_t bytes)
{
return (((bytes)+_ALIGN - ) / _ALIGN - );
}
static void* refill(size_t n);
static char* chunk_alloc(size_t size, size_t &nobjs);
public:
static void* allocate(size_t bytes);
static void deallocate(void* ptr, size_t bytes);
static void *reallocate(void *ptr, size_t old_size, size_t new_size);
}; char *alloc::start = ;
char *alloc::end = ;
size_t alloc::heap_size = ;
alloc::obj *alloc::freelist[_NFREELISTS] = { ,,,,,,,,,,,,,,, }; void *alloc::allocate(size_t bytes)
{
if (bytes > _MAXBYTES)
{
return malloc(bytes);
}
obj *pos = freelist[FREELIST_INDEX(bytes)];
if (pos)
{
freelist[FREELIST_INDEX(bytes)] = pos->next;
return pos;
}
else
{
return refill(ROUND_UP(bytes));
}
} void alloc::deallocate(void* ptr, size_t bytes)
{
if (bytes > _MAXBYTES)
{
free(ptr);
}
else
{
obj *tmp = (obj *)ptr;
tmp->next = freelist[FREELIST_INDEX(bytes)];
freelist[FREELIST_INDEX(bytes)] = tmp;
}
} void *alloc::reallocate(void *ptr, size_t old_size, size_t new_size)
{
alloc::deallocate(ptr, old_size);
ptr = allocate(new_size);
return ptr;
}
//返回一个大小为n的对象,并且有时候会为适当的freelist增加节点
//假设n已经上调到8的倍数
void *alloc::refill(size_t bytes)
{
size_t nobjs = _NOBJS;
obj **myfreelist = NULL;
obj *result = NULL;
obj *current_obj = , *next_obj = ;
char *chunk = chunk_alloc(bytes, nobjs);
if (nobjs == )
{
return chunk;
}
else
{
myfreelist = freelist+ FREELIST_INDEX(bytes);
//这一块返回给客户端
result = (obj*)chunk;
//下面导引freelist指向新配置的空间(内存池)
*myfreelist = next_obj = (obj*)(chunk + bytes); for (int i = ;; i++)
{
current_obj = next_obj;
next_obj = (obj*)((char*)next_obj + bytes); if (nobjs - == i)
{
current_obj->next = ;
break;
}
else
{
current_obj->next = next_obj;
}
}
return result;
}
}
/*
从链表中分配nobjs个空间,每一块的大小为bytes.如果链表中至少有一个满足要求,那么分配尽可能多的块并且修改nobjs的值,如果数量不足就从内存中分配两倍的空间
*/
char *alloc::chunk_alloc(size_t bytes, size_t &nobjs)
{
char *result = ;
size_t total_bytes = bytes*nobjs;
size_t bytes_left = end - start;
if (bytes_left >= total_bytes)
{
result = start;
start = start + total_bytes;
return result;
}
else if(bytes_left >= bytes)
{
nobjs = bytes_left / bytes;
total_bytes = nobjs*bytes;
result = start;
start = start + total_bytes;
return result;
}
else
{
size_t bytes_to_get = * total_bytes + ROUND_UP(heap_size >> );
if (bytes_left > )
{
obj* volatile *myfreelist = freelist + FREELIST_INDEX(bytes_left);
((obj*)start)->next = *myfreelist;
*myfreelist = (obj*)start;
}
start = (char*)malloc(bytes_to_get);
if (!start)
{
obj **myfreelist = , *p = ;
for (int i = ; i < _MAXBYTES; i += _ALIGN)
{
myfreelist = freelist + FREELIST_INDEX(i);
p = *myfreelist;
if (!p)
{
*myfreelist = p->next;
start = (char*)p;
end = start + i;
return chunk_alloc(bytes, nobjs);
}
}
end = ;
}
heap_size += bytes_to_get;
end = start + bytes_to_get;
return chunk_alloc(bytes, nobjs);
}
} }
#endif
4.3 list
相比較vector的連續空間,list的好處在於每次插入或者刪除一個元素就配置或者釋放一個元素的空間,因此List對於空間的運用绝对的精准,list本身和list的结点是不同的结构需要分开设计。list不能用普通指针作为迭代器因为它的结点不能抱枕在存储空间中连续存在,list迭代器必须有能力指向List的节点并且正确的进行递增递减操作,list是一个双向迭代器。list有一个重要性质,插入和接和操作不会导致原有的迭代器失效。
STL源码剖析 学习笔记 MiniSTL的更多相关文章
- c++ stl源码剖析学习笔记(一)uninitialized_copy()函数
template <class InputIterator, class ForwardIterator>inline ForwardIterator uninitialized_copy ...
- 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源码剖析-学习笔记
1.模板是一个公式或是蓝图,本身不是类或是函数,需进行实例化的过程.这个过程是在编译期完成的,编译器根据传递的实参,推断出形参的类型,从而实例化相应的函数 2. 后续补充-.
- STL源码剖析读书笔记之vector
STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...
- 重温《STL源码剖析》笔记 第三章
源码之前,了无秘密. --侯杰 第三章:迭代器概念与traits编程技法 迭代器是一种smart pointer auto_Ptr 是一个用来包装原生指针(native pointer)的对象,声明狼 ...
- STL源码剖析读书笔记--第四章--序列式容器
1.什么是序列式容器?什么是关联式容器? 书上给出的解释是,序列式容器中的元素是可序的(可理解为可以按序索引,不管这个索引是像数组一样的随机索引,还是像链表一样的顺序索引),但是元素值在索引顺序的方向 ...
- 重温《STL源码剖析》笔记 第六、七、八章 next_permutation (字典序)
源码之前,了无秘密 ——侯杰 第六章算法 next_permutation 比如:01342 -> 01423 -> 01432 方法:从尾端开始往前寻找两个相邻的元素,令第一个元素为* ...
- 重温《STL源码剖析》笔记 第五章
源码之前,了无秘密 ——侯杰 序列式容器 关联式容器 array(build in) RB-tree vector set heap map priority-queue multiset li ...
随机推荐
- 制作并发布个人CocoaPods库
随着对 CocoaPods 越来越多的依赖,我们也可以尝试把自己的库发布到它上面. 1.在Github上新建一个项目(名字我随便取了一个,其他步骤截图为WCUIKit).自己做相应修改即可. 2.克隆 ...
- 组合数学题 Codeforces Round #108 (Div. 2) C. Pocket Book
题目传送门 /* 题意:每一次任选i,j行字符串进行任意长度前缀交换,然后不断重复这个过程,问在过程中,第一行字符串不同的个数 组合数学题:每一列不同的字母都有可能到第一行,所以每列的可能值相乘取模就 ...
- checkbox全选和取消功能
这是开发中常见的小功能,想当初我也曾对于attr和prop的不了解踩过坑. 前端工作中,常常会使用到select复选框,select复选框有一个属性checked,当使用js或者jquery控制这个属 ...
- 论tab切换的几种实现方法
tab切换在网页中很常见,故最近总结了4种实现方法. 首先,写出tab的框架,加上最简单的样式,代码如下: <!DOCTYPE html> <html> <head> ...
- android中用Intent传数据,如果用传递的是一个类,就将类实现Parcelable接口
Parcelable,内存单位,跨进程使用,或者intent传递对象的时候使用.android中用Intent传数据,如果用传递的是一个对象,就将对象实现Parcelable接口,而不是将对象序列化. ...
- tomcat日志详释
1.tomcat的日志分类: 一是运行中的日志,它主要记录运行的一些信息,尤其是一些异常错误日志信息 . 二是访问日志信息,它记录的访问的时间,IP ,访问的资料等相关信息. 2.tomcat的日志目 ...
- 呼啦圈(keyframes和transform结合)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- MySql(五)select排序查询
举个栗子/**查询员工信息,要求工资按照从高到低进行排序(默认升序)**/SELECT * FROM employees ORDER BY salary ASC;/**方法2:**/SELECT * ...
- lsb_release No LSB modules are available
lsb_release 提示: No LSB modules are available 执行: sudo apt-get install lsb-core
- 14XML解析
XML解析 XML解析 DOM4J DOM4J是dom4j.org出品的一个开源XML解析包Dom4j是一个易用的.开源的库,用于XML,XPath和XSLT的解析及相关应用.它应用于Java平台,采 ...