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 memcpy).

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 memcpy). Extra manipulations will be necessary to properly re-initialize the hidden pointers in the copy. For this reason the copy constructor and copy-assignment operator for this class will not qualify as trivial.

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的更多相关文章

  1. c++ stl源码剖析学习笔记(一)uninitialized_copy()函数

    template <class InputIterator, class ForwardIterator>inline ForwardIterator uninitialized_copy ...

  2. c++ stl源码剖析学习笔记(二)iterator

    ITERATOR 迭代器 template<class InputIterator,class T> InputIterator find(InputIterator first,Inpu ...

  3. c++ stl源码剖析学习笔记(三)容器 vector

    stl中容器有很多种 最简单的应该算是vector 一个空间连续的数组 他的构造函数有多个 以其中 template<typename T> vector(size_type n,cons ...

  4. STL源码剖析-学习笔记

    1.模板是一个公式或是蓝图,本身不是类或是函数,需进行实例化的过程.这个过程是在编译期完成的,编译器根据传递的实参,推断出形参的类型,从而实例化相应的函数 2. 后续补充-.

  5. STL源码剖析读书笔记之vector

    STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...

  6. 重温《STL源码剖析》笔记 第三章

    源码之前,了无秘密. --侯杰 第三章:迭代器概念与traits编程技法 迭代器是一种smart pointer auto_Ptr 是一个用来包装原生指针(native pointer)的对象,声明狼 ...

  7. STL源码剖析读书笔记--第四章--序列式容器

    1.什么是序列式容器?什么是关联式容器? 书上给出的解释是,序列式容器中的元素是可序的(可理解为可以按序索引,不管这个索引是像数组一样的随机索引,还是像链表一样的顺序索引),但是元素值在索引顺序的方向 ...

  8. 重温《STL源码剖析》笔记 第六、七、八章 next_permutation (字典序)

    源码之前,了无秘密  ——侯杰 第六章算法 next_permutation 比如:01342 -> 01423 -> 01432 方法:从尾端开始往前寻找两个相邻的元素,令第一个元素为* ...

  9. 重温《STL源码剖析》笔记 第五章

    源码之前,了无秘密  ——侯杰 序列式容器 关联式容器 array(build in) RB-tree vector set heap   map priority-queue multiset li ...

随机推荐

  1. C++小项目-吃豆子游戏

    GMap.h #pragma once //保证头文件只被编译一次 #include "stdafx.h" #define MAP_LEN 19 //逻辑地图大小 (逻辑地图由行. ...

  2. [BZOJ5109/CodePlus2017]大吉大利,晚上吃鸡!

    Description 最近<绝地求生:大逃杀>风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏.在游戏中,皮皮和毛毛最喜欢做的事情就是堵桥,每每有一个好时机都能收到不少的快递 ...

  3. ACM_数数有多少(第二类Stirling数-递推dp)

    数数有多少 Time Limit: 2000/1000ms (Java/Others) Problem Description: 小财最近新开了一家公司,招了n个员工,但是因为资金问题,办公楼只有m间 ...

  4. C# 相关概念

    解决方案 在磁盘上由 .sln 文件表示,是一个或多个相关项目的容器. 例如,如果为 Python 应用程序编写 C++ 扩展,该 C++ 项目可以驻留在同一解决方案中. 解决方案还可以包含 Web ...

  5. 239 Sliding Window Maximum 滑动窗口最大值

    给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧.你只可以看到在滑动窗口 k 内的数字.滑动窗口每次只向右移动一位.例如,给定 nums = [1,3,-1,-3, ...

  6. C语言用指针输出数组里的值

    因为*(arr)是指arr数组内存编号所对应的首地址的值,即arr[0]的值.所以用*(arr+i)可以实现遍历整个数组.

  7. cocos2d-x android 环境部署

    1.下载jdk http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 2.下载 and ...

  8. 动态排序JavaBean

    Java中如果对对象排序可以考虑实现Comparable接口,但是需要排序的属性一旦指定就不能再修改.BeanUtils组件提供了对JavaBean动态排序的支持,即可以在运行时指定排序的属性.实例运 ...

  9. LeetCode第63题--不同路径

    一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” ).机器人每次只能向下或者向右移动一步.机器人试图达到网格的右下角(在下图中标记为“Finish”).现在考虑网格中 ...

  10. 梦想CAD控件文字COM接口知识点

    一.参数绘制文字 在CAD设计时,需要绘制文字,用户可以设置设置绘制文字的高度等属性. 主要用到函数说明: _DMxDrawX::DrawText 绘制一个单行文字.详细说明如下: 参数 说明 DOU ...