C++ 编程:编程语言范式
一个 编程范式 是一个基于不同概念的编程模型,它塑造了程序员设计、组织和编写程序的方式。一个 多范式编程语言 允许程序员选择特定的单个方法或混合不同编程范式的部分。C++ 作为一种多范式编程语言,支持使用过程式或面向对象编程的单个或混合方法,并混合使用泛型甚至函数式编程概念。
过程式编程 可以定义为 命令式编程 的一种子类型,它是一种基于过程调用概念的编程范式,其中 语句 被结构化到过程(也称为子例程或函数)中。过程调用是模块化的,并受范围限制。一个过程式程序由一个或多个 模块 组成。每个模块由一个或多个 子程序 组成。模块可能包含过程、函数、子例程或方法,具体取决于编程语言。过程式程序可能具有多个级别或范围,子程序在其他子程序内定义。每个范围可以包含在外部范围中不可见的名称。
过程式编程与简单的顺序编程相比,提供了许多优势,因为过程式代码
- 更易于阅读和维护
- 更灵活
- 促进良好程序设计的实践
- 允许模块以 代码库 的形式再次使用。
类型 指的是计算机语言如何处理其变量,以及它们如何通过 类型 区分。变量是程序在执行期间使用的值。这些值可以改变;它们是变量,因此得名。静态类型 通常会导致执行速度更快的编译代码。当编译器知道正在使用的确切类型时,它可以生成更容易执行正确操作的机器代码。在 C++ 中,变量需要在使用之前定义,以便编译器知道它们的类型,因此是静态类型的。非静态类型的语言称为 动态类型。
静态类型通常可以在编译时更可靠地发现类型错误,从而提高编译程序的可靠性。简而言之,这意味着“圆形销钉不能放入方形孔中”,因此编译器将在类型导致歧义或不兼容使用时报告它。但是,程序员对类型错误的普遍程度以及静态类型将捕获多少已编写错误存在分歧。静态类型支持者认为,程序在进行类型检查后会更加可靠,而动态类型支持者则指出了已证明可靠的动态代码和小型错误数据库。因此,静态类型的价值随着类型系统强度的增加而增加。
静态类型系统比约束较不强大的语言结构更能约束强大的语言结构的使用。这使得强大的结构更难使用,因此将选择“适合问题的正确工具”的负担放在程序员的肩上,否则他们可能倾向于使用可用的最强大的工具。选择过于强大的工具可能会导致额外的性能、可靠性或正确性问题,因为对于可以从强大的语言结构中预期得到的属性存在 理论限制。例如,不加选择地使用 递归 或 全局变量 会导致有据可查的不利影响。
静态类型允许构建不太可能被用户意外误用的库。这可以用作传达库开发人员意图的附加机制。
类型检查 是验证和强制类型约束的过程,这可以在编译时或运行时发生。编译时检查,也称为 静态类型 检查,由编译器在编译程序时执行。运行时检查,也称为 动态类型检查,由程序在运行时执行。如果类型系统确保类型之间的转换必须有效或导致错误,则称编程语言为 强类型。另一方面,弱类型 语言不提供此类保证,通常允许类型之间的自动转换,而这些转换可能没有用武之地。C++ 介于两者之间,允许自动类型转换和程序员定义的转换,允许在解释一种类型为另一种类型时几乎完全灵活。将一种类型的变量或表达式转换为另一种类型称为 类型转换。
面向对象编程 可以被视为过程式编程的扩展,其中程序由称为 对象 的单个单元的集合组成,这些单元具有不同的目的和功能,对 实现 的依赖有限或没有依赖。例如,汽车就像一个对象;它可以将你从 A 点带到 B 点,而无需了解汽车使用的是哪种发动机或发动机的工作原理。面向对象的语言通常提供一种方法来 记录 对象可以做什么和不能做什么,就像驾驶汽车的说明书一样。
一个对象是由成员和方法组成的。成员(也称为数据成员、特征、属性或特性)描述了该对象。方法通常描述与特定对象相关的操作。可以将对象视为名词,其成员视为描述该名词的形容词,其方法视为该名词可以执行的动词。
例如,一辆跑车就是一个对象。它的成员可能是它的高度、重量、加速度和速度。对象的成员只是保存有关该对象的数据。跑车的某些方法可以是“驾驶”、“停车”、“比赛”等。方法只有与跑车相关联才真正有意义,成员也是如此。
让我们构建跑车对象的“蓝图”称为类。类不会告诉我们跑车的速度或颜色,但它确实告诉我们跑车将有一个代表速度和颜色的成员,并且它们将分别是数字和单词。类还为我们制定了方法,告诉汽车如何停车和驾驶,但这些方法仅凭蓝图无法采取任何行动——它们需要一个对象才能产生影响。
C++ 中的类与 C 中的结构相同;区别在于类用户可以通过私有选项隐藏数据。在 C++ 中,对象是类的实例,它被视为一个内置变量,它保存许多值。
封装,即信息隐藏(对用户而言)的原则,是隐藏类数据结构并允许通过公共接口更改数据的过程,在公共接口中,传入的值会经过有效性检查,因此它不仅允许隐藏对象中的数据,还允许隐藏行为。这可以防止接口的客户端依赖于可能在将来发生变化的实现部分,从而使这些更改更容易进行,即无需更改客户端。在现代编程语言中,信息隐藏的原则以多种方式体现出来,包括封装和多态性。
继承描述了两种(或更多种)类型的对象之间的关系,其中一种被认为是另一种的“子类型”或“子类”;因此,“子类”对象被认为继承了父类的特性,允许共享功能。这使得程序员可以重用或减少代码,并简化软件的开发和维护。
继承通常也被认为包括子类型化,其中一种类型的对象被定义为另一种类型的更专门的版本(参见里氏替换原则),但非子类型化继承也是可能的。
继承通常通过描述以继承层次结构(也称为继承链)排列的类来表示,继承层次结构是由它们的继承关系创建的树状结构。
例如,可以创建一个名为“哺乳动物”的变量类,它具有进食、繁殖等特征;然后定义一个子类型“猫”,它继承了这些特征而无需显式地对其进行编程,同时添加了“追逐老鼠”等新特征。这使得不同类型的对象的共性能够表达一次并多次重复使用。
在 C++ 中,我们可以拥有与其他类相关的类(一个类可以通过一个更旧的、预先存在的类
来定义)。这导致了新类具有旧类所有功能的情况,并且还引入了自己的特定功能。我们在这里指的是派生,其中一个给定类是另一个类,而不是组合,其中一个给定类包含另一个类。
当我们讨论类(和结构)继承时,将进一步解释此 OOP 属性,请参阅本书的类继承部分。
如果想要同时使用多个完全正交的层次结构,例如允许“猫”从“卡通人物”和“宠物”以及“哺乳动物”继承,那么我们正在使用多重继承。
多重继承是指一个类可以继承两个或多个类的属性(分别称为基类、父类、祖先类或超类)的过程。
本书的C++ 类继承部分将更详细地介绍这一点。
多态性允许为多个相关但不同的目的重复使用单个名称。多态性的目的是允许为一个通用类使用一个名称。根据数据的类型,将执行通用情况的特定实例。
多态性的概念更为广泛。每次使用具有相同名称但实现不同的两个函数时,就会存在多态性。它们也可以在接口方面有所不同,例如,通过采用不同的参数。在这种情况下,选择要执行哪个函数是通过重载解析来完成的,并且在编译时完成,因此我们将其称为静态多态性。
动态多态性将在类部分中深入介绍,我们将讨论它在派生类中重新定义方法时的使用。
泛型编程或多态性是一种编程风格,它强调允许单个值采用不同的类型作为技术,只要保持某些约定,例如子类型和签名。简单来说,泛型编程是基于寻找高效算法的最抽象表示。模板普及了泛型的概念。模板允许在不考虑最终将使用它的类型的情况下编写代码。模板在标准模板库 (STL)中定义,泛型编程是在 STL 中引入 C++ 的。
自由格式指的是程序员如何编写代码。基本上,除了 C++ 的语义规则之外,没有关于如何选择编写程序的规则。只要是合法的 C++,任何 C++ 程序都应该可以编译。
C++ 的自由格式特性被一些程序员用来编写混淆的 C++ 代码(专门编写起来难以理解的代码)(取决于你的观点)。在正确的上下文中,这也可以被视为一种工艺展示(非功能性但对语言的艺术性控制),但总的来说,混淆的使用只被认为是一种有用的源代码安全机制,它确保源代码更难以被第三方有意地分析、复制或使用。通过对编译器有足够的了解,源代码也可以被设计为在其编译后的形式中保留“水印”,这些“水印”将允许将其追溯到原始来源。