首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

C 中的面向对象编程

一些我很尊敬的程序员说,从零开始学习编程的学生应该从学习高级编程语言开始,比如 LISP 的某些变体,更多地关注他们想让计算机做什么,而不是计算机如何做。他们这样做。我不同意。我建议学习编程的人从学习 C 开始。与大多数其他语言相比,C 是一种相对低级的语言。(从技术上讲,它被称为“高级语言”,但仅与机器语言和汇编语言进行比较。)C“接近金属”,因为它的语句非常接近必须翻译的机器语言语句在程序运行之前由 C 编译器编译。这教会了学生很多关于计算机如何分配内存、它们如何存储和检索数据、它们如何做出决定以及它们如何进行数学运算的知识。

学习 C 首先会给一个人应有的坚实基础知识,即使一个人打算只进行应用程序编程。否则,如果您的雇主要求您编写设备驱动程序或通信功能或某些嵌入式固件,会发生什么情况?你将无法做到!您的雇主将不得不雇用其他人。猜猜谁会晋升?不是你。所以先学C。不是因为它简单(不是)或安全(不是)或一种好的语言(不是),而是因为它是低级的(它允许您编写固件、驱动程序、编译器和操作系统)和无处不在(它被大量使用,并且它的编译器已经存在于地球上几乎每台计算机上)。然后学习C++,只是基础知识。尤其要学习 STL 及其容器、算法和迭代器。但是现在跳过 OOP 部分;OOP 并不像吹嘘的那样,此外,您以后总是可以学习这部分内容。然后学习 Perl 5,并将它用于涉及文本、Unicode、文件或系统管理的任何事情,因为它擅长这些任务(这些任务恰好是几乎所有其他编程语言所擅长的任务)。忽略批评者(他们很多,主要是 Python 狂热分子)。Perl 是 TIMTOWTDI(不止一种方法)的语言,是现存最好的计算机编程语言之一。并且不要对印记 ($@%&) 发脾气;您最终会爱上它们,因为它们是该语言的最佳功能,因为它们巧妙地将名词与动词分开。(Perl 很大程度上基于人类语言。)然后学习 Python 3,并将其用于几乎所有其他方面(通信、应用程序、数学、科学等)。如果您没有充分的理由使用其他语言进行编程,那么 Python 应该是您的首选语言。它的语法看起来很干净(但要非常小心空格,因为它们很重要)。不过,不要将 Python 用于文本、Unicode、文件或系统管理;Python 很烂;对那些使用 Perl。然后,在您开始专业编程之后,根据需要学习您需要学习的任何其他内容。

是的,是的,成千上万的专业人士现在会跳来跳去,与我刚才所说的一切相矛盾(并且在这个过程中相互矛盾);但请记住,我从 1973 年开始零星地编写计算机程序,从 2005 年开始大量编写计算机程序,所以我并不是没有这方面的经验。

C 中的面向对象编程

与ALGOL一族的大多数过程式编程语言类似,C语言是一个有结构化程序设计、具有变量作用域(variable scope)以及递归功能的过程式语言。其采用的静态类型系统可以防止无意的程序设计操作。C语言中所有的可执行代码都被包含在子程序(函数)里。其传递参数均是以值传递(pass by value),另外也可以传递指针(a pointer passed by value)。C语言是自由形式语言,即其源代码的缩进并不影响程序的功能,而是使用分号作为语句的结尾,大括号来表示代码块。由于C语言的语言规模较小,若干高层的机制需要使用定义的函数来提供。比如,C语言并没有直接处理复合对象(例如字符串、集合、列表、数组等)的操作,也没有对于存储器分配工具和内存回收工具的直接定义,同时也本身不具有输入和输出以及文件访问的方法。然而,用户定义的函数和C语言标准库中的函数为这些高层的机制提供了可能性。

C语言也具有以下的特性:

基本数据类型包括字符、整型和浮点数等。另外也有派生的各种数据类型,如指针、数组、结构和联合。

部分的变量类型可以转换,例如整数型和字符型变量。

透过指针(pointer),C语言可以容易的对存储器进行低端控制。

不同的变量类型可以用结构体(struct)组合在一起。

具有基本的控制流:语句组、条件判断、多路选择、循环等。

函数可以返回各种数据类型的值,并且都可以递归调用。每次调用函数会重新创建变量。

C语言只有32个保留字(reserved keywords),使变量、函数命名有更多弹性。

编译预处理(preprocessor)让C语言的编译更具有弹性。

面向对象编程 (OOP) 自 80 年代末至今广泛使用以来一直是一种常用的范例。如今,C 语言虽然极其重要,因为它是我们现代计算机系统之树的根,但由于它失去了吸引力,因此在很大程度上被抛弃了。在 C 中实现 OOP 的想法引起了不同的反应。大多数程序员会想“为什么不直接使用 C++?”,其他人会说“你不能用 C 做到这一点”,最后一组会说“那会花太长时间!”除了 Objective-C 被掩盖这一事实(我认为这很好),人们会惊讶地发现 C 实际上有不同的策略来完成面向对象的编程。具体来说,实际上有三种方法可以在 C 中完成 OOP(恰当地命名):

