C语言实现面向对象编程 | 干货

前言

        GOF的《设计模式》一书的副标题叫做“可复用面向对象软件的基础”,从标题就能看出面向对象是设计模式基本思想。

由于C语言并不是面向对象的语言,C语言没有直接提供封装、继承、组合、多态等面向对象的功能,但C语言有struct和函数指针。我们可以用struct中的数据和函数指针,以此来模拟对象和类的行为。

所以在正式开始设计模式前,先看看如何用C语言实现面向对象编程。

本章针对面向对象的封装、继承、组合、多态给出C语言的实现方法。

封装

        封装是指对象仅暴露必要的对外接口(这里指public方法)来和其它对象进行交互,其它的属性和行为都无需暴露,这使得对象的内部实现可以自由修改。

这也要求对象包含它能进行操作所需要的所有信息,不必依赖其它对象来完成自己的操作。

以下以电力公司的例子演示封装。

电力公司生产并提供电力。为了汇聚各种发电厂的电力并让用户获得电力,电力公司提供了两个统一接口:

1、电力公司汇聚各种发电厂的电力,无论是火力发电厂、水力发电厂、原子能发电厂等都使用一个接口。如果什么时候一家火力发电厂改造成了风力发电厂,发电厂的实现完全不一样了,但接口不变,所以电力公司感觉不到发电厂变了,不需要为了发电厂实现升级而改造电力公司的系统。

2、电力公司向用户提供电力,无论用户用电的设备是烤面包机还是洗衣机,电力公司和用户之间都使用一个接口(电源插座)。用户的用电设备可以千变万化,但接口(电源插座)不变。所以电力公司不用关系用户的什么设备在用电。

代码:

#include <stdio.h>struct PowerCompany {int powerReserve;void (*PowerPlant)(struct PowerCompany *this, int power);void (*PowerUser)(struct PowerCompany *this, int power);
};void PowerPlant(struct PowerCompany *this, int power)
{ this->powerReserve += power;printf("默认发电厂,发电%d瓦\n", power); return;  
}void PowerUser(struct PowerCompany *this, int power)
{if (this->powerReserve >= power) {printf("用电%d瓦\n", power);this->powerReserve -= power;} else {printf("电力不足,用电失败\n");}return;
}/* struct PowerCompany 的构造函数 */
void PowerCompany(struct PowerCompany *this)
{this->powerReserve = 0;this->PowerPlant = PowerPlant;this->PowerUser = PowerUser;return;
}/* struct PowerCompany 的析构函数 */
void _PowerCompany(struct PowerCompany *this)
{}int main(void)
{struct PowerCompany myPowerCompany;PowerCompany(&myPowerCompany);/* 发电 */myPowerCompany.PowerPlant(&myPowerCompany, 1000);/* 用电 */myPowerCompany.PowerUser(&myPowerCompany, 800);myPowerCompany.PowerUser(&myPowerCompany, 800);_PowerCompany(&myPowerCompany);return 0;
}

从电力公司的例子中可以看出,良好的封装可以有效减少耦合性,封装内部实现可以自由修改,对系统的其它部分没有影响。

继承

        面向对象编程最强大的功能之一就是代码重用,而继承就是实现代码重用的主要手段之一。继承允许一个类继承另一个类的属性和方法。

我们可以通过识别事物之间的共性,通过抽象公共属性和行为来构造父类,而通过继承父类来构造各子类。父类,即公共属性和行为,就得到了复用。

以下哺乳动物的例子演示继承。

猫和狗都是哺乳动物,它们具有公共的属性和行为。比如,猫和狗都有眼睛,且它们都会叫。

我们把眼睛的颜色、会叫抽象出来,作为哺乳动物父类的属性,让猫类、狗类都继承哺乳动物父类,可实现对”眼睛的颜色“、”会叫“实现的复用。

UML:

代码:

