一个简单的例子说明结构体配合函数指针的作用(面向对象编程到底是什么)

学习C/C++语言的过程,新手比较难以理解为何区分出两种阵营

《面向过程编程》
在这里插入图片描述

《面向对象编程》
在这里插入图片描述

如果你拥有着这个疑问并且查阅资料或者上网百度,我想大概你会得到以下似懂非的解答

面向对象编程(OOP)和面向过程编程(POP)是两种不同的编程范式,它们在解决问题和组织代码方面有着不同的方法。
面向对象编程 (OOP):
OOP 是一种以对象为中心的编程范式。在 OOP 中,程序被组织成对象的集合,每个对象都有自己的状态(属性)和行为(方法)。
OOP 的主要思想是将现实世界中的问题抽象成对象,并通过定义对象之间的交互来解决问题。
关键概念包括类(class)、对象(object)、封装(encapsulation)、继承(inheritance)、多态(polymorphism)等。
面向过程编程 (POP):
POP 则是一种按照一系列步骤来解决问题的编程范式。在 POP 中,程序由一系列函数或者过程组成,这些函数依次执行以完成任务。
POP 的核心思想是将问题分解成一系列的步骤,然后按照步骤顺序执行来解决问题。
关键概念包括函数(function)、过程(procedure)、顺序结构、选择结构和循环结构等。
区别总结:
抽象级别不同:OOP 更注重于对象的抽象和封装,而 POP 更注重于流程和步骤的抽象。
复用性:OOP 中的对象可以被多次使用,因此具有更高的复用性;而 POP 中函数也可以被多次调用,但更多是通过将相同的步骤重复利用来实现复用。
维护性:OOP 的封装性使得代码更易于维护,因为修改一个对象的实现不会影响到其他对象;而 POP 中的函数关注于功能的实现,修改时需要更多地考虑调用关系。
可扩展性:OOP 更容易扩展,因为可以通过添加新的类和对象来扩展功能;POP 则更容易出现函数之间的耦合,使得扩展性相对较差。

但实际上我只想用一个C面向过程的编程为开端,后续改成面向对象编程,对比两者之间的区别,即可简单的搞懂两者之间的区别以及这样子做的作用。

假设你的老婆是小学老师,他们的班上有若干的学生需要统计平均分以及总分,现在需要你写一个程序,来记录以上的信息,并且在需要的时候调用。

按照你扎实的基础,你很快就会写出一段

#include "stdio.h"


int average_(int math, int english, int chinese)
{
   int result = (math + english + chinese) / 3;
   return result;
}

int total_(int math, int english, int chinese)
{

    int result = math + english + chinese;
    return result;
}

int main() {

    int chen_math = 96;
    int chen_english = 97;
    int chen_chinese = 98;
    printf("chen_homework.average = %d\n", average_(chen_math, chen_english, chen_chinese));
    printf("chen_homework.total = %d\n", total_(chen_math, chen_english, chen_chinese));
    int lai_math = 95;
    int lai_english = 91;
    int lai_chinese = 98;

    printf("lai_homework.average = %d\n",  average_(lai_math, lai_english, lai_chinese));
    printf("lai_homework.total = %d\n",  total_(lai_math, lai_english, lai_chinese));
   
//此处应该还要有50个学生,想想算不算屎山代码
    return 0;

}

假设你花了一些时间,把所有学生的成绩按照这个方式记录下来了,整个运算过程非常的清晰,记录变量->运算结果->调用结果

但如果突然有一天,老婆说,我们学校决定新增加一种统计方式,对语文数学英语进行加权统计,语文占110%,数学110%,英语80%,但保留原来的统计方式(毕竟教学政策日新月异[doge] )

于是你马上写了一个average2()和total2()函数
并且替换了原来main函数的

int average2_(int math, int english, int chinese)
{
   int result = (math*1.1 + english*0.8 + chinese*1.1) / 3;
   return result;
}

int total2_(int math, int english, int chinese)
{

    int result = math*1.1 + english*0.8 + chinese*1.1;
    return result;
}

