博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C 语言数据结构的封装方法
阅读量:4110 次
发布时间:2019-05-25

本文共 3553 字,大约阅读时间需要 11 分钟。

本文是C语言封装设计的第三篇文章,前两篇请见和。

本文介绍C语言中如何封装数据结构,让调用者可以引用这个数据结构,但无法获知这个数据结构的内部构成,也无法读写这个数据结构的成员变量。

解决这个问题,常见的手法是提供一个new_xxx 函数,返回一个数据结构的指针。
比如,数据结构为 dlist_t, 那么在 dlist.h和dlist.c中定义如下:

// dlist.h#ifndef DLIST_H#define DLIST_Htypedef struct dlist_t dlist_t;dlist_t* new_dlist();int dlist_init(dlist_t* plist, uint64_t capacity);int dlist_destroy(dlist_t* plist);int dlist_append(dlist_t* plist, void* pdata);// .....#endif

dlist​.c 中的源码为:

// dlist.c#include "dlist.h"#include 
#include
typedef struct dlist_node_t {    struct dlist_node_t* pprev;    struct dlist_node_t* pnext;    void* pdata;} dlist_node_t;struct dlist_t {   dlist_node_t* phead;   dlist_node_t* ptail;   uint64_t capacity;   // ....};dlist_t* new_dlist() {    dlist_t* plist = malloc(sizeof(dlist_t));    if (!plist) return NULL;    memset(plist, 0, sizeof(dlist_t));    return pplist;}int dlist_init(dlist_t* plist, uint64_t capacity) {    // ...}int dlist_destroy(dlist_t* plist) {    // ...}int dlist_append(dlist_t* plist, void* pdata) {    // ...}// ....

这种方法,能够让调用者使用dlist_t这个数据结构,但无法获知这个数据结构的内部构成,也无法读写这个数据结构的成员变量。

但这种方法的不足在于,这个数据结构不能在栈上分配,并且这个对象只能从堆中分配,无法从调用者的内存池中分配。

 

因此,我们对上面这种方法进行改进,彻底解决这些不足。先探讨一下解决的思路:

要想能够在栈上声明这个数据结构变量,那么这个数据结构必须对调用者完全可见,并且在源码编译过程中就能确定这个对象的尺寸大小。但违背了我们的需求:向调用者隐藏数据结构的内部实现。

因此,要完美解决这些问题,我们需要提供两种数据结构,一种对调用者可见,但没有数据结构的内部实现;另一种对调用者不可见,这是真正的数据结构,有完整的内部实现细节。这两种数据结构的大小完全相同,因此可以相互转化。

 

具体的做法是:对调用者可见的数据结构,是个char数组,数组的大小为真正数据结构的大小。提供对应的宏,封装这些细节。

我们用这个思路,改造一下上面的代码:dlist​.h 中的源码为:

#ifndef DLIST_H#define DLIST_H#include 
#include
typedef struct dlist_t dlist_t;// 在栈上声明一个dlist_t结构,varptr_name 为指向这个结构的指针#define DLIST_VAR(varptr_name) DLIST_VAR2(varptr_name, __LINE__)// 通过malloc 申请一个 dlist_t,arptr_name 为指向这个结构的指针#define DLIST_NEW(varptr_name) dlist_t* varptr_name = (dlist_t*)malloc(DLIST_SIZE);// 在其它struct 中,声明一个dlist_t结构的成员变量#define DLIST_FIELD_DEF(varname) char varname[DLIST_SIZE];// 引用结构中的dlist_t成员,返回dlist_t*的指针#define DLIST_FIELD(full_varname) ((dlist_t*)(full_varname))// for internal use!#define DLIST_SIZE 24#define DLIST_VAR2(varptr_name, n) \        char varptr_name ## n [DLIST_SIZE]; \        dlist_t* varptr_name = (dlist_t*)varptr_name ## n;// public 接口函数int dlist_init(dlist_t* plist, uint64_t capacity);int dlist_destroy(dlist_t* plist);int dlist_append(dlist_t* plist, void* pdata);// .....#endif

 

dlist​.c 中的源码为:

// dlist.c#include "dlist.h"#include 
#include
#include
typedef struct dlist_node_t {        struct dlist_node_t* pprev;        struct dlist_node_t* pnext;        void* pdata;} dlist_node_t;struct dlist_t {   dlist_node_t* phead;   dlist_node_t* ptail;   uint64_t capacity;   // ....};int dlist_init(dlist_t* plist, uint64_t capacity) {        //printf("sizeof(dlist_t) = %d\n", sizeof(dlist_t));        assert(DLIST_SIZE == sizeof(dlist_t));        memset(plist, 0, sizeof(dlist_t));        plist->capacity = capacity;        return 0;}int dlist_destroy(dlist_t* plist) {        return 0;}int dlist_append(dlist_t* plist, void* pdata) {        return 0;}

我们写一个测试程序,测试一下各种使用场景:

// test_dlist.c#include "dlist.h"typedef struct queue_t {        int len;        int cap;        DLIST_FIELD_DEF(list)} queue_t;int main() {        DLIST_VAR(plist)        dlist_init(plist, 1024);        dlist_destroy(plist);        DLIST_NEW(plist2)        dlist_init(plist2, 1024);        free(plist2);        queue_t q;        dlist_init(DLIST_FIELD(q.list), 1024);        return 0;}

通过这种方法,我们就可以实现数据结构的完美封装,调用者可以使用这个数据结构,但不能读写内部成员,并且这个对象可以在栈、堆或用户自己的内存池中分配。

我的微信号 "实力程序员",欢迎大家关注我。

转载地址:http://gvlsi.baihongyu.com/

你可能感兴趣的文章
使用TcpClient可避免HttpWebRequest的常见错误
查看>>
EntityFramework 学习之一 —— 模型概述与环境搭建 .
查看>>
C# 发HTTP请求
查看>>
初试visual studio2012的新型数据库LocalDB
查看>>
启动 LocalDB 和连接到 LocalDB
查看>>
Palindrome Number --回文整数
查看>>
Reverse Integer--反转整数
查看>>
Container With Most Water --装最多水的容器(重)
查看>>
Longest Common Prefix -最长公共前缀
查看>>
Letter Combinations of a Phone Number
查看>>
Single Number II --出现一次的数(重)
查看>>
Valid Parentheses --括号匹配
查看>>
Generate Parentheses--生成匹配括号(重)
查看>>
Remove Element--原地移除重复元素
查看>>
Remove Duplicates from Sorted Array--从有序数组中移除重复元素
查看>>
Count and Say
查看>>
Gas Station
查看>>
Palindrome Partitioning --回文切割 深搜(重重)
查看>>
Valid Palindrome 简单的回文判断
查看>>
Pascal's Triangle -- 生成杨辉三角
查看>>