SGI STL一级二级空间配置器代码剖析笔记

2023-11-02

       STL不仅是一个可复用组件库,而且是一个包罗算法与数据结构的软件框架。说到框架本身就有庞大,稳定,完整而可扩展的含义。因此学习STL源码不仅可以帮助我们编写良好代码的习惯,而且能够让我们熟悉算法,数据结构,我们也会受到这种编程思维影响,在编写代码时考虑全方面,可复用,高效的情况。只要好好的分析,一定能收获很多。我也是菜鸟一只,在这里想将自己看的与想的记录与分享!

我剖析的是SGL 版本的STL,读的书是候捷的书。

STL六大组件:空间配置器,容器,迭代器,算法,仿函数,配置器。

容器:各种数据结构,vector,list,deque,set,map用来存放数据。

算法:各种常见算法,sort,search,copy,erase。STL算法是一种function template。

迭代器:扮演的角色是容器与算法的粘合剂,是所谓的“泛型指针”。 共5种类型,从实现角度看,迭代器实现了operator*,operator->,operator++,operator--等指针操作重载的class temlpate。

为什么是粘合剂?根据我的理解,在算法的实现上面看,算法本身的实现与数据的存储无关。既算法的思维是不变的,不会因为你存的数据不同算法不同。因此设计迭代器在数据与算法之间架起了一个桥梁。

仿函数:类似函数,作为算法的一种策略。

配接器:一种修饰容器,或者仿函数,或迭代器接口的东西,。

空间配置器:负责空间的分配与管理,此篇文章就讨论一下空间配置器。(没有考虑多线程的情况)

一级空间配置器__malloc_alloc_template:

首先这个类中一共有下面几个函数:

函数名 函数作用
static void *allocate(size_t n) 申请n字节空间
static void *reallocate(void *p,size_t oldsz,size_t newsz) p指向空间大小改变,重新开辟newsz空间大小
static void (*set_malloc_handler(void (*f)()))() 函数指针函数,用来模拟set_new_handler机制
static void *oom_malloc(size_t n) 用来allocate函数里面申请空间出错处理
static void *oom_realloc(void*,size_t n) 用来reallocate函数里面申请空间出错处理
static void  deallocate(void *p,size_t n) 释放p指向空间

除了几个函数之外还有个静态变量:

static void (*_malloc_alloc_oomhandler)();  //函数指针

一级空间配置中,比较重要的是set_new_handler机制的模拟实现。在vc中不支持这个机制,在vs中支持。在这里他模拟了此实现。

主要在这里就是分析这个set_new_handler机制的主要实现。

static void (*set_malloc_handler(void(*f)()))()
{
    void (*old)()= _malloc_alloc_oomhandler;
	_malloc_alloc_oomhandler = f;
	return *old;
}

_malloc_alloc_oomhandler是一个函数指针,开始赋值为空。(静态变量在类外初始化)

set_malloc_handler() 函数的参数是函数指针,接收_malloc_alloc_oomhandler类型的函数指针。我们可以看见函数实现中,为_malloc_alloc_oomhandler赋予了一个函数地址,相当于为set_new_handler机制提供了处理函数。

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_oomhandler; //等待用户设置处理函数 set_new_handler
		if(0 == my_malloc_handler)
		{
			__THROW_BAD_ALLOC;
		}
		(*my_malloc_handler)();
		result = malloc(n);
		if(result)
			return result;
	}
}

从上面的代码中我们可以看见 如果当 _malloc_alloc_oomhandler为空时,函数会直接抛出异常,如果不为空,则会指向所指向函数。这是在oom_malloc()函数执行,当执行到这里时,已经说明在申请空间时不能正常申请,转而调用处理函数,或者抛出异常。

画出一级空间配置器执行流程图:

二级空间配置器:

类声明如下:

enum{__ALIGN = 8};
enum{__MAX_BYTES=128};
enum{__NFREELISTS =__MAX_BYTES / __ALIGN}; //free-list个数

