sgi 之vector
来源:互联网 发布:远程桌面端口 编辑:程序博客网 时间:2024/06/04 17:55
最简单的sgi vector竟然写了四五天。
这次编写所暴露的问题是:
1. 一定要单元测试,否则在最后差错的时候会崩溃的
2. 写代码一定要仔细,记住,要bugfree
ccconstruct.h
#ifndef C_CONSTRUCT_H#define C_CONSTRUCT_H#include <iostream>#include <new.h>inline void destroy(char *, char *){}inline void destroy(int *, int *){}inline void destroy(long *, long *){}inline void destroy(float *, float *){}inline void destroy(double *, double *){}//对于int* p,也可以调用这个函数,比较怪异template <class T>inline void destroy(T* pointer) { pointer->~T();}template <class ForwardIterator>inline void destroy(ForwardIterator first, ForwardIterator last) { for (; first < last ; ++first) destroy(first);}template <class T1, class T2>inline void construct(T1* p, const T2& value) { new (p) T1(value);}#endif
calloc.h
#ifndef C_ALLOC_H#define C_ALLOC_H#include <stdio.h>#include <stdlib.h>enum {ALIGN = 8};//enum {MAX_BYTES = 128};enum {NFREELISTS = 16};#define __THROW_BAD_ALLOC std::cerr << "out of memory " <<std::endl; exit(1)//第一级配置器template <int inst>//这个模板参数在单线程中没有用,主要用于多线程。__malloc_alloc_template<0>,__malloc_alloc_template<1>就实例化出两个不同的类,可以用于两个不同的线程中,这样既不用加锁也不会减速class __malloc_alloc_template {private: //oom: out of memory static void * oom_malloc( size_t); static void * oom_realloc(void *, size_t); static void (* __malloc_alloc_oom_handler )();//这是个函数指针,是一个成员变量,而不是成员函数public: static void * allocate (size_t n) { void * result = malloc(n); if (0 == result) result == oom_malloc(n); return result; } static void deallocate(void *p, size_t) { free(p); } static void *reallocate(void *p, size_t /*old size*/, size_t new_sz) { void *result = realloc(p,new_sz); if (0 == result) return oom_realloc(p, new_sz); return result; } static void (* set_malloc_handler (void (*f)())) () { //set_malloc_handler是一个函数,其参数是一个函数指针,其返回值也是一个函数指针。这地方要好好揣摩。如果将set_malloc_handler (void (*f)()) 看做p,则就是 (*p)(),set_malloc_handler的返回值就是p void (* old)() = __malloc_alloc_oom_handler; __malloc_alloc_oom_handler == f; return old; }};template<int inst>void (*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler) () = 0;template <int inst>void * __malloc_alloc_template<inst>::oom_malloc(size_t n) { void (* my_malloc_handler) (); void *result; for(;;) { my_malloc_handler = __malloc_alloc_oom_handler; if (0 == my_malloc_handler) {__THROW_BAD_ALLOC;} (*my_malloc_handler) ();//如果用户自定义处理函数,则此函数会寻找可用的内存,并释放这个内存 result = malloc(n);//再重新尝试配置内存 if (result) return result; }}template <int inst>void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n) { void (* my_malloc_handler) (); void * result; for (;;) { my_malloc_handler = __malloc_alloc_oom_handler; if (0 == my_malloc_handler) {__THROW_BAD_ALLOC;} (*my_malloc_handler) (); result = realloc(p, n); if (result) return result; }}typedef __malloc_alloc_template<0> malloc_alloc;//第二级配置器template <bool threads, int inst>class __default_alloc_template {private: //bytes上调至8的倍数 static size_t ROUND_UP(size_t bytes) { return ( (bytes + ALIGN -1) & ~(ALIGN - 1)); }private: union obj { union obj * free_list_link; };private: static obj * free_list[NFREELISTS]; static size_t FREELIST_INDEX(size_t bytes) { return ( (bytes + ALIGN -1)/ALIGN -1); } //当freelist中没有大小为n个块,调用此函数,会返回从内存池中返回若干个块,将其中的一个返回,将剩余的放入freelist中 static void *refill(size_t n); //从内存池中分配一大块空间,大小为nobjs个大小为 size的块,如果内存不足,nobjs会减小 static char *chunk_alloc(size_t size, int &nobjs); static char *start_free;//内存池起始位置 static char *end_free;//内存池结束位置 static size_t heap_size;//一个不太重要的变量public: static void * allocate(size_t n) { obj ** my_free_list; obj * result; if (n > MAX_BYTES) return (malloc_alloc::allocate(n)); my_free_list = free_list + FREELIST_INDEX(n); result = *my_free_list; if (result == 0) { void *r = refill(ROUND_UP(n)); return r; } *my_free_list = result->free_list_link; return result; } static void deallocate(void *p, size_t n) { obj * q = (obj *) p; obj ** my_free_list; if (n >MAX_BYTES) {//对于大块就free,对于小块是要回收到freelist中,以备再次使用 malloc_alloc::deallocate(p,n); return; } my_free_list = free_list + FREELIST_INDEX(n); q->free_list_link = *my_free_list; *my_free_list = q; } static void * reallocate(void *p, size_t old_sz, size_t new_sz) { void * result; size_t copy_sz; if (old_sz > MAX_BYTES && new_sz > MAX_BYTES) { return (malloc_alloc::reallocate(p,old_sz, new_sz)); } if (ROUND_UP(old_sz) == ROUND_UP(new_sz)) return p; result = allocate(new_sz); copy_sz = new_sz > old_sz ? old_sz : new_sz; memcpy(result, p , copy_sz); deallocate(p, old_sz); return result; }};template<bool threads, int inst>void * __default_alloc_template<threads,inst>::refill(size_t n) { int nobjs = 20; char *chunk = chunk_alloc(n, nobjs); obj ** my_free_list; obj * result; obj * current_obj, * next_obj; int i; if (1 == nobjs) return chunk; my_free_list = free_list + FREELIST_INDEX(n); result = (obj *)chunk; *my_free_list = next_obj = (obj *)(chunk + n); for (int i = 1;; ++i) { current_obj = next_obj; next_obj = (obj *)((char *)next_obj + n); if (i == nobjs - 1) { current_obj->free_list_link = NULL; break; } current_obj->free_list_link = next_obj; } return result;}template<bool threads, int inst>char *__default_alloc_template<threads, inst>::chunk_alloc(size_t size, int &nobjs) { char * result; size_t total_bytes = size * nobjs; size_t bytes_left = end_free - start_free; if (bytes_left >= total_bytes) { result = start_free; start_free += total_bytes; return result; } else if (bytes_left >= size){//至少能提供一个块 result = start_free; nobjs = bytes_left / size; total_bytes = size * nobjs; start_free += total_bytes; return result; } else { size_t bytes_to_get = 2 * total_bytes +ROUND_UP(heap_size >> 4);//ROUND_UP(heap_size >> 4)作用不大 if (bytes_left >0) { obj ** my_free_list = free_list + FREELIST_INDEX(bytes_left); ((obj *)start_free)->free_list_link = *my_free_list; *my_free_list = (obj *)start_free; } start_free = (char *)malloc(bytes_to_get); if (0 == start_free) {//没有多余内存,需要从freelist中找到块 int i; obj ** my_free_list, *p; for (i = size; i < MAX_BYTES; i += ALIGN) { my_free_list = free_list + FREELIST_INDEX(i); p = *my_free_list; if (0 != p) { *my_free_list = p->free_list_link; start_free = (char *)p; end_free = start_free + i; return chunk_alloc(size,nobjs); } } end_free = 0; start_free = (char *)malloc_alloc::allocate(bytes_to_get); } heap_size += bytes_to_get; end_free = start_free + bytes_to_get; return chunk_alloc(size, nobjs); }}template<bool threads, int inst>char *__default_alloc_template<threads, inst>::start_free = 0;template<bool threads, int inst>char *__default_alloc_template<threads, inst>::end_free = 0;template<bool threads, int inst>size_t __default_alloc_template<threads, inst>::heap_size = 0;//注意一定要有typename告诉编译器,这个模板类肯定有这个类型objtemplate<bool threads, int inst>typename __default_alloc_template<threads, inst>::obj * __default_alloc_template<threads, inst>::free_list[NFREELISTS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };typedef __default_alloc_template<false, 0> alloc;template<class T, class Alloc>class simple_alloc {public: //返回n个T大小的内存 static T *allocate(size_t n) { return 0 == n ? 0 : (T *) Alloc::allocate(n * sizeof(T)); } static T *allocate() { return (T *) Alloc::allocate(sizeof(T)); } static void deallocate(T *p, size_t n) { if (0 != n) { Alloc::deallocate(p, n * sizeof(T)); } } static void deallocate(T *p) { Alloc::deallocate(p, sizeof(T)); }};#endif
cvector.h
#ifndef C_VECTOR_H#define C_VECTOR_H#include "calloc.h"//#include "stl_construct.h"#include <iostream>#include <memory>#include "cconstruct.h"using namespace std;template <class T, class Alloc = alloc>class cvector {public: typedef T value_type; typedef value_type* pointer; typedef value_type* iterator; typedef value_type& reference; typedef const value_type* const_iterator;protected: typedef simple_alloc<value_type, Alloc> data_allocator; iterator start; iterator finish; iterator end_of_storage; void insert_aux(iterator position, const T& x); //这仅是释放vector所占内存,不是析构函数 void deallocate() { if (start) data_allocator::deallocate(start, end_of_storage - start); } iterator allocate_and_fill(size_t n, const T& x) { iterator result = data_allocator::allocate(n);//获得生内存 uninitialized_fill_n(result, n, x);//uninitialized_* 之类的函数都是用于生内存的操作,效率较高 return result; } void fill_initialize(size_t n, const T& value) { start = uninitialized_fill_n(n, value); finish = start + n; end_of_storage = finish; } iterator allocate_and_copy(size_t n, const_iterator first, const_iterator last) { iterator result = data_allocator::allocate(n); uninitialized_copy(first, last, result); return result; }public: iterator begin() { return start; } iterator end() { return finish; } size_t size() const { return finish - start; } size_t capacity() const { return end_of_storage - start; } bool empty() const { return start == finish; } reference operator [] (size_t n) { return *(start + n);} cvector() :start(0), finish(0), end_of_storage(0) {} cvector(size_t n, const T& x) { fill_initialize(n,x); } explicit cvector(size_t n) { fill_initialize(n, T()); } cvector(cvector<T, Alloc>& x) { start = allocate_and_copy(x.size(), x.begin(), x.end()); finish = end_of_storage = start + x.size(); } void swap(cvector<T, Alloc>& x) { std::swap(start, x.start); std::swap(finish, x.finish); std::swap(end_of_storage, x.end_of_storage); } void insert(iterator position, size_t n, const T& x); void resize(size_t new_sz, const T& x) { if (new_sz < size()) { erase(begin() + new_sz, end()); } else insert(end(), new_sz - size(), x); } cvector<T, Alloc>& operator=(const cvector<T, Alloc>& x); void resize(size_t new_sz) { resize(new_sz, T()); } ~cvector() { destroy(start, finish); deallocate(); } reference front() { return *start; } reference back() { return *(finish - 1);} void push_back(const T& x) { if (finish != end_of_storage) { construct(finish, x); ++finish; } else { insert_aux(end(), x); } } iterator insert(iterator position, const T& x) { size_t n = position - start; if (finish != end_of_storage && position == finish()) { construct(finish, x); ++finish; } else insert_aux(position, x); return begin() + n; } void pop_back() { --finish; destroy(finish); } iterator erase(iterator position) { if (position + 1 != finish) copy(position+1, finish, position); --finish; destroy(finish); return position; } iterator erase(iterator b, iterator e) { iterator i = copy(e, finish, b); destroy(i, finish); finish -= e - b; return b; } void clear() { erase(start, finish); }};template <class T, class Alloc>inline booloperator==(const cvector<T, Alloc>& x, const cvector<T, Alloc>& y) { return (x.size() == y.size()) && (equal(x.begin(), x.end(), y.begin()));}template <class T, class Alloc>inline booloperator<(const cvector<T, Alloc>& x, const cvector<T, Alloc>& y) { return lexicographical_compare(x.begin(), x.end(), y.begin(), y.end());}template <class T, class Alloc>inline booloperator!=(const cvector<T, Alloc>& x, const cvector<T, Alloc>& y) { return !(x == y);}template <class T, class Alloc>inline booloperator>(const cvector<T, Alloc>& x, const cvector<T, Alloc>& y) { return y < x;}template <class T, class Alloc>inline booloperator<=(const cvector<T, Alloc>& x, const cvector<T, Alloc>& y) { return !(y < x);}template <class T, class Alloc>inline booloperator>=(const cvector<T, Alloc>& x, const cvector<T, Alloc>& y) { return !(x < y);}template<class T, class Alloc>cvector<T, Alloc>& cvector<T, Alloc>::operator=(const cvector<T,Alloc> &x) { if (&x != this) { size_t len = x.size(); if (len > capacity()) { iterator tmp = allocate_and_copy(len, x.begin(), x.end()); destroy(start, finish); deallocate(); start = tmp; finish = start + len; end_of_storage = finish; } else if (size() >= len) { iterator tmp = copy(x.begin(), x.end(), start); erase(tmp, end()); finish = tmp; } else { finish = copy(x.begin(), x.begin() + size(), start); finish = uninitialized_copy(x.begin() + size(), x.end(), finish); } }}template<class T, class Alloc>void cvector<T,Alloc>::insert_aux(iterator position, const T &x) { if (finish != end_of_storage) { //这里要考虑为什么不直接copy_backward(position, finish, finish+1) //这是因为原vector的最后一个元素要向后移动一个地址,而这个新的地址上没有对象,所以直接construct就行了,这样效率最高,就只有这个新地址不需要析构 construct(finish, *(finish - 1)); ++finish; copy_backward(position, finish - 2, finish - 1); *position = x; } else { size_t old_sz = size(); size_t len = old_sz !=0 ? 2 * old_sz : 1; iterator new_start = data_allocator::allocate(len); iterator new_finish = new_start; try { new_finish = uninitialized_copy(start, position, new_start); construct(new_finish, x); ++new_finish; new_finish = uninitialized_copy(position , finish, new_finish); } catch (...) { destroy(new_start, new_finish); data_allocator::deallocate(new_start, len); throw; } destroy(begin(), end()); deallocate(); start = new_start; finish = new_finish; end_of_storage = start + len; }}template<class T, class Alloc>void cvector<T, Alloc>::insert(iterator position, size_t n, const T &x) { if (n != 0) { if (end_of_storage - finish >= n) { size_t elems_after = finish - position; iterator old_finish = finish; if (elems_after > n) { uninitialized_copy(finish - n, finish, finish); finish += n; copy_backward(position, old_finish - n, old_finish); fill(position, position + n, x); } else { uninitialized_fill_n(finish, n - elems_after, x); finish += n - elems_after; uninitialized_copy(position,old_finish, finish); finish += elems_after; fill(position, old_finish, x); } } else { size_t old_sz = size(); size_t len = old_sz + max(old_sz, n); iterator new_start = data_allocator::allocate(len); iterator new_finish = new_start; new_finish = uninitialized_copy(begin(), position, new_start); uninitialized_fill_n(new_finish, n, x); new_finish += n; new_finish = uninitialized_copy(position, end(), new_finish); destroy(start, finish); deallocate(); start = new_start; finish = new_finish; end_of_storage = start + len; } }}#endif
测试代码:
cvector<term> vec; for (int i = 0; i < 1; ++i) vec.push_back(term("aa",i));
class term {public: string a; int b; term(const string& str, int c):a(str), b(c){} };
push_back第一个时,这时就从内存池里申请了20个40byte的块,一块返回作为vector的一个元素,另外19个串成单链表放入freelist[4]中
push_back第二个时,将40byte的块返还freelist,从内存池中申请20个72byte的块,一块返回作为vector的一个元素,另外19个串成单链表放入freelist[4]中
当元素较多时,就不再用freelist中的块,这时就是,用多少,就malloc多少内存,这时因为vector是连续地址的,所以要找一个连续的内存块
注意,erase,pop_back都不会减少vector所占内存。
当vector被析构时,先依存析构元素,最后再处理vector所占内存。如果这块内存比较小,小于等于128,则还是返还给freelist;如果内存块大于128,就直接释放这块内存
stl有一个比较好的思想,就是stl所有容器的内存分配都是用同一个freelist和内存池,这样就减少了内存碎片和频繁申请内存和释放内存的费时操作
但是这样会有一个副作用,就是程序在长时间的运行中,freelist所带的内存块可能会很多很多,就很占用系统资源,这些内存块又不能主动释放
sgi stl的二级配置器的工作原理是:
如果用户申请的内存>=128,调用第一级配置器,也就是malloc 和free
否则,用二级配置器。
二级配置器,有一个freelist和一个内存池。freelist是一个具有16个元素的数组,每个元素就是一条空闲块的链表,每条链表中的空闲块大小一致,而相邻链表中的空闲块大小相差2倍
如果用户free的内存块大小小于128,就插入到对应freelist的链表的表头
如果用户申请的内存块小于128,并且对应的链表有空闲块,就直接返回此内存块。如果此链表没有空闲块了,则就向内存池申请20个内存块,返回一个给用户,剩下的19个连接成对应的链表。但是,如果内存池空间不足,可能申请不到20个内存块,但内存池的剩余空间至少能供应一个块,同样返回。
如果,内存池空间一个块都不能供应了,那就通过malloc获取40个内存块大小的空间,将20个返回,剩下的空间留在内存池,供下次使用。
如果内存空间已经用尽,malloc也不能获取内存了,那么就要查看freelist中的空闲块,设用户申请的内存大小为p, 那么先查看大于p的最小块所在的链表是否为空,不为空,就将这个内存块放入内存池中,否则,就查看更大的块所在的链表
- sgi 之vector
- SGI STL源码之vector
- SGI STL的序列式容器之vector浅析
- sgi---1 vector
- vector (仿sgi stl)
- 文章标题 SGI的vector
- sgi stl 之list
- sgi 之heap, priority_queue
- SGI源码注释之stl_vector.h初窥
- SGI-STL学习笔记之allocator
- SGI-STL学习笔记之heap算法
- SGI-STL学习笔记之list::sort()
- SGI-STL学习笔记之IntroSort
- SGI-STL学习笔记之IntroSort
- SGI-STL学习笔记之allocator .
- SGI STL 源码解读之Allocator
- SGI STL 源码解读之 Iterator
- SGI STL源码解读之 string
- 深入理解JavaScript系列(7):JavaScript核心(晋级高手必读篇)
- IOS加密 AES 256算法(EncryptAndDecrypt.h)
- iPhone 4S优化指南
- UVA 1339 - Ancient Cipher
- freemarker中导入样式表的写法,以及javascript的写法注意
- sgi 之vector
- kmalloc vmalloc malloc mmap
- Android拓展系列(2)--Git使用
- 我的iPhone桌面
- 一名顶尖程序员的诗意栖居
- proc数据结构
- FreeMarker中集合的判断
- 自己动手写ORM框架(六):实现查询功能FindById方法
- Gerrit审核服务器搭建简易教程(Ubuntu 12.10)