libcsptr源码解析
libcsptr是一个使用c语言实现的简单的智能指针库
github仓库地址是:[libcsptr]https://github.com/proficientc/libcsptr
数据结构
libcsptr中的智能指针的数据结构分布大致如图,从图中可以看出,每个智能指针都保存有元数据,其中包含引用计算以及析构函数等信息
元数据
libcsptr中的ptr智能指针都有一个元数据的结构体,对于unique_ptr来说,这个结构体是s_meta。对于shared_ptr来说,这个结构体是s_meta_shared
这两个结构体的组成是:
s_meta -> pointer_kind (指针类型,数据类型:enum)
f_destructor (析构函数,类型:void*(void* , void*))
void * ptr (有效数据起始地址)
s_meta_shared -> pointer_kind (enum)
f_destructor (析构函数,类型:void*(void* , void*))
void * ptr (有效数据起始地址)
volatile size_t ref_count (引用计数)
指针参数
libcsptr在开辟智能指针时需要一个参数结构体,s_malloc_args
typedef struct {
CSPTR_SENTINEL_DEC
size_t size; // 有效数据大小
size_t nmemb; // 成员个数,用于判断是否是数组
enum pointer_kind kind; // 智能指针类型
f_destructor dtor; // 析构函数指针
struct {
const void *data; // meta中的data地址
size_t size; // meta中的data的大小
} meta;
} s_smalloc_args;
s_malloc_args 用在函数s_malloc_impl中,下面是s_malloc_impl的注释
CSPTR_MALLOC_API
static void *smalloc_impl(s_smalloc_args *args) {
if (!args->size)
return NULL;
// align the sizes to the size of a word
size_t aligned_metasize = align(args->meta.size); //meta.size
size_t size = align(args->size); //args->size 有效数据大小
size_t head_size = args->kind & SHARED ? sizeof (s_meta_shared) : sizeof (s_meta); // s_meta结构体大小
s_meta_shared *ptr = alloc_entry(head_size, size, aligned_metasize); // s_meta结构体大小 + meta data的大小 + 有效数据的大小 + 存放s_meta结构体大小与meta data的大小之和的数据大小
if (ptr == NULL)
return NULL;
char *shifted = (char *) ptr + head_size; // meta data数据起始地址
if (args->meta.size && args->meta.data)
memcpy(shifted, args->meta.data, args->meta.size);
size_t *sz = (size_t *) (shifted + aligned_metasize); //sz 指向存放放s_meta结构体大小与meta data的大小之和的数据的位置
*sz = head_size + aligned_metasize;
*(s_meta*) ptr = (s_meta) {
.kind = args->kind,
.dtor = args->dtor,
#ifndef NDEBUG
.ptr = sz + 1 // sz再加1就是有效数据起始地址
#endif
};
if (args->kind & SHARED)
ptr->ref_count = 1;
return sz + 1;
}
meta data的作用
从dealloc_entry函数大概可以看出,meta data中的数据最后会被dtor析构函数释放
CSPTR_INLINE static void dealloc_entry(s_meta *meta, void *ptr) {
if (meta->dtor) {
void *user_meta = get_smart_ptr_meta(ptr);
if (meta->kind & ARRAY) {
s_meta_array *arr_meta = (void *) (meta + 1);
for (size_t i = 0; i < arr_meta->nmemb; ++i)
meta->dtor((char *) ptr + arr_meta->size * i, user_meta);
} else
meta->dtor(ptr, user_meta); //user_meta 指向meta data起始地址
}
#ifdef SMALLOC_FIXED_ALLOCATOR
free(meta);
#else /* !SMALLOC_FIXED_ALLOCATOR */
smalloc_allocator.dealloc(meta);
#endif /* !SMALLOC_FIXED_ALLOCATOR */
}
可以看个例子:
check/test/shared.c中的dtor其实只是个对dtor_run赋下值的函数,也就是说对于一般最简单的shared_ptr来说,meta data没什么用
START_TEST (test_shared_sref) {
int dtor_run = 0;
f_destructor dtor = lambda(void, (UNUSED void *ptr, UNUSED void *meta) { dtor_run = 1; });
smart void *ptr = shared_ptr(int, 42, dtor);
assert_valid_ptr(ptr);
{
smart void *ptr2 = sref(ptr);
ck_assert_msg(ptr == ptr2, "Expected reference to be the same pointer.");
}
ck_assert_msg(dtor_run == 0, "Expected destructor NOT to have run.");
} END_TEST