template<bool threads,int inst>
class __default_alloc_template
{
private:
	static size_t ROUND_UP(size_t bytes) //将bytes上调为8的倍数
	{
		return (((bytes)+__ALIGN-1)&~(__ALIGN-1));
	}
	static size_t FREELIST_INDEX(size_t bytes) // 由bytes计算其应该使用free_list的下标
	{
		return (((bytes)+__ALIGN-1) / __ALIGN-1);
	}
private:
	union obj //free_list 节点构造
	{
		union obj *free_list_link;
		char client_data[1];
	};
	static void *refill(size_t n); //申请空间,返回一个大小为n的对象
    //配置一大块空间,大小为nobjs个size,如果配置nobjs个区块不便,nobjs可能会降低
	static char* chunk_alloc(size_t size,int &nobjs);
	static obj *free_list[__NFREELISTS]; //自由链表数组 
private:
	static char *start_free; //内存池起始位置
	static char *end_free;   //内存池结束位置
	static size_t heap_size;
public:
	static void *allocate(size_t n); //申请空间
	static void deallocate(void *p,size_t n); //回收空间
	static void *reallocate(void *p,size_t old_sz,size_t new_sz);
};
//为静态成员变量初始化
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;
template<bool threads,int inst>
__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};

二级空间配置器设计巧妙,它与一级配置器规定当申请空间大于128字节时,由一级空间配置器负责申请空间,当申请空间字节小于128时,由二级空间配置器负责。而二级空间配置值将128字节大小分成了16组,每组都是一个自由链表。每组各自管理大小分别是8,16,24,32.。。。。128bytes的大小区块。

例如当申请5个字节时,则会在free_list[0]中为其分配空间。计算过程为 : FREELIST_INDEX(8)计算出来其应该在数组下标为0的自由链表中分配空间。

下面讲解函数:

allocate函数:

template<bool threads,int inst>
void *__default_alloc_template<threads,inst>::allocate(size_t n)
{
	obj ** my_free_list;
	obj *result;
	if(n > (size_t)__MAX_BYTES)
	{
		cout<<"调用一级空间配置器"<<endl;
		return (malloc_alloc::allocate(n));
	}
	my_free_list = free_list+FREELIST_INDEX(n);
	result = *my_free_list;
	if(NULL == result)
	{
		void *r = refill(ROUND_UP(n));
		return r;
	}
	*my_free_list = result->free_list_link;
	return result;
}

allocate函数流程: 申请空间: 当申请空间字节大于128字节 调用一级空间配置器。否则调用FREELIST_INDEX()函数让指针指向适当的数组元素地址,然后将数组中保存的自由链表地址赋值给result,既result指向了自由链表。如果自由链表为空,那么就调用refill()函数(此处调用了ROUND_UP()函数,申请空间大小都为8的倍数)去填充空间,然后将申请到的空间返回。如果不为空,则说明这个位置有相同大小可用的块(>=1块)。则进行头删操作(链表头删,将第一块分配出去),然后返回result。

refill()函数:

template<bool threads,int inst>
void *__default_alloc_template<threads,inst>::refill(size_t n)
{
	int nobjs =20;
	//调用chunk_alloc尝试取得nobjs区块作为free_list的新节点
	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(i=1;;++i) // 进行分块
	{
		current_obj = next_obj;
		next_obj =(obj*)((char*)next_obj+n);
		if(nobjs -1 == i)
		{
			current_obj->free_list_link = 0;
			break;
		}
		else
		{
			current_obj->free_list_link = next_obj;
		}
	}
	return result;
}

refill()函数流程: 设置nobjs =20,表示需要申请nobjs块size大小的空间。在二级空间配置器中,如果当某个自由链表为空,则会默认 为它在内存池中申请20块size大小的空间。这个大小会随着真实剩余空间改变。

                         调用chunk_alloc(size,nobjs)函数,真正的空间配置函数,去分配nobjs*size大小空间。然后返回空间的首地址。

                         如果nobjs为1,注意,nobjs的参数传递是引用,可以在函数里面改变。也就是因为空间不足,实际只申请了一块大小空间。虽然少,但是够我们这一次用了。然后直接返回。

                         否则 调用函数FREELIST_INDEX()函数让指针指向适当的数组元素地址(存的是指向自由链表的指针),接下来的工作就是将申请来的空间分割成块,块的大小就是你要申请空间的最小8的倍数。然后返回首个块地址(这个块就不会在自由链表中,因为已经分配出去了)。

