【UML+OOPC嵌入式C语言开发】面向对象开发实战开发之书籍与作者信息存储
创始人
2025-05-29 07:24:31

文章目录

  • 论述
  • 代码实现
    • 使用数组实现
    • 使用链表实现
  • 遇到的问题
    • 报错1
    • 报错二

论述

  在某次需求中,如果书籍A有两个属性——名字与编号,当然了每本书籍还会对应一个作者;同时呢,每个作者还会有自己的属性,即名字与作者电话。(这里只是一个假设,实际中书籍与书籍作者不可能只含有这些属性😅)
  这样,那么就会有两种实现方式:

  • 方式一:只设计一个类,类中的属性有:书籍名字、书籍编号、作者名字、作者电话;
  • 方式二:两个类,书籍类含有三个属性:名字、编号、作者类指针,作者类也含有两个属性,即名字与电话,那么这两个类。
      上述的两种方式,其实各有优劣:
  • 当需要存储的每本书籍的作者都不想同时,使用方式一较为方便。从类的操作上来看,类的初始化是需要一次;
  • 当需要存储的书籍的作者同时拥有两本或两本以上的书籍时,使用方式二较为方便,从作者的信息存储来看,使用方式二作者信息不需要多次存储,而使用方式一时需要多次存储作者信息。
      也就是说,这两者的最终结构会如下图:
    在这里插入图片描述
      上面是在讨论类的设计,但实际编程中还需要考虑数据存储的方式,即使用链表存储还是使用使用数组存储。两者的差别不大, 同样的在结构上也各有优劣,使用数组存储时,增加、查找比较方便;而使用链表存储时,删除、插入比较方便。

代码实现

使用数组实现

main.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文件

#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文件

#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

#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

#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

#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源文件

# 声明伪目标
.PHONY:clean# 指定make最终生成的文件
TARGET := target
# 指定依赖文件
OBJ =  book.o $(TARGET) : $(OBJ)gcc -o $@ main.c  $^ # 编译某一个文件  $@表示目标  $^表示依赖
%.o : %.cgcc -c -o $@ $^

  上述两种实现方式均可使用这个makefile文件。
  使用makefile文件的好处是:在某个项目中,如果只有一个文件受到改动,那么编译时就可不必花多余时间编译没有改动过的文件,直接编译已经改动过的文件与主文件即可。(这个现象在一个大工程中更能直观体现,但是在小项目中可以查看编译过程的命令)
&esmp;&esmp;实现上述的优点主要是使用了依赖,也就是说如果某个目标文件的依赖没有发生更新,那么他自己也是不需要更新的。代码体现在:

# 编译某一个文件  $@表示目标  $^表示依赖
%.o : %.cgcc -c -o $@ $^

  符号"%"是一个通配符,也就是能够表达符合这个条件的所有文件,比如:%.o可以表示a.o、b.o、c.o、d.o……

遇到的问题

报错1

报错描述

  小编直接使用vscode+gcc无法运行,会出现报错 undefined reference to `bookList_new’。这个报错小编最终锁定在bookListbookInfo = (bookList)bookList_new();

** 分析及解决方案**

  小编分析,这玩意与编译链接有关(这方面小编不太熟悉)。小编最终是使用 vscode+Makefile+gcc,完成编译操作的,当然了运行也是在终端中完成的。

报错二

问题描述

  当我们在使用类初始化bookListbookInfo = (bookList)bookList_new();时,调用的xxx_new()函数少括号时,编译器是不会发现的,但是实际运行时程序就会卡死。

分析与解决方案

  这里的解决方案比较简单,只需要在xxx_new()函数后加上一个括号即可。

相关内容

热门资讯

玩家必看“阿拉游戏中心其实有透... 您好:阿拉游戏中心这款游戏可以开挂,确实是有挂的,需要软件加微信【4194432】,很多玩家在阿拉游...
科普实测“相约麻将可不可以开挂... 您好:相约麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【69174242】很多玩家在相约麻...
玩家必备“风风跑胡子开挂辅助脚... 您好:风风跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【3716361】很多玩家在这款游...
分享实测“美猴王斗牛透视辅助工... 您好:美猴王斗牛这款游戏可以开挂,确实是有挂的,需要软件加微信【4194432】,很多玩家在美猴王斗...
{我来分享}飞鹰互娱炸金花开挂... 有 亲,根据资深记者爆料飞鹰互娱炸金花是可以开挂的,确实有挂(咨询软件无...