#include <stdio.h>struct Mammal {int eyeColor;void (*ShowEyeColor)(struct Mammal *this);int callNum;void (*Call)(struct Mammal *this);
};void ShowEyeColor(struct Mammal *this)
{if (this->eyeColor == 1) {printf("眼睛是绿色\n");} else {    printf("眼睛是蓝色\n");}return;
}void Call(struct Mammal *this)
{printf("叫%d声\n", this->callNum);return;
}// struct Mammal 的构造函数
void Mammal(struct Mammal *this, int eyeColor, int callNum)
{this->eyeColor = eyeColor;    this->ShowEyeColor = ShowEyeColor;  this->callNum = callNum;this->Call = Call;return;  
}struct Dog {struct Mammal mammal;
};// struct Dog 的构造函数
void Dog(struct Dog *this, int eyeColor, int callNum)
{Mammal(this, eyeColor, callNum);// 狗类的其它属性,略return;
}// struct Dog 的析构函数
void  _Dog(struct Dog *this)
{}struct Cat {struct Mammal mammal;// 猫类的其它属性,略
};// struct Cat 的构造函数
void Cat(struct Cat *this, int eyeColor, int callNum)
{Mammal(this, eyeColor, callNum);return;
}// struct Cat 的析构函数
void  _Cat(struct Cat *this)
{}int main(void)
{struct Dog myDog;Dog(&myDog, 1, 3);myDog.mammal.ShowEyeColor(&myDog);myDog.mammal.Call(&myDog);_Dog(&myDog);struct Cat myCat;Cat(&myCat, 2, 5);myCat.mammal.ShowEyeColor(&myCat);myCat.mammal.Call(&myCat);_Cat(&myCat); return 0;
}

多态

        多态与继承是紧耦合的关系,但它通常作为面向对象技术中最强大的优点之一。

子类从继承父类的接口,每个子类是单独的实体,每个子类需要对同一消息有单独的应答。

每个子类对同一消息的应答采用继承的相同接口,但每个子类可以有不同的实现,这就是多态。

在猫和狗的例子中,猫类、狗类都继承了哺乳动物父类的“叫”的方法,但猫类、狗类的叫声并不一样,所以猫类、狗类可以采用不同的“叫”的实现。

以下代码演示了多态。

代码:

#include <stdio.h>struct Mammal {int eyeColor;void (*ShowEyeColor)(struct Mammal *this);int callNum;void (*Call)(struct Mammal *this);
};void ShowEyeColor(struct Mammal *this)
{if (this->eyeColor == 1) {printf("眼睛是绿色\n");} else {    printf("眼睛是蓝色\n");}return;
}void Call(struct Mammal *this)
{printf("叫%d声\n", this->callNum);return;
}/* struct Mammal 的构造函数 */
void Mammal(struct Mammal *this, int eyeColor, int callNum)
{this->eyeColor = eyeColor;    this->ShowEyeColor = ShowEyeColor;  this->callNum = callNum;this->Call = Call;return;  
}struct Dog {struct Mammal mammal;
};void Bark(struct Dog *this)
{int i;for (i = 0; i < this->mammal.callNum; i++) {printf("汪 ");}printf("\n");return;
}/* struct Dog 的构造函数 */
void Dog(struct Dog *this, int eyeColor, int callNum)
{Mammal(this, eyeColor, callNum);this->mammal.Call = Bark;return;
}// struct Dog 的析构函数
void  _Dog(struct Dog *this)
{}struct Cat {struct Mammal mammal;
};void Meow(struct Cat *this)
{int i;for (i = 0; i < this->mammal.callNum; i++) {printf("喵 ");}printf("\n");return;
}/* struct Cat 的构造函数 */
void Cat(struct Cat *this, int eyeColor, int callNum)
{Mammal(this, eyeColor, callNum);this->mammal.Call = Meow;return;
}// struct Cat 的析构函数
void  _Cat(struct Cat *this)
{}int main(void)
{struct Dog myDog;Dog(&myDog, 1, 3);struct Cat myCat;Cat(&myCat, 2, 5);struct Mammal *myMammal;myMammal = &myDog;myMammal->Call(myMammal);myMammal = &myCat;myMammal->Call(myMammal);_Dog(&myDog);_Cat(&myCat);return 0;
}

组合

        组合与继承都是面向对象中代码复用的方式,也只有通过组合和继承两种方式能够实现使用其他类构建新类。

在前面讲的继承关系中,我们把通用属性和行为抽象出来作为父类。

例如:猫、狗都是哺乳动物,它们具有哺乳动物通用的属性和行为。猫、狗与哺乳动物的关系是“is-a”,即猫、狗(is-a)哺乳动物。而组合关系体现的是“has-a”。以房子和窗户的关系举例。

我们可以单独构建窗户类,然后把窗户类应用到各种房子类上。此时房子(has-a)窗户,但绝不是窗户(is-a)房子。

以下UML和代码演示了组合。

UML:

代码

