C语言的面向对象的封装方法

2023-11-19

1.3.1 变长结构体的实现

以上文数据结构C语言——双向链表及其实现的双向环链为例,具体封装方法如下:

在上述双向环链中节点中不可避免地引入了数据指针 void *data占用了8byte的空间,那么能不能在链表每个节点中省去这8byte的空间呢?我们可以使用一个长度为0的字符串指针作为一个不占空间的指示符(数据域的入口地址),在申请节点空间时就需要申请节点结构体空间大小加上头结点中用户设置的size大小的空间,这样每个节点的下方就会附加上一段数据域的空间用于存放数据。

简单示例

struct node_st{
	struct node_st *prev;
    struct node_st *next;
    char data[0];//长度为0的数组仅作为一个地址知识的作用不占用内存空间
};

struct node_st *node = malloc(sizeof(*node) + head->size);//申请节点空间

/*
 * 除此之外还需要改动一些其他操作
 */

改进版本

//llist,h :10 
struct llist_node_st{
    //void *data;
    struct llist_node_st *prev;
    struct llist_node_st *next;
    char data[0];
};

//llist.c :15
    //new->head.data = NULL;

//llist.c :30
    //newnode->data = malloc(ptr->size);

//llist.c :56
    struct llist_node_st *cur = NULL;

    for(cur = ptr->head.next; cur != &ptr->head; cur = cur->next){
        if(cmp(key, cur->data) == 0)
            break;
    }

    return cur;

//llist.c :83
    //free(node->data);

//llist.c :105
    //free(node->data);

//llist.c :124
        //free(cur->data);

/*
 * 其他部分不需要改动
 */

1.3.2 面向对象的封装方法

在封装C的函数时可以采用面向对象的封装方法,将结构体类化,以实现对用户隐藏函数的效果。集体方法如下:

在结构体中定义一组对应方法的函数指针,将需要隐藏的函数方法声明转移到.c文件中并在结构体的初始化过程中添加对应函数指针的赋值操作,用户使用方法时仅需要使用 结构体指针->函数指针(参数表) 的方法即可实现对函数的调用。

llist.h

#ifndef LLIST_H__
#define LLIST_H__

#define LLIST_FORWARD   0x0
#define LLIST_BACKWARD  0x1

typedef void llist_op(const void *);
typedef int llist_cmp(const void *, const void *);

struct llist_node_st{
    //void *data;
    struct llist_node_st *prev;
    struct llist_node_st *next;
    char data[0];
};

typedef struct llist_head{
    int size;
    struct llist_node_st head;

    int (*insert)(struct llist_head *, const void *, unsigned char );
    void *(*find)(struct llist_head *, const void *, llist_cmp *);
    int (*delete)(struct llist_head *, const void *, llist_cmp *);
    int (*fetch)(struct llist_head *, const void *, llist_cmp *, void *);
    void (*travel)(struct llist_head *, llist_op *);

}LLIST;



LLIST *llist_creat(int size);


void llist_destroy(LLIST *);


#endif


llist.c的前几行

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "llist.h"

int llist_insert(LLIST *, const void *data, unsigned char mode);
void *llist_find(LLIST *, const void *key, llist_cmp *);
int llist_delete(LLIST *, const void *key, llist_cmp *);
int llist_fetch(LLIST *, const void *key, llist_cmp *, void *data);
void llist_travel(LLIST *, llist_op *);

LLIST *llist_creat(int size){
    LLIST *new;
    new = malloc(sizeof(*new));
    if(new == NULL){
        return NULL;
    }

    new->size = size;
    //new->head.data = NULL;
    new->head.prev = &new->head;
    new->head.next = &new->head;
    new->insert = llist_insert;
    new->find = llist_find;
    new->delete = llist_delete;
    new->fetch = llist_fetch;
    new->travel = llist_travel;

    return new;
}

main.c 中的函数方法调用

handler = llist_creat(sizeof(SCORE));
handler->insert(handler, &tmp, LLIST_BACKWARD);
handler->travel(handler, print_s);
... ...

1.3.2 隐藏数据类型的封装方法

隐藏数据类型的封装方法是基于可变长结构体的封装基础实现的,与面向对象的封装方法不兼容。

思想:将头文件中的数据结构定义到.c文件中,然后在.h文件中 typedef 一个空类型的的形式化类型,然后在.c文件中所有涉及该数据类型的函数中接受参数后都来一个强转,这样就实现了对数据结构的隐藏。

在 1.3.1的基础上修改文件如下

Makefile、main.c不变

llist.h

#ifndef LLIST_H__
#define LLIST_H__

typedef void LLIST;

#define LLIST_FORWARD   0x0
#define LLIST_BACKWARD  0x1

typedef void llist_op(const void *);
typedef int llist_cmp(const void *, const void *);



LLIST *llist_creat(int size);

