抽象
抽象是从“贴近机器理解”到“贴近人类思维”的过程,其过程从机器语言➡️汇编语言(助记符+labels)➡️非结构化编程语言(早期Basic)➡️结构化编程语言(C/C++).
汇编语言/非结构化编程语言,以单条语句为单位,所谓分支/循环都是通过跳转语句实现的
结构化编程语言,以语句块为单位,具有分支/循环/语句块/函数等语言结构
抽象带来的优点是通用,易读易写,可移植,带来的问题是可能会有性能的下降(或只有同等的性能)
抽象数据类型:a set of values + a set of operations
封装(encapsulation)
封装就是C++中的一种抽象
为什么要封装,在结构体中是只包含数据而不包含操作,不同ADT(抽象数据类型)的操作存在重名冲突,解决办法就是把数据和操作捆绑在一起,这是封装的部分思想,然后就有了扩展结构体,其中可以包含成员变量和成员函数,在C++中称为类,扩展结构体中的操作使用完整如下:
ADT::operation_function()
'::' - scope resolution operator(作用域解析运算符)
对象
ADT = 数据 + 操作
Object = 属性 + 行为
Object由Class承上启下
封装的另一部分思想就是对对象内一部分成员的访问控制,通过access specifier(访问修饰符)来实现,其一可以拒绝对某些成员的访问,其二有限制地或经过检查地访问部分成员(getter&setter)。
#undef标识符
#undef ... 用于取消之前的宏定义,这么做有以下几个用途
1)防止宏定义与其他宏定义或变量冲突
第一种情况与变量冲突:
#define MAX 1
// 打印MAX
#undef MAX
void main(){
int MAX = 10;
//打印MAX
}
第二种情况与宏定义冲突,为了增加可读性:
假设存在'animal.h',需要一个type在animal(结构体+函数)中完成类型别名的替换,那么需要使用animal创建两个对象时可以
#define type Dog
#include"animal.h"
#undef Dog
#define type Cat
#include"animal.h"
#undef Cat
也可以使用'typedef type elem(注:使用typedef elem type是完全不同的意思,意为使用type时实际使用的是elem)',但是不能取消定义。
但是在个例子中可能仍然存在问题,即两个对象存在同名函数,属于重复定义。
2)禁用库函数
C/C++的接口部分可以放在宏定义,如:
#define INTERFACE() implement_function()
C/C++的宏定义还可以使用自定义接口,并禁用库函数
#undef malloc //出于安全考虑禁用malloc函数
#define malloc //也可以
#define MALLOC() ... //使用更安全的自定义函数
继承(inheritance)
增加代码可复用性/可维护性,减少代码冗余。
多态(polymorphism)
通过向上转型(upcasting)把派生类对象作为基类对象的机制,完成同一函数不同实现。
继承和多态都可以让派生类更好地关注自身的新特性,减少其他处代码的改动。
总结:
封装将数据和操作数据的函数抽象成类和对象,其思想是数据和操作绑定以及必要的访问控制;继承则抽象了现实中派生类对象是基类对象的'is-a relationship(继承关系)',形成了'hierarchy of classes(层次结构)';多态则进一步抽象了'hierarchy of classes',将判断对象是哪个子类/层次的问题交给计算机实现。这样,OOP 将类结构化,通过继承扩展代码并使用多态来实现对数据的操控。
根据不同的需求选择不同的编程范式
组合:同一事物的不同部分
OOP适用场景:需要重设计和可扩展
泛型编程适用场景:需要通用和重用