#include <stdio.h>struct Window {int length;int width;void (*ShowWindow)(struct Window *this);
};void ShowWindow(struct Window *this)
{printf("这是长%d厘米、宽%d厘米的窗户\n", this->length, this->width);return;
}void Window(struct Window *this, int length, int width)
{this->length = length;this->width = width;this->ShowWindow = ShowWindow;return;
}void _Window(struct Window *this)
{}struct House {struct Window *window;int livingRoomNum;int bedRoomNum;int bathRoomNum;void (*ShowHouse)(struct House *this);
};void ShowHouse(struct House *this)
{printf("这是%d室%d厅%d卫的房子\n", this->bedRoomNum, this->livingRoomNum, this->bathRoomNum);return;
}void House(struct House *this, int livingRoomNum, int bedRoomNum, int bathRoomNum)
{this->livingRoomNum = livingRoomNum;this->bedRoomNum = bedRoomNum;this->bathRoomNum = bathRoomNum;this->ShowHouse = ShowHouse;return;
}void _House(struct House *this)
{}void main()
{struct House myHouse;House(&myHouse, 2, 3, 2);/* 组合是一种低耦合,如果不初始化,子类只是存放了一个空指针来占位关联。此处是与继承关系的区别。继承是一种强耦合,在继承关系中,无论如何子类拥有父类全部的信息。*/struct Window myWindow1;myHouse.window = &myWindow1;Window(myHouse.window, 100, 50);/* 通过获得其它对象的引用而在“运行时”动态定义 */myHouse.ShowHouse(&myHouse);myHouse.window->ShowWindow(myHouse.window);_Window();_House();return;
}

组合和继承的区别有以下几点:

组合关系体现的是“has-a”。继承关系体现的是“is-a”。

温馨提示

        由于微信公众号近期改变了推送规则,如果您想经常看到我们的文章,可以在每次阅读后,在页面下方点一个「赞」或「在看」,这样每次推送的文章才会第一时间出现在您的订阅列表里。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/163432.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

019-第三代软件开发-Git提交规范

第三代软件开发-Git提交规范 文章目录 第三代软件开发-Git提交规范项目介绍Git提交规范分支规范Commit Message FormatHeaderBodyFooterRevert 总结一下 关键字&#xff1a; Qt、 Qml、 git、 Commit、 release 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个…

【数据结构】优先级队列(堆)

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力…

java最新Springboot3+微服务实战12306高性能售票系统全套开发课程

java最新Springboot3微服务实战12306高性能售票系统全套开发课程 视频课程在文末获取 第1章 课程介绍与学习指南。 1-1 课前必读&#xff08;不读错过一个亿&#xff09; 1-2 课程导学 1-3 为什么要选择最新版本SpringBoot3和JDK17&#xff1f; 1-4 在线demo网站演示 第2…

谈谈 Redis 如何来实现分布式锁

谈谈 Redis 如何来实现分布式锁 基于 setnx 可以实现&#xff0c;但是不是可重入的。 基于 Hash 数据类型 Lua脚本 可以实现可重入的分布式锁。 获取锁的 Lua 脚本&#xff1a; 释放锁的 Lua 脚本&#xff1a; 但是还是存在分布式问题&#xff0c;比如说&#xff0c;一个客…

Java_Jdbc

目录 一.JDBC概述 二.JDBC API 三.ResultSet[结果集] 四.Statement 五.PreparedStatement 六. JDBC API 总结 一.JDBC概述 JDBC 为访问不同的数据库提供了同一的接口&#xff0c;为使用着屏蔽了细节问题Java程序员使用JDBC 可以连接任何提供了 JDBC驱动的数据库系统&am…

Linux考试复习整理

文章目录 Linux考试整理一.选择题1.用户的密码现象放置在哪个文件夹&#xff1f;2.删除文件或目录的命令是&#xff1f;3.显示一个文件最后几行的命令是&#xff1f;4.删除一个用户并同时删除用户的主目录5.Linux配置文件一般放在什么目录&#xff1f;6.某文件的组外成员的权限…

双指针——复写零

一&#xff0c;题目要求 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&#xff0c;不要从函数返回任何东…

Python Connect SQLServer 2008

Macos&#xff08;经过了两天&#xff0c;无数次的方法验证&#xff0c;寻找各种资料&#xff0c;总结如下&#xff09; brew install freetds0.91 如果出现错误就进行手工安装&#xff0c;也可以直接使用 brew install freetds安装最新版本&#xff08;测试通过&#xff09; …

Kerberos认证协议介绍