int llist_insert(LLIST *, const void *data, unsigned char mode);
void *llist_find(LLIST *, const void *key, llist_cmp *);
int llist_delete(LLIST *, const void *key, llist_cmp *);
int llist_fetch(LLIST *, const void *key, llist_cmp *, void *data);
void llist_travel(LLIST *, llist_op *);

void llist_destroy(LLIST *);


#endif

llist.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "llist.h"

struct llist_node_st{
    //void *data;
    struct llist_node_st *prev;
    struct llist_node_st *next;
    char data[0];
};

struct llist_head_st{
    int size;
    struct llist_node_st head;

};

LLIST *llist_creat(int size){
    struct llist_head_st *new;
    new = malloc(sizeof(*new));
    if(new == NULL){
        return NULL;
    }

    new->size = size;
    //new->head.data = NULL;
    new->head.prev = &new->head;
    new->head.next = &new->head;

    return new;
}

int llist_insert(LLIST *p, const void *data, unsigned char mode){
    struct llist_head_st *ptr = p;
    struct llist_node_st *newnode;
    
    newnode = malloc(sizeof(*newnode) + ptr->size);
    if(newnode == NULL){
        return -1;
    }

    //newnode->data = malloc(ptr->size);
    if(newnode->data == NULL){
        return -2;
    }
    memcpy(newnode->data, data, ptr->size);

    if(mode == LLIST_FORWARD){
        newnode->prev = &ptr->head;
        newnode->next = ptr->head.next;

    }
    else if(mode == LLIST_BACKWARD){
        newnode->prev = ptr->head.prev;
        newnode->next = &ptr->head;
    }
    else{
        return -3;
    }

    newnode->next->prev = newnode;
    newnode->prev->next = newnode;
    
    return 0;
}

static struct llist_node_st *find_(LLIST *p, const void *key, llist_cmp *cmp){
    struct llist_head_st *ptr = p;
    struct llist_node_st *cur = NULL;

    for(cur = ptr->head.next; cur != &ptr->head; cur = cur->next){
        if(cmp(key, cur->data) == 0)
            break;
    }

    return cur;
}


void *llist_find(LLIST *p, const void *key, llist_cmp *cmp){
    struct llist_head_st *ptr = p;
    struct llist_node_st *node;

    node = find_(ptr, key, cmp);

    if(node == &ptr->head){
        return NULL;
    }

    return node->data;
}

int llist_delete(LLIST *p, const void *key, llist_cmp* cmp){
    struct llist_head_st *ptr = p;
    struct llist_node_st *node;
    node = find_(ptr, key, cmp);
    if(node == &ptr->head){
        return -1;
    }

    node->prev->next = node->next;
    node->next->prev = node->prev;

    //free(node->data);
    free(node);
    return 0;

}

int llist_fetch(LLIST *p, const void *key, llist_cmp *cmp, void *data){
    struct llist_head_st *ptr = p;
    struct llist_node_st *node;
    node = find_(ptr, key, cmp);
    if(node == &ptr->head){
        return -1;
    }

    node->prev->next = node->next;
    node->next->prev = node->prev;

    if(node->data != NULL){
        //memset(data, 0, ptr->size);
        memcpy(data, node->data, ptr->size);
        
    }

    //free(node->data);
    free(node);
    return 0;    
}

void llist_travel(LLIST *p, llist_op *op){
    struct llist_head_st *ptr = p;
    struct llist_node_st *cur, *next;

    for(cur = ptr->head.next; cur != &ptr->head; cur = cur->next){
        op(cur->data);
    }

}

void llist_destroy(LLIST *p){
    struct llist_head_st *ptr = p;
    struct llist_node_st *cur, *next;

    for(cur = ptr->head.next; cur != &ptr->head; cur = next){
        next = cur->next;
        //free(cur->data);
        free(cur);
    }

    free(ptr);
    ptr = NULL;

}

结果测试仍然正确有效。

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

C语言的面向对象的封装方法 的相关文章