chunk_alloc函数(分配空间函数):

template<bool threads,int inst>
char* __default_alloc_template<threads,inst>::chunk_alloc(size_t size,int &nobjs)
{
	char *result;
	size_t total_size = size *nobjs;
	size_t bytes_left = end_free - start_free; //内存池剩余空间
	if(bytes_left >=total_size) //完全够分配
	{
		result = start_free;
		start_free += total_size;
		return result;
	}
	else if(bytes_left >=total_size)  //只够分配一个或多个。不够size个
	{
		nobjs = bytes_left /size;
		total_size = size*nobjs;
		result = start_free;
		start_free +=total_size;
		return result;
	}
	else   //内存池一个都不能分配
	{
		size_t  bytes_to_get = 2*total_size+ROUND_UP(heap_size>>4);
		//回收零头
		if(bytes_left >0)
		{
			obj **my_free_list;
			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)
		{
			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); //修正nobjs
	}
}

chunk_alloc函数过程: 进入函数后,首先计算需要申请空间的大小 total_size,然后计算了内存池空间的大小bytes_left。

                               当内存池剩余空间大小等于 需求大小空间时,哪我们可以直接分配。让result指向start_free(指向内存池空间开始),

然后将start_free +=total_size;改变内存池起始位置。(返回的是整块空间)

                              当内存池剩余空间大小不足分配total_size大小时,哪我们可以减少申请的空间。计算新的nobjs大小,改变内存池首地址位置,然后返回申请到空间首地址。

                              当内存池剩余空间不足分配块空间时,我们只能向堆申请空间。不过在此之前我们依然有措施可能申请到空间。注意的是在向堆申请空间时,一般都是申请2倍空间(用一半,此时内存池所剩无几,用做内存池补充)。回收零头,将内存池中无法分配出一个块的内存挂到合适的自由链表中去。采用同样是头插方式。然后向堆申请空间,申请成功后,改变heap_size大小,让start_free指向新的内存池地址,改变end__ree指向,然后递归调用chunk_alloc(),做法非常高明,这样调用后,此时内存池中已经有足够的空间,这时就会为其分配适当的空间然后返回。但是这是在向堆区申请空间成功后,如果失败,说明系统内存没有足够的空间,此时就会在块足够大的自由链表下面去寻找一块空间。将找到的块在其自由链表中取出,然后充当为内存池。然后继续递归调用chunk_alloc(),修正nobjs。如果在自由链表中依然没有找到可以用的空间,此时已经是山穷水尽,但是二级配置器采用了调用一级配置器的方法,不是都没有空间了吗?调用一级有用吗? 答案是可能有用,因为一级空间配置器中有一个处理函数,它可能会帮我们省出内存空间。

 deallocate函数(空间回收函数):

template<bool threads,int inst>
void __default_alloc_template<threads,inst>::deallocate(void *p,size_t n)
{
	obj *q = (obj*)p;
	obj **my_free_list;
	if(n > __MAX_BYTES)
	{
		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;
}

   deallocate函数(空间回收函数):当回收的空间大于128个字节后,调用一级回收函数。当小于128字节通过FREELIST_INDEX(n)函数找到合适的自由链表,然后依然是头插,将空间p插入到自由链表中,至此空间回收完成。

同样画一个流程图来理解二级空间配置器顶点执行过程:

在我看来应该要把分配空间和申请空间区别出来,分配空间的意思是在已经有的空间中分配出一部分,申请空间是指向系统申请空间。在二级空间配置器中,只有在chunk_allic()函数中不能分配足够大的块时,向系统申请空间,其他情况都是在自由链表和内存池中分配空间。

讲了这么多,也只是大概把空间配置器的过程讲清楚,其中更有一些实现技巧,也是对c语言基础知识的考验。读源码不仅可以补充语法基础,也能让我们看出这些经典代码考虑问题的全面,复用性强的特点。我们不仅要学习语法,更深层的磨炼,更要学习这种思维。

其还实现了一个接口,可以通过接口来调用一二级空间配置器。

template<class T,class Alloc>
class simple_alloc
{
public:
	static T *allocate(size_t n)
	{
		return (0 == n)?0:(T*)Alloc::allocate(n*sizeof(T));
	}
	static T *allocate(void)
	{
		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));
	}
};