int chen_math = 96;
int chen_english = 97;
int chen_chinese = 98;
printf("chen_homework.average = %d\n", average2_(chen_math, chen_english, chen_chinese));
printf("chen_homework.total = %d\n", total2_(chen_math, chen_english, chen_chinese));
int lai_math = 95;
int lai_english = 91;
int lai_chinese = 98;

printf("lai_homework.average = %d\n",  average2_(lai_math, lai_english, lai_chinese));
printf("lai_homework.total = %d\n",  total2_(lai_math, lai_english, lai_chinese));

但各位童鞋有没有一刻想过,好累啊,这当中有什么问题出现?

  • 两个学生我就要改四行,如果是50个学生呢?
  • 万一突然要改回来原来的计算方式,岂不是很麻烦?
  • 代码看起来很不整洁
  • 变量很多,不好管理

于是我们进入今天的正题,把单个学生看做一个对象,对这个对象进行编程,解决上述的几个问题


我们先用结构体把学生的成绩记录下来,并且使用函数指针让每个学生的成员中也引用到average_()和total_()
此时整段代码就可以优化成以下的结构



#include "stdio.h"


int average_(int math, int english, int chinese)
{
   int result = (math + english + chinese) / 3;
   return result;
}

int total_(int math, int english, int chinese)
{

    int result = math + english + chinese;
    return result;
}

int average2_(int math, int english, int chinese)
{
   int result = (math*1.1 + english*0.8 + chinese*1.1) / 3;
   return result;
}

int total2_(int math, int english, int chinese)
{

    int result = math*1.1 + english*0.8 + chinese*1.1;
    return result;
}

typedef struct 
{
  int math;
  int english;
  int chinese;
  int (*average)(int math, int english, int chinese);//参数名字可以省略,也可以和指派的函数的参数名不同
  int (*total)(int math, int english, int chinese);//参数名字可以省略,也可以和指派的函数的参数名不同

} Score_ClassTypeDef;


Score_ClassTypeDef chen_homework = 
{
    .math = 95, //.math 可以省略,前提是和结构体的成员顺序对齐,不省略能提高可读性
    .english = 97,
    .chinese = 98,
    .total = total_,
    .average = average_
};

Score_ClassTypeDef lai_homework = 
{
    .math = 95,
    .english = 91,
    .chinese = 98,
    .total = total_,
    .average = average_
};

int main() {


    printf("chen_homework.average = %d\n", chen_homework.average(chen_homework.math, chen_homework.english, chen_homework.chinese));
    printf("lai_homework.average = %d\n", lai_homework.average(lai_homework.math, lai_homework.english, lai_homework.chinese));
    printf("chen_homework.total = %d\n", chen_homework.total(chen_homework.math, chen_homework.english, chen_homework.chinese));
    printf("lai_homework.total = %d\n", lai_homework.total(lai_homework.math, lai_homework.english, lai_homework.chinese));
    
    return 0;
}

这样子做的好处

  • 两个学生我就要改四行,如果是50个学生呢?
    main函数里面引用的函数名称根本不需要更改,只需要更改函数的内容,或者更改结构体中函数指针指向的函数,比如.total = total_改成.total = total2

  • 万一突然要改回来原来的计算方式,岂不是很麻烦?
    同上,结构体中函数指针重新指派给响应的新的函数,即可

 Score_ClassTypeDef chen_homework = 
{
    .math = 95, //.math 可以省略,前提是和结构体的成员顺序对齐,不省略能提高可读性
    .english = 97,
    .chinese = 98,
    .total = total2_,
    .average = average2_
};
  • 代码看起来很不整洁
    如下,由于都是使用结构体的元素来引用,所以非常符合我们阅读的习惯,很清晰
 printf("chen_homework.average = %d\n", chen_homework.average(chen_homework.math, chen_homework.english, chen_homework.chinese));
  • 变量很多,不好管理
    变量全都由结构体内进行管理,在main()当中没有直接定义变量,很容易管理,而且查找性很强

以上我们用一个很简单的例子(返回类型我统统选择int,可能不够严谨,但谨以此作为例子,好让读者理解),描述了面向过程编程->面相对象编程的转变,以及它的好处与作用,本文忽略了结构体以及函数指针的实现方式和语法的具体实现细节,但实际上只要清楚了他的作用以及为什么要用,语法随便搜索一下即可找到响应的解释

  • 46
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值