随机推荐

  • 【宠粉福利】这次我们准备了 iPhone 12、AirPods Pro、罗技鼠标等大礼等你来领!...

    喜迎开学季 C 站开豪礼 最高可开 iphone 12 盲盒开出的不只是一份礼物 更是对于一切美好的期待 拆开一个盲盒 就像开始一场未知的爱丽丝梦游仙境 为 两点一线 朝九晚九 的生活 埋下一刻期待的种子 去收获一份未知的惊喜 这次 价格再
  • CentOS 7 关闭网络限制

    1 安装CentOS 7 3操作系统mini版本即可 2 设置关闭Selinux 编辑 etc selinux config vi etc selinux config SELINUX disabled 重启机器 查看selinux状态 s
  • C++中的namespace

    namespace中文意思是命名空间或者叫名字空间 传统的C 只有一个全局的namespace 但是由于现在的程序的规模越来越大 程序的分工越来越细 全局作用域变得越来越拥挤 每个人都可能使用相同的名字来实现不同的库 于是程序员在合并程序的
  • 手撕计算机网络——应用层(四):P2P

    前言 进入应用层学习也有了一段时间了 接下来的这篇文章中小荔枝会将应用层P2P结构体系于我们客户 端系统体系在分发文件中的机理进行整理 希望今天能结束应用层学习哈哈哈 运输层我来啦 目录 前言 一 P2P的自拓展性 二 BitTorrent
  • 【高德地图API】从零开始学高德JS API(八)——地址解析与逆地址解析

    摘要 无论是百度LBS开放平台 还是高德LBS开放平台 其调用量最高的接口 必然是定位 其次就是地址解析了 又称为地理编码 地址解析 就是将地址转换为经纬度 而逆地址解析 就是将经纬度转换为地址 经纬度一般是由专业测绘机构用GPS采集 然后
  • Shell——脚本执行命令和控制语句

    前言 在正常情况下 shell按顺序执行每一条语句 直至碰到文件尾 if选择结构示例 if后面紧跟判断条件 then后面是执行语句 fi是结束标志 多重if结构示例 case多选结构 通常用于在一系列模式中匹配某个变量的值 命令 只在cas
  • CH7-查找

    文章目录 1 查找的基本概念 2 线性表的查找 2 1 顺序查找 线性查找 算法2 1 0 类型定义 算法2 1 1 顺序查找 算法2 1 2 改进后的顺序查找 性能分析 2 2 折半查找 二分或对分查找 算法2 2 1 非递归算法 算法2
  • 基于容器PaaS云技术平台方案

    本文以容器技术建设 PaaS 平台即服务 云平台的解决方案为例 分析其如何实现系统资源的集中管理 动态分配 监控 共享和调度 如何实现应用的统一部署和业务连续性保障 实现多数据中心的高可用 推动系统架构及流程的调整 应对云计算时代所带来的变
  • 分析研究<<一战到底>>节目规则演变

    分析研究 lt lt 一战到底 gt gt 节目规则演变 一 研究范围 江苏卫视2012年3月2日推出益智答题类节目 研究时间截止 2014年1月4日星期六 二 规则演变 1 初始规则 2012年3月2日规则 1 每期参加节目的有11人 分
  • 01-ZooKeeper快速入门

    1 Zookeeper概念 Zookeeper是Apache Hadoop项目下的一个子项目 是一个树形目录服务 zookeeper翻译过来就是 动物园管理员 它是用来管理Hadoop 大象 Hive 蜜蜂 Pig 小猪 的管理员 简称ZK
  • C语言程序设计 现代方法(第2版)电子书pdf下载

    C语言程序设计 现代方法 第2版 下载链接 https pan baidu com s 1XIKYGAxGhRTscgibAj3kgQ 提取码获取方式 关注下面微信公众号 回复关键字 1129
  • 关于猜数字游戏以及关机指令

    这几天学习到了一些没有接触过的东西 因此在这里记录下 首先是猜数字游戏 这个小程序特别简单 只要知道相关的几个关键函数就能明白 它的主要函数有rand 返回随机数 以及srand 用来设置随机数的起点 以及time 代码如下 include
  • 【QTUM量子链中国区】零撸180元攻略

    QTUM量子链中国区 于2020年1月7日正式上线 实名认证 无需上传 通过后赠送体验矿机一台 周期30天 总产量10QTUM 价值130元 进入官方QQ群可以目测到 这个新出的项目非常火爆 问题是 QTUM量子链中国区和著名的QTUM量子
  • ABAP 参照TR创建副本TR并释放

    简介 一般项目中为了后期传输的统一性 都会采用传输副本请求的方式来避免出现一个需求有过多的工作台TR的情况 但是常规的创建副本请求的方式不是很便捷 因此本文介绍一种参照已有TR创建副本TR的样例 效果 代码 Report YSTMS
  • Ljavax/validation/ParameterNameProvider

    利用宝塔部署项目war包出现 Ljavax validation ParameterNameProvider 的错误 初始化org springframework validation beanvalidation OptionalVali
  • day08-Linux自有服务&软件包管理

    自有服务 即不需要用户独立去安装的软件的服务 而是当系统安装好之后就可以直接使用的服务 内置 学习目标 1 了解systemctl命令用途 2 掌握使用systemctl开启 关闭 重启服务 3 了解常见自有服务ntpd firewalld
  • Linux基础学习

    安装gcc 1 apt get命令是debain Linux发新版的APT软件包管理工具 dabian ubuntu deepin等Linux系统通过以下命令 安装gcc Shell输入sudo apt get install gcc命令
  • 4--一元多项式的乘法与加法运算

    个人题解 include
  • JS判断数组中是否有重复的元素

    function isRepeatId arr arr 100 200 400 200 if arr length 1 若元素数为1一定不重复 直接返回 return false var hash for let i in arr 遍历ar
  • C语言的面向对象的封装方法

    1 3 1 变长结构体的实现 以上文数据结构C语言 双向链表及其实现的双向环链为例 具体封装方法如下 在上述双向环链中节点中不可避免地引入了数据指针 void data占用了8byte的空间 那么能不能在链表每个节点中省去这8byte的空间