我上面贴的代码是不全的,需要空间配置器完整部分代码的请到这里来下载:

https://github.com/hjh1997/info_bak/tree/STL

希望大家能够一起学习,一起进步!能分享自己的学习经验与想法,多多讨论!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SGI STL一级二级空间配置器代码剖析笔记 的相关文章

  • C++ STL 下一个排列与组合

    我知道我可以使用std next permutation在包含元素的某些容器上 1 2 3 这将生成该序列的 6 种排列 我想做的是给定一些设置 1 2 3 4 5 6 生成大小为 3 的所有可能的排列 因此对于这个例子 4 3 2 将是由
  • 使用 std::istream_iterator 限制 std::copy 的范围

    我构建了一个最小的工作示例来展示我在使用 STL 迭代器时遇到的问题 我在用着istream iterator读书floatss 或其他类型 来自 astd istream include
  • 如何找到向量中第一个小于整数 X 的元素? (c++)

    如果我有以下向量 10 10 10 20 20 20 30 30 我想要一个函数返回 X 的整数的位置或直接返回 X 之后的较小元素 例如如果我正在搜索 11 我希望函数返回 2 因为第二个元素 10 是第一个较小的元素向量中大于 11 的
  • 如何在 g++ 中使用不同的 STL

    我想对 g 使用不同的 STL 而不是其默认的 libstdc 做到这一点最简单的方法是什么 我发现 nostdinc 标志禁止 g 查找其 STL 标头 但这只是编译时的事情 它仍然会使 g 链接到它自己的 STL 所以我需要找到一种方法
  • STL迭代器是否保证集合更改后的有效性?

    假设我有某种集合 并且我在它的开头获得了一个迭代器 现在假设我修改了该集合 无论集合或迭代器的类型如何 我仍然可以安全地使用迭代器吗 为了避免混淆 以下是我讨论的操作顺序 获取集合的迭代器 修改集合 显然 不是其中的元素 而是集合本身 使用
  • 如何修复 STL 样式容器以容纳不完整或抽象类型?

    几天前 我尝试以与 STL 容器相同的风格编写一个基本的树实现 现在我尝试在我的代码中使用它 但是有两件事似乎不起作用 但可以说std vector 即 使用不完整类型和使用抽象类型 如何修复我的树实现以获得此功能 我尝试稍微压缩一下我的代
  • 如何安全地将对象(尤其是 STL 对象)传入和传出 DLL?

    如何将类对象 尤其是 STL 对象 传入和传出 C DLL 我的应用程序必须以 DLL 文件的形式与第三方插件交互 并且我无法控制这些插件是使用什么编译器构建的 我知道 STL 对象没有保证的 ABI 并且我担心这会导致我的应用程序不稳定
  • C++ STL type_traits 问题

    我正在看最新的C9讲座 http channel9 msdn com Shows Going Deep C9 Lectures Stephan T Lavavej Standard Template Library STL 10 of 10
  • 带有自定义分配器的 std::string

    所以我目前正在编写一个内存调试器 为此我需要 stl 容器对象来使用未跟踪的分配器 我的整个代码库中都散布了 std string 因此我将其键入以使用未跟踪的分配器 typedef std basic string
  • 尝试将元素推入向量

    在头文件 我没有编写 中 已经定义了一个结构体 如下所示 struct MemoryMessage public boost counted base public FastAlloc explicit MemoryMessage Memo
  • C++类名冲突

    我现在正在做一个项目 需要整合两个子项目 项目A是用C 编写的 项目B是用C编写的 一个问题是 在项目B中 有一个名为vector它是由其作者创建的 在项目 A 中 std vector in STL用来 因为项目B以后可能会更新 所以我不
  • 为什么 std::queue 使用 std::dequeue 作为底层默认容器?

    继续阅读cplusplus com http www cplusplus com reference queue queue std queue实现如下 队列被实现为容器适配器 这些类 使用特定容器类的封装对象作为其 底层容器 提供一组特定
  • std::map 和二叉搜索树

    我读过 std map 是使用二叉搜索树数据结构实现的 BST 是一种顺序数据结构 类似于数组中的元素 它将元素存储在 BST 节点中并按其顺序维护元素 例如如果元素小于节点 则将其存储在节点的左侧 如果元素大于节点 则将其存储在节点的右侧
  • 以下代码使用 std::set “合法”吗?

    我有这个代码 set
  • C++ 在地图中插入 unique_ptr

    我有一个 C 类型的对象ObjectArray typedef map
  • 更新 OSX 命令行工具 6.3 后缺少 C++ 标头 <__debug>

    从 App Store 更新到 Command Line Tools 6 3 后 程序包括
  • 使用 pybind11 修改 std::array 的默认值

    我的目标是修改在中声明的数组C struct并赋予默认值 我读过了this https pybind11 readthedocs io en stable advanced cast stl html making opaque types
  • C++/STL 字符串:如何使用通配符模仿正则表达式之类的函数?

    我想使用通配符比较 4 个字符串 例如 std string wildcards H RH H 0 5 in the last one I need to check if string is H0 and H5 只用STL能实现吗 谢谢
  • 访问由 std::shared_ptr 包装的类的运算符重载

    我的想法是我想要一个由以下内容包裹的类std shared ptr 仍然可以使用 就像它们不是指针一样 例如在我的类中定义的operator 我的课程结束后仍然可以使用std shared ptr 例如 template
  • 在 C++03 中将成员函数传递给 for_each(无 boost,无 c++11)

    下面的 解决方案 可以编译 但这不是我想要的 我想通过put成员函数for each并不是 this 使用升压是NOT一个选项 这可以在 C 03 中解决吗 include

