C语言实现抽象数据类型(ADT)


什么是抽象数据类型(ADT)

C/C++有固有数据类型,比如int,float,double。int a; 就声明且定义出一个int型变量(或者叫数据对象);但光有这些固有的数据类型不能满足编程的可读、可复用、可维护性的要求。比如想要处理一个现实中“学生小明”的对象,如果能有一个名叫“Student”的“学生”类型(好比“int”类型),通过像Student Xiaoming; 这样的语句,就可以定义出一个名为Xiaoming的学生类型对象(就好比“int a;”定义出一个名为a的int整型变量那样),那就好办了。同样的,如果能实现一个名叫“Stack”的栈数据类型,名叫“List”的链表数据类型,名叫“Car”的汽车数据类型,名叫“Plane”的飞机数据类型……,就很方便了。用C++的话说来就是Class Student{……}; Student Xiaoming; Class Stack{……}; Stack my_stack; Class Plane{……}; Plane Boing747; ,如此我们自定义出想要的数据类型,然后使用它。

用面向对象的语言和语法,更容易理解抽象数据类型,就像上面写的那样。“抽象、封装、继承、多态”是面向对象的4个特点,抽象就是把同一数据类型的共有特征概括出来。一个数据对象有四个属性:V(值),A(地址),N(名称),T(类型)。在程序编译完成后,可执行程序里面产生了信息缺失:程序中的数据对象只有地址和值,没有数据类型、数据名称、数据意义等信息了。所以抽象完成在编写程序的时候。抽象得出的数据类型的特点包含两个方面:属性和方法。拿“Student”学生类型来说,姓名,性别,年级,班级,成绩等就是学生类型的“属性”,可以由各种变量表示;努力学习,升学,考试,翘课,挂科,被加分,被表扬,被扣分,被处分等就是学生类型的“方法”,可以由各种函数表示。方法可以对属性进行操作,比如“被加分”会修改“成绩”这个属性。封装就是通过一定的语法形式,把抽象出来的属性和方法“捆绑在一起”,在形式上写成一个整体,使人从形式上就能看出两者的紧密关系,这个过程叫封装。通过封装,还可以将部分属性和方法隐藏起来,对外只留一定的接口(函数),实现“信息隐藏”。封装可以理解为抽象的具体表现形式。

C语言实现抽象数据类型不如面向对象语言来的方便,不过抽象和封装还是能实现的。


C语言通过怎样的方式来实现ADT

C语言用不完整类型作为唯一的封装工具。不完整类型的描述是:描述了对象但缺少定义对象大小所需的信息。

例如声明 struct t; //声明不完整类型t。

这告诉编译器t是一个结构标记,但并没有描述结构的成员,所以,编译器没有足够的信息去确定该结构的大小,sizeof不能用在不完整类型上,也不能用它来声明变量:

 struct t s;(这是错误的)。

但是可以定义一个指针类型指向不完整类型:

typedef struct t *T;

这个类型定义表明,类型T 的变量是指向标记为t的结构的指针。现在可以声明类型T的变量,将其作为函数的参数进行传递,并可以执行其他合法的指针运算(指针的大小并不依赖于它指向的对象,这就解释了为什么C语言允许这种行为)。不完整类型的信息将在程序的其他地方补充完整。


举例

举学生数据类型的例子说明利用不完整类型实现抽象数据类型:实现抽象数据类型,形式上还是用头文件,源文件的方式,头文件是供其他人看的,供其他模块其他客户调用的“库”、“模块对外接口”,在头文件里定义学生抽象数据类型,并给出代表学生类型“方法”(升学,考试,翘课,挂科……)的函数的原型:

studentADT.h

#ifndef STUDENTADT_H
#define STUDENTADT_H

struct Student; //声明不完整类型Student
typedef struct Student * PStudent;//定义学生抽象数据类型,客户用PStudent而不是Student来定义ADT

PStudent create(char * name, char * school, int id, int age );
void destroy(PStudent s);
void JiaFen(PStudent s, int score);  //被加分
void KouFen(PStudent s, int score);  //被扣分
void ShengXue(PStudent s, char * school);  //升学,可能升到其他学校去了
void ShowInfo(PStudent s);  //显示ADT变量的具体信息
//……可能还有其他函数实现各种学生类型的“方法”

#endif

声明的struct这个结构体里具体有哪些变量,也就是学生类型具体细节(比如学生成绩既有可能是用数组存储的,也有可能是用链表存储的),都不在这里体现,在这里隐藏起来。在源文件中再具体实现。客户不能访问struct结构的成员。PStudent才是为客户定义的,供客户使用的学生抽象数据类型(而不是Student类型),包含此头文件的客户可以使用PStudent类型的变量,调用这个头文件中的函数。
抽象数据类型需要create和destroy这样的函数。int a;//内置类型的声明,直接它自己就分配出一个int型的空间。,而抽象数据类型是自定义出来的,需要我们自己手动分配和释放空间。
客户可以这样使用学生ADT:

PStudent Xiaoming;
Xiaoming = cteate("Xiaoming", "Peking University", 20200001, 18);
//定义出一个Xiaoming的学生ADT变量,并做初始化:他的名字叫“Xiaoming”,学校是北京大学,学号20200001,年龄18岁。

JiaFen(Xiaoming, 2);//小明被加分了,加了2分。
KouFen(Xiaoming, 1);//小明被扣分了,扣了1分。
ShengXue(Xiaoming, "TsingHua University");//小明升学去了清华。
destroy(Xiaoming);//不再使用Xiaoming变量后,释放动态分配的内存,以免内存泄漏

studentADT.c可能像这样实现:

#include <stdio.h>
#include <stdlib.h>
#include "studentADT.h"

struct Student {
    char name[20];
    char school[30];
    int id;
    int age;
    int score;
};

PStudent create(char * name, char * school, int id, int age ) {
    PStudent s = malloc(sizeof(struct Student));
    strcpy(s->name, name);
    strcpy(s->school, school);
    s->id = id;
    s->age = age;
    s->score = 0;
    return s;
}

void destroy(PStudent s) { free(s); }

void JiaFen(PStudent s, int score) { s->score += score; }

void KouFen(PStudent s, int score) { s->score -= score; }

void ShengXue(PStudent s, char * school) {
   if (s->score >= 100)
       strcpy(s->school, school);// 分数够了就可以升学
}

void ShowInfo(PStudent s){
    printf("%s, %s, %d, %d, %d\n", s->name, s->school, s->id, s->age, s->score);
}

参考资料

《C语言程序设计:现代方法》K.N.King; 《新标准C++程序设计教程》郭炜;《基于Linux的C++》MOOC课程,乔林

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值