本帖最后由 呐咯密密 于 2024-5-29 17:53 编辑
1 面向对象编程基础面向对象编程涉及到三个重要的特性:封装、继承与多态。部分 C 程序员,特别是嵌入式 C 程序员有一种误解,C 语言不是面向对象的编程语言,C++、Java、Python 等更高级的才是,使用 C 语言无法实现面向对象编程。这种误解致使他们没有动力学习一些优秀的面向对象编程方法,例如设计模式、设计原则、软件架构设计等等,进而很难开发出易维护、易部署、易重用、易管理的软件,很难面对项目需求的变更、扩展,很难开发和维护大型的复杂项目。 1.1 对象 面向对象编程,“对象”是整个编程过程的关键。其常见的解释是“数据与函数的组合”。每个对象都是由一组数据(用以描述对象的状态)和一组函数(对象支持的操作,用以描述对象的行为)组成的。对象实现了数据和操作的结合,使数据和操作可以封装于“对象”这个统一体中。 在面向过程编程中,程序设计注重的是“过程”,先做什么,后做什么;在外界看来,整个程序由一系列散乱的数据和函数组合而成。而在面向对象编程中,程序设计注重的是“对象”,在外界看来,整个程序由一系列“对象”块组合而成,数据和函数封装到了对象内部。 1.2 类 对象是有“类型”的,即类。“类”是对一组对象共性的抽象,表示一类对象,而对象是某个类的一个具体化的个例,通常称之为类的实例。对象通常是由数据和函数组成的,相应的类也具有两部分内容:属性(数据的抽象)和方法(对象行为的抽象)。 除了封装属性和操作外,类还具有访问控制的能力,如某些属性和方法是私有的,不能被外界访问。通过访问控制,能够对内部数据提供不同级别的保护,以防止外界意外地改变或使用私有部分。 1. 属性 类具有属性,它是对数据(对象的状态)的抽象。在 C 程序设计时,通常使用结构体类型来表示一个类,相关属性即包含在相应的结构体类型中。例如学生具有属性:姓名、学号、性别、身高、体重等信息,可以使用如下结构体类型表示“学生类”: struct student
{
char name[10]; /* 姓名 (假定最长10字符) */
unsigned int id; /* 学号 */
char sex; /* 性别:'M',男;'F' ,女 */
float height; /* 身高 */
float weight; /* 体重 */
};
2. 方法 类具有方法,它是对象行为的抽象,在 C 程序中,方法可以看作普通函数,不过其通常有一个特点 ,函数的第一个参数为类型的指针,指向了一个确定的对象,用以表明此次操作针对哪个对象,在方法实现时,即可通过该指针访问到对象中的各个属性。(微信公众号【嵌入式系统】这是C面向对象必须的,类似C++的this) 针对学生对象,为了对外展现学生自身的信息,自我介绍的格式是对外输出一个固定格式的字符串: "Hi! My name is xxx, I'm a (boy/girl). My school number is xxx. My height is xxxcm and weight is xxxkg . "
其中的 xxx 对应学生实际的信息,基于此,可以为学生类定义并实现一个“自我介绍”的方法: void student_self_introduction(struct student *p_this)
{
printf("Hi! My name is %s, I'm a %s. My school number is %d. My height is %fcm and weight is %fkg",
p_this->name,
(p_this->sex == 'M') ? "boy" : "girl",
p_this->id,
p_this->height,
p_this->weight);
}
对于外界来讲,调用学生的“自我介绍”方法可以获知学生的全部信息。基于该类的定义,一个简易的应用程序范例详如下: void main(void)
{
struct student chengj = {"chengj", 2024001, 'M', 173, 68};
struct student hehe = {"hehe", 2024002, 'M', 150, 45};
student_self_introduction(&chengj);
student_self_introduction(&hehe);
// ...
}
类中的方法 student_self_introduction 可以作用于任一学生类对象,对于程序员来讲,编写的代码将适用于一组对象,而非特定的某一个对象,提高了代码利用率。 在实际应用中,对比代码《[color=var(--weui-LINK)]嵌入式算法14---数据流与环形队列》,不少程序员都喜欢编写出一堆非常类似的接口,它们仅通过某一个数字后缀(0、1、2……)来区分,如系统使用到 3 个栈,初级程序员可能实现 3 个入栈函数,不良示意代码如下: //三个栈入栈的不良范例,引以为戒
int push_stack0(int data)
{
//...
}
int push_stack1(int data)
{
//...
}
int push_stack2(int data)
{
//...
}
三个操作可能除了极小部分的差异外,其它处理完全相同,这就是没有面向对象编程的思维,没有定义对象类型的概念,将操作直接针对每个具体对象(栈 0、栈 1、栈 2),而不是一组同类的对象(所有栈对象)。显然,3个栈的特性和行为都基本类似,因而可以定义一个“栈类型”,如此一来,入栈操作将属于栈类型中的一个方法,适用于所有栈对象。例如: //数据压入栈, p_stack 指向具体的栈对象
int push_stack(stack *p_stack, int data);
//微信公众号:嵌入式系统
//三个栈的入栈操作均可使用同一个方法
push_stack(p_stack0, 1);
push_stack(p_stack1, 2);
push_stack(p_stack2, 3);
|