OOPC精要——你的对象无处不在

1、面向对象编程

面向对象只是一种设计方法,是一种概念,不要和编程语言混为一谈。

2、什么是类和对象

面向对象思想的核心是把数据和处理数据的方法封装在一起。
首先这里的封装,不是指放在同一个结构体里这种形式,只要逻辑上在一起就算在一起,比如放在同一个接口头文件里(也就是.h),也是一种形式——即,一个接口头文件提供了数据的结构体,以及处理这些数据的方法(函数原型声明),这已经完成了面向对象所需的最基本要求。

在数据结构里,类,其实属于 ADT(Abstract Data Type), 类型 = 大小(对齐信息) + 操作集合; 变量(对象 )= 地址值 + 类型信息;
类是用户定义的一种数据类型。类之于对象就像类型之于变量。也就是说类定义描述的数据格式及其用法,而对象则是根据数据格式规范创建的实体。

3、从面向对象的角度,来重新理解变量:

一个变量(Variable),使用面向对象(OO)的概念,我们统一称为对象(Object),除了保存于其中的内容以外,只有三个要素:

  • 由一定宽度无符号整数(Unsigned Integer)所表示的地址“数值”(Address Value)
  • 对象的大小(Size)和对齐
  • 可对该对象适用的“方法”(Method)和“运算”(Operation)
    其中,我们习惯于把后两者合并在一起称之为,变量的"类型"。

地址数值(Address Value)
  地址的数值是一个无符号整数,其位宽由CPU的地址总线宽度所决定。话虽如此,其实主要还是编译器在权衡了“用户编写代码的便利性”以及“生成机器码的效率”后为我们提供的解决方案:例如,针对8位机,编译器普遍以等效为uint16_t的整数来保存地址信息;针对16位机和32位机,编译器则普遍选择uint32_t的整数来保存地址信息;针对64位机,编译器则可能会提供两种指针类型,分别用来对应uint32_t的4G地址空间和由uint64_t所代表的恐怖地址空间……总而言之,地址的数值是一个无符号整数。
大小(Size)和对齐
如果仅从变量的大小来看整个计算机世界,就好像一副彩色图片被二值化了,到处是Memory Block,他们的尺寸通常是1个字节、2个字节、4个字节、8个字节、16个字节或者由他们组合而成的长度各异Block。这些Block通常被编译器在代码生成的时候对其到地址的宽度上,比如地址宽度是32bit的,就对齐到4字节,地址宽度是16bit的,就对齐到2字节……
一个类型的大小信息除了描述一个变量所占用的存储器尺寸以外,还隐含了该变量的对齐信息。从结论来说,32位处理器架构下:

  • 对普通的变量类型来说,编译器“倾向于”将小于等于64Bit的数据类型自动对齐到与其大小相同的整数倍上;比如2字节大小的变量会被对齐到2的整数倍地址上,4字节大小的变量会被对齐到4的整数倍地址上,以此类推。
  • 对结构体和共用体来说,它会以所有成员中最大的那个对齐作为自己的对齐值。
    struct对象的内存模型:
//通过struct 关键字定义结构体
struct {
    uint8_t    a;
    uint16_t   b;
    uint8_t    c;
    uint32_t   d;
};

memory layout:

img

在ARM Compiler里面,结构体的对齐使用以下规则:

整个结构体根据结构体内对齐要求最大的那个元素来对齐。 比如,整个结构体内部对齐要求最大的元素是希望对齐到WORD,那么整个结构体就默认对齐到4字节。

结构体内部,成员变量的排列顺序严格按照定义的顺序进行。

结构体内部,成员变量自动对齐到自己的大小——这就会导致空隙的产生。

结构体内部,成员变量可以通过 attribute ((packed))单独指定对齐方式为byte。

适用的方法(Method)和运算(Operation)
对面向对象中的对象来说,方法就是该对象类中描述的各种成员函数(Method);

对数据结构中的各类抽象数据类型(ADT,Abstract Data Type)来说,就是各类针对该数据类型的操作函数,比如链表的添加(Add)、插入(Insert)、删除(Delete)、和查找(Search)操作;比如队列对象的入队(enqueue)、出队(Dequeue)函数;比如栈对象的入栈(PUSH)、出栈(POP)等等……

对普通数值类的变量来说,就是所适用的各类运算,比如针对 int的四则运算(+、-、*、/、>、<、==、!=…)。你不能对float型的数据进行移位操作,为什么呢?因为不同的类型拥有不同的适用方法和运算。

看完这里,您应该能理解了,面向对象的思想其实应用在我们使用的各种代码里,比如用标准数据类型char、int定义的变量,是对象,用抽象数据类型的操作系统的任务控制块,是对象;STM32的HAL库的句柄,是对象;只要心中有对象,对象无处不在。

扩展阅读:
ISO/ANSI C++98标准通过添加string类扩展了C++库,因此只要包含 #include <string>库就可以使用string类型的变量(使用OO的概念,也叫对象),另外C++98的标准模板库(STL)还提供了模板类vector,可以替代传统的动态数组,C++11提供了模板类array,可以替代定长数组。
C++面向对象的语言特性,使C++可以更加充分的发挥出面向对象思想编程的优势。但是,当你有OO概念,并且明确知道自己在做什么的时候,C也可以写出面向对象的代码。

4、面向对象编程的优势与劣势

面向对象思想的核心是把数据和处理数据的方法封装在一起。封装是面向对象的出发点,扩展出的功能还有“继承和多态”,面向对象可以简单的理解为将一切事物抽象化 ,面向对象的代码结构,有效做到了层层分级、层层封装,每一层只理解需要对接的部分,其他被封装的细节不去考虑,有效控制了小范围内信息量的爆炸。学会面向对象的编程思想,只是基础,如何使用好,还需要设计模式等思想的指导。在裸机编程中,如果心怀对象,自然而然的就会将各种统一的变量和处理它们的方法进行封装,而不是全局变量满天飞,函数到处声明,形成难以维护的代码。

然而当项目的复杂度超过一定程度的时候,模块间对接的代价远远高于实体业务干活的代价, 因为面向对象概念的层级划分,要实现的业务需要封装,封装好跟父类对接。多继承是万恶之源,让整个系统结构变成了网状、环状,最后变成一坨乱麻。

Erlang 的创建者 JoeArmstrong 有句名言:

面向对象语言的问题在于,它们依赖于特定的环境。你想要个香蕉,但拿到的却是拿着香蕉的猩猩,乃至最后你拥有了整片丛林。

程序设计要专注于“应用逻辑的实现”本身,应该尽量避免被“某种技术”分心 。《UNIX编程艺术》,第一原则就是KISS原则,整本书都贯彻了KISS(keep it simple, stupid!) 原则。写项目、写代码,目的都是为了解决问题。而不是花费或者说浪费过多的时间在考虑与要解决的问题完全无关的事情上。

文章内容来源于:

模块的封装(一):C语言类的封装
模块的封装(二):C语言类的继承和派生
模块的封装(四):头文件的疼
求求你,不要再纠结指针了(1) ——万能转化公式
漫谈C变量——对齐 (1)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WALI-KANG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值