-
@[toc]
论述
在某次需求中,如果书籍A有两个属性------名字与编号,当然了每本书籍还会对应一个作者;同时呢,每个作者还会有自己的属性,即名字与作者电话。(这里只是一个假设,实际中书籍与书籍作者不可能只含有这些属性😅) 这样,那么就会有两种实现方式:
- 方式一:只设计一个类,类中的属性有:书籍名字、书籍编号、作者名字、作者电话;
- 方式二:两个类,书籍类含有三个属性:名字、编号、作者类指针,作者类也含有两个属性,即名字与电话,那么这两个类。 上述的两种方式,其实各有优劣:
- 当需要存储的每本书籍的作者都不想同时,使用方式一较为方便。从类的操作上来看,类的初始化是需要一次;
- 当需要存储的书籍的作者同时拥有两本或两本以上的书籍时,使用方式二较为方便,从作者的信息存储来看,使用方式二作者信息不需要多次存储,而使用方式一时需要多次存储作者信息。 也就是说,这两者的最终结构会如下图: 上面是在讨论类的设计,但实际编程中还需要考虑数据存储的方式,即使用链表存储还是使用使用数组存储。两者的差别不大, 同样的在结构上也各有优劣,使用数组存储时,增加、查找比较方便;而使用链表存储时,删除、插入比较方便。
代码实现
使用数组实现
main.c文件
c#include "stdio.h" #include "book.h" #include "lw_oopc.h" int main(void) { char temp1[10],temp2[10]; author*a[10] ; book*b[10]; for(int i=0;i<10;++i) { // 作者初始化 a[i] = (author*)author_new(); sprintf(temp1,"a%d",i); sprintf(temp2,"t%d",i); a[i]->vInit(a[i],temp1,temp2); // 书籍初始化 b[i] = (book*)book_new(); sprintf(temp1,"bn%d",i); sprintf(temp2,"bt%d",i); b[i]->vSetNumberAndTile(b[i],temp1,temp2); b[i]->vSetAuthor(b[i],a[i]); // 显示 b[i]->vDisplay(b[i]); } return 0; }
book.h文件
c#ifndef _BOOK_H #define _BOOK_H #include "lw_oopc.h" // 作者类 CLASS (author) { void (*vInit) (void*,char*,char*); char* (*vGetName) (void*); char* (*vGetTelNo) (void*); char _name[14]; char _telPhoto[20]; }; // 书籍类 CLASS (book) { void (*vSetNumberAndTile) (void*,char*,char*); void (*vSetAuthor) (void*,author*); void (*vDisplay) (void*); char _bookNo[20]; char _bookTitle[20]; author *_author; }; #endif //_BOOK_H
book.c文件
c#include "book.h" #include "string.h" #include "stdio.h" /************ 作者类 **********/ // 初始化 static void aInit(void*pointer,char*name,char*tel) { author*pthis = (author*)pointer; strcpy(pthis->_name,name); strcpy(pthis->_telPhoto,tel); } // 获取名字 static char* aGetName(void*pointer) { author*pthis = (author*)pointer; return pthis->_name; } // 获取电话 static char*aGetTel(void*pointer) { author*pthis = (author*)pointer; return pthis->_telPhoto; } CTOR(author) FUNCTION_SETTING(vInit,aInit); FUNCTION_SETTING(vGetTelNo,aGetTel); FUNCTION_SETTING(vGetName,aGetName); END_CTOR /********** 书籍类 *********/ // 设置书籍的书籍号与名字 static void bSetNumberAndTile(void*pointer,char*no,char*ti) { book* pthis = (book*)pointer; strcpy(pthis->_bookNo,no); strcpy(pthis->_bookTitle,ti); } // 设置作者 static void bSetAuthor(void*pointer,author*myAuthor) { book*pthis = (book*)pointer; pthis->_author = myAuthor; } // 打印书籍信息 static void bDisplay(void*poniter) { book *pthis = (book*)poniter; author*myAuthor = pthis->_author; printf("bookNumber:%s bookTitle:%s ",pthis->_bookNo,pthis->_bookTitle); printf("authorName:%s authorTel:%s\n",myAuthor->_name,myAuthor->_telPhoto); } CTOR (book) FUNCTION_SETTING(vSetAuthor,bSetAuthor); FUNCTION_SETTING(vSetNumberAndTile,bSetNumberAndTile); FUNCTION_SETTING(vDisplay,bDisplay); END_CTOR
实验结果
使用链表实现
main.c
c#include "stdio.h" #include "book.h" #include "lw_oopc.h" int main(void) { // 初始化链表头结点 bookList*bookInfo = (bookList*)bookList_new(); // 新增节点 listBookNode*b1 = (listBookNode*)listBookNode_new(); b1->vSet(b1,"C language","123123","zhoujie","19891890"); bookInfo->vAdd(bookInfo,b1); listBookNode*b2 = (listBookNode*)listBookNode_new(); b2->vSet(b2,"java language","123456","zhoujie","1989190"); bookInfo->vAdd(bookInfo,b2); listBookNode*b3 = (listBookNode*)listBookNode_new(); b3->vSet(b3,"python language","98765","zhoujie","198990"); bookInfo->vAdd(bookInfo,b3); // 判断节点是否为空 printf("isempty:%d\n",bookInfo->vIsEmty(bookInfo)); // 查找书籍信息 listBookNode*res = bookInfo->vGetInfoByBookName(bookInfo,"java language"); if(res) printf("result1 of finding: %s\n",res->_bookName); else printf("result1:not finding\n"); // 显示书籍信息 bookInfo->vDispaly(bookInfo); // 删除对应名字的书籍 bookInfo->vDeleteBookInfoByName(bookInfo,"java language"); // 再次查找删除对应名字的书籍 res = bookInfo->vGetInfoByBookName(bookInfo,"java language"); if(res) printf("result2 of finding: %s\n",res->_bookName); else printf("result2: not finding\n"); // 显示链表中存储的书籍 bookInfo->vDispaly(bookInfo); return 0; }
book.h
c#ifndef _BOOK_H #define _BOOK_H #include "lw_oopc.h" // 链表相关 CLASS (listBookNode) { char _bookName[15]; char _bookNumber[20]; char _authorName[15]; char _authorTel[15]; listBookNode*next; void (*vInit) (void*); void (*vSet) (void*,char*,char*,char*,char*); }; CLASS (bookList) { void (*vInit) (void*); void (*vAdd) (void*,listBookNode*); int (*vIsEmty) (void*); listBookNode* (*vGetInfoByBookName) (void*,char*); void (*vDispaly) (void*); void (*vDeleteBookInfoByName) (void*,char*); int listCount; listBookNode* next; }; #endif //_BOOK_H
book.c
c#include "stdio.h" #include "book.h" #include "lw_oopc.h" int main(void) { bookList*bookInfo = (bookList*)bookList_new(); listBookNode*b1 = (listBookNode*)listBookNode_new(); b1->vSet(b1,"C language","123123","zhoujie","19891890"); bookInfo->vAdd(bookInfo,b1); listBookNode*b2 = (listBookNode*)listBookNode_new(); b2->vSet(b2,"java language","123456","zhoujie","1989190"); bookInfo->vAdd(bookInfo,b2); listBookNode*b3 = (listBookNode*)listBookNode_new(); b3->vSet(b3,"python language","98765","zhoujie","198990"); bookInfo->vAdd(bookInfo,b3); printf("%d name:%s\n",bookInfo->listCount,bookInfo->next->_bookName); printf("isempty:%d\n",bookInfo->vIsEmty(bookInfo)); listBookNode*res = bookInfo->vGetInfoByBookName(bookInfo,"java language"); if(res) printf("result1 of finding: %s\n",res->_bookName); else printf("result1:not finding\n"); bookInfo->vDispaly(bookInfo); bookInfo->vDeleteBookInfoByName(bookInfo,"java language"); res = bookInfo->vGetInfoByBookName(bookInfo,"java language"); if(res) printf("result2 of finding: %s\n",res->_bookName); else printf("result2: not finding\n"); bookInfo->vDispaly(bookInfo); return 0; }
结果 Makefile源文件
c# 声明伪目标 .PHONY:clean # 指定make最终生成的文件 TARGET := target # 指定依赖文件 OBJ = book.o $(TARGET) : $(OBJ) gcc -o $@ main.c $^ # 编译某一个文件 $@表示目标 $^表示依赖 %.o : %.c gcc -c -o $@ $^
上述两种实现方式均可使用这个makefile文件。 使用makefile文件的好处是:在某个项目中,如果只有一个文件受到改动,那么编译时就可不必花多余时间编译没有改动过的文件,直接编译已经改动过的文件与主文件即可。(这个现象在一个大工程中更能直观体现,但是在小项目中可以查看编译过程的命令) &esmp;&esmp;实现上述的优点主要是使用了依赖,也就是说如果某个目标文件的依赖没有发生更新,那么他自己也是不需要更新的。代码体现在:
c# 编译某一个文件 $@表示目标 $^表示依赖 %.o : %.c gcc -c -o $@ $^
符号"%"是一个通配符,也就是能够表达符合这个条件的所有文件,比如:%.o可以表示a.o、b.o、c.o、d.o......
遇到的问题
报错1
报错描述
小编直接使用vscode+gcc无法运行,会出现报错
undefined reference to `bookList_new'
。这个报错小编最终锁定在bookList
bookInfo = (bookList)bookList_new();
** 分析及解决方案**
小编分析,这玩意与编译链接有关(这方面小编不太熟悉)。小编最终是使用 vscode+Makefile+gcc,完成编译操作的,当然了运行也是在终端中完成的。
报错二
问题描述
当我们在使用类初始化
bookList
bookInfo = (bookList)bookList_new();
时,调用的xxx_new()
函数少括号时,编译器是不会发现的,但是实际运行时程序就会卡死。分析与解决方案
这里的解决方案比较简单,只需要在
xxx_new()
函数后加上一个括号即可。
【UML+OOPC嵌入式C语言开发】面向对象开发实战开发之书籍与作者信息存储
黑心萝卜三条杠2023-09-03 16:10
相关推荐
咕噜Yuki06092 天前
Java基础篇:学会这些技能,程序员职场竞争力UP UPcodeGoogle3 天前
计算机书籍打包Moonbit4 天前
MoonBit 双周报 Vol.59:新增编译器常量支持,改进未使用警告,支持跨包函数导入...多个关键技术持续优化中!神经星星4 天前
【TVM 教程】外部张量函数Moonbit7 天前
MGPIC案例分享|零基础早鸟教程:8小时使用 wasm4 开发井子棋小游戏!邓校长的编程课堂10 天前
少儿编程进入义务教育课程:培养信息科技素养的新政策解读Moonbit18 天前
MoonBit 双周报 Vol.58:原生后端支持、多行字符串插值、json.inspect 功能等多项关键特性取得显著进展!小尤笔记1 个月前
Python操作系统的6个自动化脚本神经星星1 个月前
【TVM 教程】使用 Relay Visualizer 可视化 RelayGoppViper2 个月前
golang学习笔记17——golang使用go-kit框架搭建微服务详解