开放式。

指针样式。

和GTK 风格。

还有三种方法可以在 C 中应用函数/方法多态性:

结构风格。

脱节风格。

和界面风格。

在我们用 C 深入研究 OOP 之前,我将快速回顾一下 OOP 范式的含义;在 OOP 中,有三个核心原则:

封装。

继承。

多态性。

只要可以给出,C 就可以应用所有这三个核心原则。现在我们将通过首先查看 OOP 风格 C 的 Open 风格来深入研究。

开放式

在 OOP C 的这个变体中,开放式风格最容易应用,因为您在头文件中定义了结构及其方法,并且这些方法在其各自的模块文件中实现。让我们看一下使用动物类的经典 OOP 示例!

下面我们来分析一下头文件。

我们有一个定义的类:struct animal.

这些方法是new(堆构造函数)、create(堆/其他构造函数)、del(单个析构函数)、cleanup(析构函数和清理器)和speak.类成员由一个名为 的虚方法vspeak和一个名为 的浮点数据组成weight。现在让我们来看看这些方法将如何实现。

如您所见,Open 风格是 C 中最直接的 OOP 风格,它与大多数现代 C 代码的实际编写方式几乎没有什么不同。函数指针用于实现虚方法,我们通过方法集中操作和数据操作来进行基本封装。但是你问的继承呢?也适用:

实现继承是更高级的 C 技能发挥作用的地方。如果您还记得在 C 类中,结构实例的地址始终是该结构的第一个成员的地址。

这意味着指向 type 的指针struct hominid也可以作为指向struct animal! 然而,在现代 C 中不建议这样做,因为这将打破可能导致未定义行为的严格别名规则,但有一种更明确的行为方式可以完成同样的事情:通过union!

您可以采取的另一种策略是:

获取基础实例的地址:

或者只是简单地完全使用基本实例:

就个人而言,我认为使用 union 策略更干净,但这只是我的意见。

指针样式

指针样式是 Open 样式的替代方案,您可以使用指针授予的权力来实现数据隐藏!

让我们重用 Open 样式示例标题并修改它以展示差异:

如果您没有注意到,Pointer 样式会省略标头中的结构定义,而是将结构定义移动到模块文件中并init删除构造函数。这样做的好处是您可以实现真正的数据隐藏。除非能够访问源代码,否则没有人能知道结构的真正定义是什么!缺点是您可以通过堆分配创建类实例,而不是选择堆分配或从不同的存储(如全局或堆栈存储)实例化实例。

GTK风格

GTK 风格是 Pointer 风格的变体,但 GTK 风格实现了一种相当有趣的方法,而不是引导指针。在 GTK 样式中,您可以根据需要选择在标头中定义结构。起初,GTK 风格看起来与 Pointer 风格没有太大区别(除了结构定义之外)......

真是个转折!GTK 风格与指针风格不同,始终使用指向基类的指针!这可能很难做到,但这里的情况是当有人创建一个实例到 astruct hominid时,用户会得到一个指向 a 的指针struct animal。数据仍然是 a 的数据struct hominid,并且特定于的方法struct hominid会将指针从struct animalto向下转换struct hominid并做需要做的事情,再次通过使用 a 的力量union。让我们看一下这个hominid_set_height作为例子,因为struct animal没有一个叫的成员height:

现在我们已经了解了 C 可以实现核心 OOP 原则的不同方式,但是 C 可以实现多态性的不同方式呢?

结构样式

可以在 C 中实现的第一种多态性风格是 struct 风格,这是我们在 C 中看到的用于实现 OOP 的三个示例中使用的主要风格。只是一个实现成员和虚拟方法的结构类,没有明显的区别:

杂乱风格

脱节风格是 C 标准库使用的多态形式(信不信由你!),通过使用qsort和bsearch!之所以称为不相交样式,是因为虚函数没有专门绑定到类实现,这带来了不占用更多内存来存储函数指针并将该成本传递到堆栈内存而不是堆内存的好处。

我实际上在我的自定义数据结构实现中使用了脱节样式多态性来实现虚拟析构函数。

界面风格

界面风格是结构风格的细微变化。它基本上是一个只有函数指针的结构,可以用于实现您自己的 vtable(请不要)或巧妙地用于包含成员的结构。

使用支持 C11 的 C 编译器,您可以使用匿名嵌套结构/联合来清理额外的成员访问语法,并将整个结构内联到主结构类中:

我们走了,看起来干净多了。

结论:

如您所见,不仅可以使用许多不同的策略来为 C 实现 OOP,还可以使用不同的策略来实现多态性。如果你在想“我宁愿使用 C++”,那么我会说当然,但请记住,有时人们想要或必须使用 C。如果开发人员的任务是制作一个需要更广泛的框架或库怎么办?触及开发者受众?通过以 OOP 方式拥有 C 库或框架,您可以使与其他语言的接口更加简单。OOP 风格的 C 库可以更轻松地与其他 OOP 语言(如 C++、Java、Python、C# 等)进行交互。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230125A02WKE00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券