概述 官网&#xff1a;https://www.kerberos.org/ 官方文档&#xff1a;http://web.mit.edu/kerberos/krb5-current/doc/ 为TCP/IP网络系统设计的可信的第三方身份认证协议。网络上的Keberos服务基于DES对称加密算法&#xff0c;但也可以用其他算法替代。因此&#xff0c;Keb…

CSS 基础知识-01

CSS 基础知识 1.CSS概述2. CSS引入方式3. 选择器4.文字控制属性5. 复合选择器6. CSS 特性7.背景属性8.显示模式9.选择器10.盒子模型 1.CSS概述 2. CSS引入方式 3. 选择器 4.文字控制属性 5. 复合选择器 6. CSS 特性 7.背景属性 8.显示模式 9.选择器 <!DOCTYPE html> <…

(※)力扣刷题-栈和队列-用栈实现队列

使用栈实现队列的下列操作&#xff1a; push(x) – 将一个元素放入队列的尾部。pop() – 从队列首部移除元素。peek() – 返回队列首部的元素。empty() – 返回队列是否为空。 说明: 你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empt…

【Java基础面试三十一】、String a = “abc“; ,说一下这个过程会创建什么,放在哪里?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;String a “abc”; &am…

Java集合类

Java集合类 集合类 集合类其实就是为了更好地组织、管理和操作我们的数据而存在的&#xff0c;包括列表、集合、队列、映射等数据结构。 集合根接口 Java中已经帮我们将常用的集合类型都实现好了&#xff0c;我们只需要直接拿来用就行了 所有的集合类最终都是实现自集合根…

【Javascript保姆级教程】运算符

文章目录 前言一、运算符是什么二、赋值运算符2.1 如何使用赋值运算符2.2 示例代码12.3 示例代码2 三、自增运算符3.1 运算符3.2 示例代码13.3 示例代码2 四、比较运算符4.1 常见的运算符4.2 如何使用4.3 示例代码14.4 示例代码2 五、逻辑运算符逻辑运算符列举 六、运算符优先级…

数据预处理—滑动窗口采样数据

一个简单的例子&#xff1a; # data: 这是要应用滑动窗口采样的输入数据&#xff0c;通常是一个序列&#xff0c;例如列表或NumPy数组。 # window_size: 这是滑动窗口的大小&#xff0c;表示每个窗口中包含的元素数量。 # step_size: 这是滑动窗口移动的步长&#xff0c;表示每…

变化检测数据集制作详细版

本文记录在进行变化检测数据集制作过程中所使用的代码 首先需要准备相同地区不同时间的两幅影像&#xff0c;裁减成合适大小&#xff0c;如256*256。相同区域命名相同放在两个文件夹下。 接着使用labelme对变化区域进行标注&#xff0c;这里不再进行labelme安装及标注的赘述。…

支持语音与视频即时通讯项目杂记(一)

第一部分解释服务端的实现。 &#xff08;服务端结构&#xff09; 下面一个用于实现TCP服务器的代码&#xff0c;包括消息服务器&#xff08;TcpMsgServer&#xff09;和文件中转服务器&#xff08;TcpFileServer&#xff09;。 首先&#xff0c;TcpServer是TcpMsgServer和Tcp…

语音识别whisper的介绍、安装、错误记录

介绍 Whisper是OpenAI于2022年9月份开源的通用的语音识别模型。它是在各种音频的大型数据集上训练的模型&#xff0c;也是一个可以执行多语言语音识别、语音翻译和语言识别的多任务模型。 论文链接&#xff1a;https://arxiv.org/abs/2212.04356 github链接&#xff1a;https:…

手部关键点检测4:Android实现手部关键点检测(手部姿势估计)含源码 可实时检测

目录 1. 前言 2.手部关键点检测(手部姿势估计)方法 (1)Top-Down(自上而下)方法 (2)Bottom-Up(自下而上)方法&#xff1a; 3.手部关键点检测模型训练 4.手部关键点检测模型Android部署 &#xff08;1&#xff09; 将Pytorch模型转换ONNX模型 &#xff08;2&#xff09; …

日常中msvcp71.dll丢失怎样修复?分享5个修复方法

在 Windows 系统中&#xff0c;msvcp71.dll 是一个非常重要的动态链接库文件&#xff0c;它承载了许多应用程序和游戏的运行。如果您的系统中丢失了这个文件&#xff0c;那么您可能会遇到无法打开程序、程序崩溃或出现错误提示等问题。本文将介绍 5 个快速修复 msvcp71.dll 丢失…