使用C语言实现智能指针

使用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++中的一样简单高效。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值