C 语言面向对象思想

article/2025/11/11 11:09:10

注:本博客参考了很多大神的文章加上自己的理解,只做学习复习用

一. C语言面向对象设计引入

1.我们为什么要学习面向对象思想???

在我们平时的开发中也经常会遇到一些简单的项目我们很快就完成了,并且测试着也没有任何问题,但是随着产品迭代,需求增加,项目维护起来越来越耗时,一个看似简单的需求,需要花费较多的时间去开发,出现项目延期。 

项目超期的很大一部分原因都是因为代码“牵一发动全身”,当我们的项目需求在累加的过程中,功能相近的应用我们也习惯性的复制,粘贴。导致出现了很多的冗余代码,同时业务逻辑与执行操作相互交织,出现了很多奇奇怪怪的问题。

 

那么我们该怎么办呢??

可能我们会提到“高内聚,低耦合

 

那么什么是高内聚,低耦合??

  • 内聚指的是模块内部的功能,最强的内聚就是功能单一到不能拆分,也就是原子化,
  • 耦合指的模块之间的关系,最弱的耦合设计是通过一个主控模块来协调n个模块之间的运作。

强内聚和弱耦合是相辅相成的,一个良好的设计是由若干个强内聚模块以弱耦合的方式组装起来的

 

说起来好像很高大上,不好理解,其实在很多优秀的开源代码中,都体现出了这种思想。

rt-thread,linux,lwip协议栈,包括我们常用的HAL库等

我们以linux的平台总线模型为例:

  •  bus :总线对象,描述一个总线,管理device和driver,完成匹配
  • device对象(静态):设备对象,描述设备信息,包括地址,中断号,甚至其他自定义的数据
  • driver对象(动态):描述设备驱动的方法(代码逻辑)

可共用的部分(代码逻辑)与差异化的东西(硬件地址)分离。

 

当然我们平时开发的程序还没有那么庞大,还不用那么复杂,那么想实现类似的结构,基本上都使用了面向对象的思想。面向对象思想的引入可以帮我们有效的避免一些问题。(是部分欧),为了更高质量的代码,后期说一下c语言的设计模式。

 

 

2.认识C语言

C 语言是一门抽象的面向过程的语言,C 语言广泛应用于底层开发,C 语言在计算机体系中占据着不可替代的作用,可以说 C 语言是编程的基础

 

上面说C语言是面向过程的,而C++是面向对象的,然而何为面向对象,什么又是面向过程呢?

  • 面向过程,我认为过程就是步骤,是解决问题的按部就班。
  • 面向对象就是构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

 

不管怎么样,我们最原始的目标只有一个就是实现我们所需要的功能,从这一点说它们是殊途同归的。过程与对象只是侧重点不同而已。

准确的说,面向对象是一种思想,和语言本身无关。

面向过程的方法写出来的程序是一份蛋炒饭,而用面向对象写出来的程序是一份盖浇饭

盖浇饭的好处就是”菜”“饭”分离,从而提高了制作盖浇饭的灵活性。饭不满意就换饭,菜不满意换菜。用软件工程的专业术语就是”可维护性“比较好,”饭” 和”菜”的耦合度比较低。

蛋炒饭将”蛋”“饭”搅和在一起,想换”蛋”“饭”中任何一种都很困难,耦合度很高,以至于”可维护性”比较差。

当然面向过程的性能要比面向对象高,对象需要实例化,开销比较大。炒饭还是盖饭,适合的才是最好的。

随着软件需求的变化,我们的代码不断的增加,面向对象的方法,无疑更加容易扩展,及管理维护。

 

 

二. c语言面向对象概念与实现

我们知道面向对象的思想主要应用在C++,JAVA等高级语言,它有几个重要的特性。

封装,抽象,继承,多态。

1.封装与抽象

1.封装与抽象

 

封装性是面向对象编程的三大特性(封装性、继承性、多态性)之一,但也是最重要的特性。封装+抽象相结合就可以对外提供一个低耦合的模块。

数据封装是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。

在C语言中,数据封装可以从结构体入手,结构体里可以放数据成员和操作数据的函数指针成员。当然,结构体里也可以只包含着要操作的数据。