随机推荐

  • Java阻塞队列

    目录 一 阻塞队列的特点 二 生产者 消费者 存在问题 三 阻塞队列 Java实现 属性 方法 put方法 生产者 线程专门调用的方法 get方法 消费者 线程专门调用的方法 执行顺序分析 图解 在我们上图的代码当中 如果把while改成i
  • Sharding-JDBC(八)5.3 系列升级解读

    目录 一 背景 二 影响范围 1 Maven 坐标调整 2 自定义算法调整 3 事务调整 4 配置文件调整 三 升级指导 1 新的 ShardingSphereDriver 数据库驱动 2 正在使用 Spring Boot Starter
  • 2023华为OD机试真题【找朋友/单调栈】

    题目描述 在学校中 N个小朋友站成一队 第i个小朋友的身高为height i 第i个小朋友可以看到的第一个比自己身高更高的小朋友j 那么j是i的好朋友 要求j gt i 请重新生成一个列表 对应位置的输出是每个小朋友的好朋友位置 如果没有看
  • python爬虫系列5--xpath

    教程地址 http www runoob com xpath xpath tutorial html XPath在python的爬虫学习中 起着举足轻重的地位 对比正则表达式re两者可以完成同样的工作 实现的功能也差不多 但XPath明显比
  • 用 STM32 通用定时器做微秒延时函数(STM32CubeMX版本)

    概述 在使用 DHT11 的时候 时序通信需要微秒来操作 STM32CubeMX 自带一个系统时钟 但是实现的是毫秒级别的 因此就自己用通用计时器实现一个 文章目录 概述 1 配置定时器时钟 2 计数器时钟频率及计数模式 预分频系数 计数器
  • tomcat调优的几个方面

    和早期版本相比最新的Tomcat提供更好的性能和稳定性 所以一直使用最新的Tomcat版本 现在本文使用下面几步来提高Tomcat服务器的性能 增加JVM堆内存大小 修复JRE内存泄漏 线程池设置 压缩 数据库性能调优 Tomcat本地库
  • css画间距可控制的虚线

    借助linear gradient dash div margin left 50px margin right 50px height 10px background linear gradient to left transparent
  • linux git代码明明是最新版本的,status为啥全是modified?

    解决办法 依次执行以下两句代码 git rm cached r git reset hard
  • 使用 PyTorch 对自定义数据集进行二分类(基于Vision Transformer)

    内容 简短描述 ViT 的简短描述 编码部分 使用 ViT 对自定义数据集进行二分类 附录 ViT hypermeters 解释 简短描述 视觉转换器是深度学习领域中流行的转换器之一 在视觉转换器出现之前 我们不得不在计算机视觉中使用卷积神
  • 【Python】turtle海龟画图练习

    Turtle 方法查看 turtle 海龟绘图 同心圆 import turtle i 1 r 0 while i lt 6 r 30 自己设 turtle circle r 画个圆 turtle penup 起笔 turtle sety
  • 前后端RSA加解密

    前端vue RSA加密 一 安装 npm install jsencrypt save dev 二 创建js文件 在src目录下创建util文件夹 然后在util文件夹下创建 security js 文件 1 引入jsencrypt 引入加
  • Java数据类型转换

    1 基本数据类型 byte short char int long float double boolean 2 引用类型数据 String 枚举 数组 接口 枚举 3 基本数据和引用类型数据的区别 1 基本数据类型变量 存的是值的本身 2
  • APP从苹果开发者A账号转移到B账号的流程

    今天把公司的一个APP从苹果开发者A账号转移到B账号 在这里记录具体操作流程 准备好开发者账号A APP所在的原账号 开发者账号B APP迁移目标账号 登录A账号 选择 App Store Connect 点击 Go to App Stor
  • Linux相关关机命令及服务器关机后如何进行开机操作

    linux一般用在服务器上 很少遇到关机的情况 毕竟关机服务就会中断 除非特殊情况不得已才会关闭 正确的关机流程 sync gt shutdown或reboot或halt 无论重启还是关机 都需要先sync将内存数据同步到硬盘中 避免数据丢
  • 抽象数据类型Polynomial 的实现(第二章 P40-43 算法2.22,2.23)

    抽象数据类型Polynomial 的实现 多项式的加法 乘法 typedef int Status Status是函数的类型 其值是函数结果状态代码 如OK等 typedef int Boolean Boolean是布尔类型 其值是TRUE
  • vue关于json数据格式的展示<pre>标签的使用

    起因 需要把字符串按json格式展示到页面上 直接展示或者利用JSON parse 展示页面上数据都会挤成一坨 解决方法 利用
  • 重构——在对象之间搬移特性(1)

    我们都知道 类往往因为承担过多的责任而变得臃肿不堪 这种情况下 一般会使用 提炼类 这种手法将一部分责任分离出去 如果一个类变得 不负责任 一般会使用 内联类 这种手法将它融入另一个类 如果一个类使用了另一个类 一般会运用 隐藏委托关系 手
  • Fast DDS入门六、Fast DDS的动态类型及示例程序

    上一节 Fast DDS入门五 在Windows平台创建一个简单的FastDDS示例程序 该示例程序介绍了采用Fast DDS Gen工具通过接口定义语言 IDL 来生成数据结构类 该数据结构类具备序列化反序列化处理 用户只需要关注编写ID
  • java linkedhashmap list_java – 将所有键从LinkedHashMap提取到列表的方法

    我正在使用许多LinkedHashMap 它们是LinkedHashMap lt Long Long gt LinkedHashMap lt Long Double gt 或LinkedHashMap lt Long Integer gt
  • SGI STL一级二级空间配置器代码剖析笔记

    STL不仅是一个可复用组件库 而且是一个包罗算法与数据结构的软件框架 说到框架本身就有庞大 稳定 完整而可扩展的含义 因此学习STL源码不仅可以帮助我们编写良好代码的习惯 而且能够让我们熟悉算法 数据结构 我们也会受到这种编程思维影响 在编