使用C语言实现智能指针
文章来源:https://linuxcpp.0voice.com/?id=30397
C语言实现std::shared_ptr这个智能指针并且保证线程安全,且使用起来和C++的一样简单高效
在研究指针指针的时候想着用C语言是否能够实现智能指针,找到了这一篇文章,文章中有不完善的地方,本文一并改正
C++中的std::shared_ptr是一个智能指针,可以自动释放其所持有的资源。在C语言中,也可以通过结构体和函数来实现类似的功能,并保证线程安全。
我们可以定义一个结构体SharedResource,用于保存共享资源的信息,包括数据指针和引用计数器:
typedef struct SharedResource {
void* data;
int ref_count;
void (*destructor)(void*);
} SharedResource;
其中destructor函数指针指向资源销毁函数。如果是malloc分配内存,则destructor应该为free;如果使用其他方式分配内存,则相应地修改destructor即可。
接下来定义struct shared_ptr,这个结构体包含对共享资源的引用以及线程安全相关信息:
typedef struct shared_ptr {
SharedResource* resource;
pthread_mutex_t lock;
} shared_ptr;
我们需要编写一些函数来初始化、销毁和操作这两个结构体。首先是初始化函数make_shared()和destroy_shared():
static inline void init_shared_resource(SharedResource* res, void* data, void (*destructor)(void*)) {
res->data = data;
res->ref_count = 1; // 初始化引用计数器为1
res->destructor = destructor;
}
static inline void destroy_shared_resource(SharedResource* res) {
if (res && (--res->ref_count) == 0) { // 如果当前是最后一个引用,则销毁资源
if (res->data && res->destructor != NULL) {
res->destructor(res->data);
}
free(res);
}
}
static inline shared_ptr* make_shared(void* data, void (*destructor)(void*)) {
shared_ptr* ptr = (shared_ptr*)malloc(sizeof(shared_ptr));
if (!ptr) return NULL;
ptr->resource = (SharedResource*)malloc(sizeof(SharedResource));
if (!ptr->resource) {
free(ptr);
return NULL;
}
init_shared_resource(ptr->resource, data, destructor);
pthread_mutex_init(&(ptr->lock), NULL);
return ptr;
}
static inline void destroy_shared(shared_ptr** ptr) {
if (!ptr || !(*ptr)) return;
pthread_mutex_lock(&((*ptr)->lock));
destroy_shared_resource((*ptr)->resource);
pthread_mutex_unlock(&((*ptr)->lock));
free(*ptr);
*ptr = NULL;
}
上述函数中,我们使用了互斥锁来保证线程安全。在destroy_shared()函数中,我们首先加锁以防止其他线程同时访问该智能指针,然后调用destroy_shared_resource()函数销毁共享资源,并最终解锁。
接下来,我们需要编写一些操作指针的函数。例如,增加引用计数器的retain()和获取数据的get():
static inline void retain(shared_ptr* ptr) {
pthread_mutex_lock(&(ptr->lock));
++(ptr->resource->ref_count);
pthread_mutex_unlock(&(ptr->lock));
}
static inline void* get(const shared_ptr* ptr) {
return (NULL == ptr || NULL == ptr->resource) ? NULL : ptr->resource->data;
}
其中,在retain()中,我们首先加锁以防止其他线程同时访问该智能指针,然后将引用计数器加1,并最终解锁。在get()中,我们只需要返回保存在共享资源结构体中的数据指针即可。
我们还可以通过定义一些宏来简化代码:
#define shared_ptr_data(ptr, type) ((type*)get(ptr))
#define make_shared(type, destructor, ...) \
make_shared((type*)calloc(1,sizeof(type)), (void(*)(void*))destructor, ##__VA_ARGS__)
其中shared_ptr_data宏可以方便地获取共享资源的数据指针;make_shared宏可以直接使用类型名和析构函数创建shared_ptr对象。
附上一个完整的测试程序(目前还有小问题):
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define shared_ptr_data(ptr, type) ((type*)get(ptr))
//#define make_shared(type, destructor, ...) make_shared((type*)calloc(1,sizeof(type)), (void(*destructor)(void*)), ##__VA_ARGS__)
typedef struct SharedResource {
void* data;
int ref_count;
void (*destructor)(void*);
} SharedResource;
typedef struct shared_ptr {
SharedResource* resource;
pthread_mutex_t lock;
} shared_ptr;
static inline void init_shared_resource(SharedResource* res, void* data, void (*destructor)(void*)) {
res->data = data;
//引用计数初始化为1
res->ref_count = 1;
res->destructor = destructor;
}
static inline void destroy_shared_resource(SharedResource* res) {
// 如果当前是最后一个引用,则销毁资源
if (res && --res->ref_count == 0) {
if (res->data && res->destructor) {
res->destructor(res->data);
}
free(res);
}
}
static inline shared_ptr* make_shared(void* data, void (*destructor)(void*)) {
shared_ptr* ptr = (shared_ptr*)malloc(sizeof(shared_ptr));
if (!ptr) {
return NULL;
}
ptr->resource = (SharedResource*)malloc(sizeof(SharedResource));
if (!ptr->resource) {
free(ptr);
return NULL;
}
init_shared_resource(ptr->resource, data, destructor);
pthread_mutex_init(ptr->lock, NULL);
return ptr;
}
static inline void destroy_shared(shared_ptr** ptr) {
if (!ptr || !(*ptr))
return;
pthread_mutex_lock(&(*ptr)->lock);
destroy_shared_resource((*ptr)->resource);
pthread_mutex_unlock(&(*ptr)->lock);
free(*ptr);
*ptr = NULL;
}
static inline void retain(shared_ptr* ptr) {
pthread_mutex_lock(&(ptr->lock));
++(ptr->resource->ref_count);
pthread_mutex_unlock(&(ptr->lock));
}
static inline void* get(const shared_ptr* ptr) {
return (NULL == ptr || NULL == ptr->resource) ? NULL : ptr->resource->data;
}
typedef struct test {
int a;
char b;
} test_t;
static void free_test(void* data) {
printf("Freeing data %p\n", data);
free(data);
}
int main() {
test_t tt = {10, 'a'};
int a = 10;
shared_ptr* ptr1 = make_shared(&tt, free_test);
shared_ptr* ptr2 = make_shared(&a, NULL);
retain(ptr1); // 将ptr1引用计数加1
printf("ptr1.res_count: %d\n", ptr1->resource->ref_count);
test_t* t = shared_ptr_data(ptr1, test_t);
if (t) {
t->a = 10;
t->b = 'a';
printf("test.a=%d,test.b=%c\n", t->a, t->b);
}
destroy_shared(&ptr1); // 销毁ptr1所持有的资源,但是由于ptr2也指向同样的资源,所以不会被销毁
destroy_shared(&ptr2); // 销毁ptr2所持有的资源
return 0;
}
在这个示例程序中,我们创建了两个智能指针,并将它们分别指向一个共享资源。然后对其中一个智能指针进行了引用计数加一的操作,并试图销毁其中一个指针所持有的资源。由于另一个智能指针还在使用该资源,因此该资源并没有被销毁。最后,我们通过shared_ptr_data宏获取共享资源的数据指针,并对其进行访问。
总之,通过定义合适的结构体和函数来实现std::shared_ptr类似的功能,在C语言中也可以很方便地实现智能指针机制。同时,通过定义一些宏来简化代码,可以使得C语言实现的智能指针使用起来和C++中的一样简单高效。