下面以一个简单的实例作为演示。

设计一个软件模块,模块中要操作的对象是长方形,需要对外提供的接口有:

1、创建长方形对象;

2、设置长、宽;

3、获取长方形面积;

4、打印长方形的信息(长、宽、高);

5、删除长方形对象。

首先,我们思考一下,我们的接口命名大概是怎样的?其实这是有规律可循的,我们看RT-Thread的面向对象接口是怎么设计的:

 

我们也模仿这样子的命名形式来给我们这个demo的几个接口命名:

1、rect_create
2、rect_set
3、rect_getArea
4、rect_display
5、rect_delete

我们建立一个rect.h的头文件,在这里声明我们对外提供的几个接口。这时候我们头文件可以设计为:

 

这样做是没有什么问题的。可是数据隐藏得不够好,我们提供给外部用的东西要尽量简单。

我们可以思考一下,对于C语言的文件操作,C语言库给我们提供怎么样的文件操作接口?如:

 

FILE *fopen(const char *pathname, const char *mode);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

 

我们会创建一个文件句柄(描述符),然后之后只要操作这个文件句柄就可以,我们不用关心FILE具体是怎么实现的。

什么是句柄?看一下百度百科的解释:

 

我们也可以创建我们的对象句柄,对外提供的头文件中只需暴露我们的对象句柄,不用暴露具体的实现。以上头文件rect.h代码可以修改为:

 

这里用到了void*其为无类型指针,void *可以指向任何类型的数据。然后具体要操作怎么样的结构体可以在.c中实现:

 

在基于对象的编程中,封装性是最基础也最重要的内容。其对象主要包含两方面内容:属性方法

在基于C语言的对象编程中,可以使用句柄来表示对象,即句柄指向的数据结构的成员代表对象的属性,实际操作句柄的函数则表示对象的方法。

 

2.继承

继承简单说来就是父亲有的东西,孩子可以继承过来。

当创建一个类时,我们不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。

这个已有的类称为基类,新建的类称为派生类

继承在C++ 中还会细分为很多,我们就不考虑那么多了,只分享比较简单也比较实用的。

在C语言对象编程中,有两种方法实现继承:

第一种是:结构体包含结构体实现继承。

第二种是:利用私有指针实现继承。

下面依旧以实例进行分享:

 

结构体包含结构体

假如我们要操作的对象变为长方体,长方体就可以继承长方形的数据成员和函数,这样就可以复用之前的一些代码。具体操作看代码:

  1. 结构体

可见,长方体结构体可以继承长方形结构体的数据、长方体对象相关操作也可以继承长方形对象的相关操作。这样可以就可以复关于长方形对象操作的一些代码,提高了代码复用率。

 

利用私有指针实现继承

利用私有指针实现继承

在结构体内部增加一个私有指针成员,这个私有成员可以达到扩展属性的作用,比如以上的Rect结构体设计为:

typedef struct _Rect
{
 char *object_name;
 int length;
 int width;
 voidprivate
}Rect, *pRect;

这个private指针可以在创建对象的时候与其它拓展属性做绑定。比如:

想要拓展的数据为:

 

带拓展属性的对象创建函数:

 

显然,使用私有指针也是可以实现继承的一种方式。

 

3.多态

3.多态

多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。

多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

 

C++中常使用虚函数实现多态。先定义一个弱的/虚的函数,其它地方再定义同名的真的函数,实际用的是真的函数。

 

首先,我们可以使用函数指针来模拟C++的虚函数表:

* 模拟C++的虚函数表 */
typedef struct _Ops
{int (*area)(void);
}Ops;/* 基类 */  
typedef struct _Shape 
{Ops ops;int width;int height;
}Shape;/* 真实函数*/
int rectangle_area(void)
{printf("Rectangle class area\n");
}/* Triangle的area函数 */
int triangle_area(void)
{printf("Triangle class area\n");
}/* 主函数 */
int main(void)
{Rectangle rectangle;memset(&rectangle, 0, sizeof(Rectangle));rectangle.shape.ops.area = rectangle_area; /* 与自己的area函数做绑定 */Triangle triangle;memset(&triangle, 0, sizeof(Triangle));triangle.shape.ops.area = triangle_area; /* 与自己的area函数做绑定 */Shape *shape;shape = (Shape*)&rectangle;shape->ops.area();shape = (Shape*)▵shape->ops.area();return 0;
}

 

