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 = 0) { return _allocate((difference_type)n, pointer(0)); } 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 = 8};//小型區塊
enum {_MAXBYTES = 128}; enum {_NFREELISTS = _MAXBYTES/_ALIGN}; enum { _NOBJS = 20 }; private: //可以看作確定地址也可以看作指向下一節點
union obj { union obj *next; char client[1]; }; 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 - 1)&~(_ALIGN - 1)); } static size_t FREELIST_INDEX(size_t bytes) { return (((bytes)+_ALIGN - 1) / _ALIGN - 1); } 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 = 0; char *alloc::end = 0; size_t alloc::heap_size = 0; alloc::obj *alloc::freelist[_NFREELISTS] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; 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 = 0, *next_obj = 0; char *chunk = chunk_alloc(bytes, nobjs); if (nobjs == 1) { return chunk; } else { myfreelist = freelist+ FREELIST_INDEX(bytes); //這一塊返回給客戶端
result = (obj*)chunk; //下面導引freelist指向新配置的空間(內存池)
*myfreelist = next_obj = (obj*)(chunk + bytes); for (int i = 1;; i++) { current_obj = next_obj; next_obj = (obj*)((char*)next_obj + bytes); if (nobjs - 1 == i) { current_obj->next = 0; break; } else { current_obj->next = next_obj; } } return result; } } /* 從鏈表中分配nobjs個空間,每一塊的大小為bytes.如果鏈表中至少有一個滿足要求,那么分配盡可能多的塊並且修改nobjs的值,如果數量不足就從內存中分配兩倍的空間 */
char *alloc::chunk_alloc(size_t bytes, size_t &nobjs) { char *result = 0; 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 = 2 * total_bytes + ROUND_UP(heap_size >> 4); if (bytes_left > 0) { 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 = 0, *p = 0; for (int i = 0; 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 = 0; } 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有一個重要性質,插入和接和操作不會導致原有的迭代器失效。