《目录》
- 面向过程与面向对象
- C 与 C++ 的区别
- 代码复用
- 分层思想
- 封装、继承、多态
- C语言实现封装
- C语言实现继承
- C语言实现多态
面向过程与面向对象
[理论]
面向过程:OOP,分析问题的解决步骤,而后用 函数 把步骤按顺序一一实现并调用即可。
面向对象:POP,把构成问题的事务分解为各种对象,而建立对象的目的不是为了完成一个一个的步骤,而是描述某个事务在解决整个问题过程中发生的行为。
对于刚刚学完 C 的小白来说,面向对象的思想和思想可谓有点复杂。
如果把 俩个思想 "类比" 到一句话里面,异同就会十分明显。
另外,我习惯英文缩写,这样传递的信息十分简短方便,全称百度。
e.g. 下雨的时候,人们为了防止被雨淋湿打开了雨伞☔️。
面向过程解析:
步骤是:下雨了,打开伞,挡雨,都是动词。
编码实现:fall(),open(),prevent(),这 3 个函数一一对应上行的步骤如 fall() 代表下雨。
面向对象解析:
对象:我 、伞、 雨,都是名词。
编码实现:me,umbrella,rain
对象的行为:我可以打开伞、伞可以挡雨、雨可以下(下雨?️)
再以实际情况安排对象行为的顺序:雨下,我打开伞,伞挡雨。
编码实现:rain.fall(),me.open(),umbrella.prevent()
因此,面向过程是 把问题分解为若干个步骤,每个步骤实现为一个函数,按照顺序实现并在调用时传递数据给函数解答问题。
面向对象是 抽象出问题的各种对象,把数据和解决问题的方法封装在对象中,而后各个对象之间通过行为实现解答问题
练习:试着用面向对象的思想重写下图,
早期的编程语言,如 汇编语言、只有0和1的机器语言,本质上是面向机器编程。
那个时代里,活着的年轻人们重新思考了软件编程的认知。
他们开始认为编程应该以人为本,开发了许多符合人类习惯和逻辑思维的语句,比如:
- 如果......就...... if...else
- 循环 while
- 去哪里 goto
- ......
正是这些使他们摆脱了对计算指令的束缚,使用人类语言代替了机器语言。
正由于这样的语言关注逻辑处理过程,所以也被称为面向过程、面向人的、面向程序员的编程语言,比如 Fortran、C。
高级编程语言的普及极大地释放了程序员的自主性,软件开发迎来黄金时期,程序员的第一个极客时代到来,比尔·盖茨、乔布斯都是在那个时代成长起来的。
但是人的欲望是没有止境的,人能做到的越多,想得到的也就越多,越来越庞大的软件开发计划被不断地提了出来。
但是面向过程的复杂性随着软件规模的膨胀以更快的速度膨胀。
面向过程的软件关注逻辑流程,更容易被设计成面条式程序,长长的过程调用执行,像一根面条。
而大型项目最后由这样一根一根面条组成,就成了一个毛线团,最后谁也理不清了。
于是很多大型软件的开发过程开始失控,最终以失败告终,人们遇到了软件危机。
软件危机使人们开始重新审视软件编程这件事情的本质,除了一部分科学计算或者其他特定目的的软件,大部分的软件是为了解决现实世界的问题,企业的库存管理、银行的账务处理等等。
所以,软件编程的本质是程序员用代码的方式使现实世界的事务运行在计算机上,计算机软件是为了解决现实世界的问题而开发出来的,那么软件编程这件事情应该关注的重点是客观世界的事物本身,而不是程序员的思维方式或者计算机的指令。
如果软件编程的重点是客观世界的事物本身,那么编程语言如何才能更好地满足这一需求?
于是,面向对象的编程语言应运而生。
面向对象编程以对象作为软件编程的基本单位,提出一切皆对象,客观世界的用户、账号、商品是对象;创建、组合、关联这些对象的工厂、适配器、观察者也是对象;将所有这些对象分析、设计、开发出来,一个软件系统就完成了,这个软件系统灵活、强大,最重要的是可以根据需求变化快速更新维护。
面向对象编程似乎已经进化到编程这件事情哲学意义上的终点,是编程语言的终极形态。
现实看起来也确实如此,最近三十年诞生的编程语言几乎全部都是面向对象的编程语言,面向对象一统天下。
但事实真的如此吗?
回望历史我们站在上帝视角,一切都是如此清晰充满条理,凝望未来,我们还能如此笃定吗?
情况也许并非如此。
事实上,现实中的面向对象编程几乎从未实现人们期望中的面向对象编程。
这有人的原因,也有编程语言的原因.......
而随着科技的不断发展,特别是大数据,人工智能以及移动互联网的发展,面向数据的编程需求越来越多,能够更好迎合这一需求的编程模型开始得到青睐,比如函数式编程。
而极客型的程序员对强类型的面向对象编程越来越不感冒,他们希望在编程的时候能够得到更多的自由,编程语言的重心似乎重新出现面向程序员的趋势。
随着计算机性能的不断增强,以及互联网应用对计算资源需求的不断增加,如何更好地利用 CPU 的多核以及分布式集群的多服务器特性,必须是软件编程以及架构设计时需要考虑的重要问题,软件编程越来越多需要考虑机器本身,相对应的,反应式编程得到越来越多的关注。
我们都在时代里,而时代是在螺旋式的上升......
C 与 C++ 的区别
在 C 的基础上,
- C++ 增加了面向对象的机制
- C++ 增加了范型编程的机制
- C++ 增加了异常处理
- C++ 增加了运算符与函数重载
- C++ 增加了标准模板库
- C++ 增加了引用的概念,可以让引用函数参数较方便
- C++ const 的使用更合理了
- 隐式类型转换在 C++ 中不能编译过
- 指针的初始化方式 NULL 改成 nullptr,更合理
面向对象只是一种思想,C 也可以实现,实现的方式也很多。
在 Liunx 内核中便采用了面向对象的思想(封装、继承、多态).....
Liunx内核大概有 3 万多个文件,2000多万行代码。
如果使用面向过程的思想,一个一个函数的去分析肯定不行。
我们会使用面向对象、代码复用、分层的思想去分析大框架、子系统。
代码复用
代码复用:代码可以复用,一般会写成一个函数。如求最大值、输出、排序等。
函数是最小的代码复用:
- 函数 (通用代码段打包)
- 库 (通用函数打包,功能单一)
- 框架 (通用库打包,功能齐全)
- 操作系统
- 计算机系统
分层思想
分层思想应用十分广泛,从函数到计算机系统都有用到。
分层的优势:
- 代码复用
- 软件层次结构清晰,易于维护
- 兼容性好:跨平台
- 扩展性好:功能易于扩展
封装、继承、多态
【封装】:将客观事物抽象为类:属性(几条腿、几只翅膀)和方法(能飞还是能游)
【继承】:子类对象拥有父类对象的属性和方法(继续家产、基因遗传特征相同)
【多态】:一个接口多种实现(覆盖和函数重载)
C语言实现封装
虽然 C 语言没有支持面向对象的机制,但也可以通过很多方法实现面向对象哦。
比较简单的是通过 【结构体 + 函数指针】,虽然结构体内部不能定义函数,但可以借用函数指针。
#include <stdio.h>
// 结构内部体不能定义函数
typedef struct animal{
int age;
int weight;
}Animal;
int main( )
{
Animal a;
a.age = 1;
a.weight = 10;
printf("age = %d, weight = %d\n", a.age, a.weight);
}
【结构体 + 函数指针】:
#include <stdio.h>
// 定义一个函数
void (print) ( ){
puts("模拟类的方法:喵喵喵~\n");
}
typedef struct animal{
int age;
int weight;
void (*fp) ( void ); // 声明一个函数指针 fp,fp是一个野指针,应该使用 typedef 为 fp 初始化
}Animal;
int main( )
{
Animal a;
a.age = 1;
a.weight = 10;
a.fp = print; // 函数名就是函数的地址
printf("age = %d, weight = %d\n", a.age, a.weight);
a.fp( ); // 用函数指针来解引用以调用该函数
}
通过【结构体 + 函数指针】模拟方法;
如果类的方法特别多,那函数指针也会定义很多,我们还可以把函数指针封装起来。
typedef struct functions{
void (*fp1) ( void );
void (*fp2) ( void );
void (*fp3) ( void );
void (*fp4) ( void );
void (*fp5) ( void );
}function;
typedef struct animal{
int age;
int weight;
function f; // 方法被统一封装,function *fp 也可以;
}Animal;
C语言实现继承
#include <stdio.h>
// 定义一个函数
void (print) ( ){
puts("模拟类的方法:喵喵喵~\n");
}
typedef struct animal{
int age;
int weight;
void (*fp) ( void );
}Animal;
typedef struct cat{
Animal a; // 结构体内嵌模拟继承
char sex;
}Cat;
int main( )
{
Animal a;
a.age = 1;
a.weight = 10;
a.fp = print; // 函数名就是函数的地址,a.fp = &print 效果和意义是一样的; 函数名做右值时是一样的,但数组名是不一样的
printf("age = %d, weight = %d\n", a.age, a.weight);
a.fp( ); // 用函数指针来解引用以调用该函数
Cat b;
b.a.age = 2;
b.a.weight = 20;
b.a.fp = print;
b.a.fp();
}
以上通过【结构体内嵌】模拟继承;
除此之外,如果父类(全局变量)已经初始化过,我们不想初始化了就可以使用【结构体指针】模拟继承:
#include <stdio.h>
// 定义一个函数
void (print) ( ){
puts("模拟类的方法:喵喵喵~\n");
}
typedef struct animal{
int age;
int weight;
void (*fp) ( void );
}Animal;
typedef struct cat{
Animal* p; // 结构体指针
char sex;
}Cat;
int main( )
{
Animal a;
a.age = 1;
a.weight = 10;
a.fp = print;
printf("age = %d, weight = %d\n", a.age, a.weight);
a.fp( );
Cat c;
c.p = &a; // 通过一个指向父类
c.p->fp( ); // 通过子类访问父类方法
printf("age = %d, wieght = %d\n", c.p->age, c.p->weight);
}
C语言实现多态
C语言实现多态分为:
- 编译时的多态(重载),存储空间不同
- 运行时的多态(覆盖),允许子类对父类的方法进行改写
重载使用 kmalloc 函数:
kmalloc 的参数是枚举类型
枚举类型可以根据不同的类型,申请不同的区域从而实现编译时的多态:
覆盖使用 回调函数。
标签:fp,函数,weight,void,面向对象,面向,过程,age From: https://blog.51cto.com/u_13937572/6439265