父类指针shape来操作两个子类时,使用相同的接口时调用了不同的函数:

 

因为这里只有一个操作函数,所以就没有建立一个函数表来包装一层了。我们可以再加一个函数表,如:

 

C语言并不是面向对象的语言,要想完全实现与C++一样的一些面向对象的特性会比较难,但是在嵌入式开发过程中,C语言又应用广泛,而在大型项目中,一个好的软件框架可以帮助我们更有效的开发,所以面对对象的思想就显得极其重要了。

 

 

三. 面向对象在我们实际中的应用

上节说了一些基本的概念和举例,但是还是不好理解??

其实我们在平时开发中也遇到很多这种应用。

 

1.LCD菜单架构

 

面向过程

面向对象:

 

2.linux,rt-thread IO设备驱动模型:

封装的体现:

 

 


http://chatgpt.dhexx.cn/article/9EOvFtsH.shtml

相关文章

软件测试的历史

软件测试的历史 -概述1软件测试的发展2软件测试面临的挑战 -国内外现状1发达国家软件测试的体现2我国软件测试的发展 -软件测试与软件项目的关系1软件测试和软件项目的关系2软件质量风险的表现 -软件测试的发展趋势-第三方测试 -概述 只要坚持学习,路永远就在脚下&a…

【软件工程】软件工程需求分析——面向对象分析

halo~我是bay_Tong桐小白 本文内容是桐小白个人对所学知识进行的总结和分享,知识点会不定期进行编辑更新和完善,了解最近更新内容可参看更新日志,欢迎各位大神留言、指点 软件工程需求分析——面向对象分析 【更新日志】 面向对象分析概述建立…

[软件工程] 面向对象设计

面向对象设计 面向对象设计一、 面向对象设计的准则(一) 模块化(二) 抽象(三) 信息隐藏(四) 弱耦合对象之间的耦合可分为两大类:(1) 交互耦合(2) 继承耦合 (五…

软件集成测试策略有哪些,面向对象的集成测试与传统的软件集成测试有什么不同?分别有哪些策略?...

满意答案 jijg你 2014.01.23 采纳率:56% 等级:9 已帮助:568人 提到面向对象的集成测试,就首先要搞清楚面向对象的集成有什么特征,是与传统软件集成有什么区别。 面向对象软件的三个主要特征是:封装、继承和多态。封…

Java面向对象浅谈

作者简介 作者名:编程界明世隐 简介:CSDN博客专家,从事软件开发多年,精通Java、JavaScript,博主也是从零开始一步步把学习成长、深知学习和积累的重要性,喜欢跟广大ADC一起打野升级,欢迎您关注&…

面向对象测试与传统软件测试有何异同,面向对象软件测试与传统测试有何区别...

由于面向对象技术具有封装、继承、多态等新的特性,带来了传统程序设计所不存在的错误,导致原来行之有效的软件测试技术对面向对象技术开发的软件有些力不从心。 面向对象软件测试与传统软件测试有以下不同: 测试关注的对象不同 传统测试的对象…

面向对象软件测试

面向对象技术涉及到的核心概念 1、对象:是一种对特定的数据以及业务逻辑代码继续封装的可操作实体。2、消息:某些对象请求执行操作的一个请求就是消息,消息机制是面向对象应用的关键。3、接口:一组行为或操作的集合,它…

JavaScript获取完整当前域名

window.location.protocol"//"window.location.host; // 返回https://mp.csdn.netwindow.location.host; //返回url 的主机部分,例如:mp.csdn.net window.location.hostname; //返回mp.csdn.net window.location.href; //返回整个url字符串…

php获取当前页面链接_php怎么获取当前完整url地址

对于PHP学习者来说,应该都知道$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。显然PHP获取当前页面的完整url地址,主要就用到$_SERVER这个系统变量。 下面我们就通过具体的示例给大家介绍PHP获取…

php获取域名方法,PHP实现获取域名的方法有哪些?

PHP实现获取域名的方法有哪些?这篇文章主要介绍了PHP实现获取域名的方法。使用php获取域名的方法很多,有用变量、正则、有字符接取方面的函数的都可以,但云服务器网(yuntue.com)小编今天就过来分享的是很简单的四种方法,当然也是比较实用的,也是效率比较高的;实例总结了常…

php 获取当前域名

当前url&#xff1a;http://localhost:805/test/helloworld.php echo SERVER_NAME&#xff1a;.$_SERVER[SERVER_NAME]; //获取当前域名&#xff08;不含端口号&#xff09; echo <p>;echo HTTP_HOST&#xff1a;.$_SERVER[HTTP_HOST];//获取当前域名 &#xff08;含端…

Element ui table表格内容超出隐藏显示省略号

element ui官方文档上面有个参数 :show-overflow-tooltip"true" 给el-table-column设置:show-overflow-tooltip"true"属性可以使单元格超出的内容被隐藏显示为...当鼠标移入时单元格的上方会弹出一个tooltip来显示单元格的所有内容。 <el-table v-loa…

element Descriptions 内容超出隐藏 Tooltip展示完整内容

使用 el-descriptions 时&#xff0c;可能会出现内容过长导致换行的情况&#xff0c;如图 这样会导致整体很不美观&#xff0c;所以封装了一个组件来解决此问题 效果图 当内容过长时会隐藏超出部分&#xff0c;鼠标移上去Tooltip展示全部内容&#xff0c;内容未超出的&#xf…

文字超出隐藏

1.单行文字 <html><head><meta charset"utf-8"><style>#row{display: block;/* 必备的四个条件 */width:100px;/*宽度 */overflow: hidden;/*超出隐藏*/white-space: nowrap;/* 强制不换行 */text-overflow:ellipsis;/*文字隐藏的格式 */}&l…

溢出超出DIV边框的内容自动隐藏方法

CSS样式实现溢出超出DIV边框宽度高度的内容自动隐藏方法 平时我们布局时候&#xff0c;有的文字内容多了会超过溢出我们限制的高度&#xff0c;有的图片会撑破DIV&#xff0c;让网页错位变乱。 这样我们就需要解决如何使用CSS来超出设置CSS宽度和CSS高度的内容自动隐藏掉&…

css单行/多行文本溢出隐藏 /超出隐藏

1、单行文本 - 溢出隐藏 div {width: 100px; &#xff08;固定宽度&#xff09;overflow:hidden; //超出的文本隐藏text-overflow:ellipsis; //用省略号显示white-space:nowrap; //不换行&#xff08;文字不允许换行&#xff0c;单行文本&#xff09; } 实现效果&a…

数字图像处理——图像质量评价(Python)

简单的按照评价指标公式&#xff0c;计算出待评价图像与标准图像之间的评价指标值&#xff0c;评价公式如下图&#xff1a; M和N表示图像的尺寸&#xff0c;L表示图像的灰度取值范围&#xff0c;对于8bit的灰度图像而言取L255 实验数据如下所示&#xff1a; 标准图像 待评价…

图像质量评价matlab实现(含代码)

这篇是上两篇的一个延续吧&#xff0c;对偏振HSI伪彩色图像融合增强效果的一个评价 代码里面包含了很多种评价方式&#xff0c;附带一个评价说明&#xff0c;链接如下&#xff1a; 图像质量评价指标&#xff08;全&#xff09;&#xff0c;可结合blog-机器学习文档类资源-CSD…

基于Python的无参考图像质量评价

源码下载地址&#xff1a;https://download.csdn.net/download/sheziqiong/85602575 源码下载地址&#xff1a;https://download.csdn.net/download/sheziqiong/85602575 无参考图像质量评价 简介 参考RankIQA中的方法&#xff0c;将回归问题转化为分类回归的 问题&#xff…

图像质量评价方法综述

图像质量评价综述 1、背景介绍 图像是人类感知和机器模式识别的重要信息源&#xff0c;其质量对所获取信息的充分性和准确性起着决定性的作用。然而&#xff0c;图像在获取、压缩、处理、传输、显示等过程中难免会出现一定程度的失真。如何衡量图像的质量、评定图像是否满足某种…