闲聊C++与面向对象思想

艾伦·凯曾说,“I made up the term object-oriented, and I can tell you I did not have C++ in mind.”(“我发明了术语‘面向对象’,可以告诉您我没有C++”)。

今天看到这句话,激发了笔者写一篇文章聊聊C++与面向对象思想的关系。

正统的面向对象思想

艾伦·凯(Alan Kay)是计算机科学家,是面向对象编程(Object-Oriented Programming,简称OOP)的发明者。他在1960年代和1970年代的工作对个人计算机和现代编程概念产生了深远影响。

艾伦·凯对面向对象编程的理解强调以下几个核心概念:

  1. 封装:对象应该将其状态和行为封装起来,隐藏内部实现的细节,只暴露有限的接口与外界交互。而C++提供friend等机制破坏了内部的隐藏。

  2. 消息传递:对象之间的交互应该通过发送消息来完成,而C++将这些消息理解成调用其他对象的方法。

  3. 动态绑定:系统在运行时决定哪个代码将被执行,而不是像C++这样在编译时固定下来。

  4. 原型继承:对象可以通过克隆现有的对象(原型)来创建新对象,并可能在此基础上进行修改,这与C++基于类的继承是不同的。

因此,艾伦·凯的这句话认为C++并没有完全捕捉到他最初提出的面向对象编程的精神和概念。他所提倡的面向对象编程理念更侧重于高层次的抽象和设计原则,而不仅仅是语言特性的集合。
Alan Kay认为在OOP中消息传递比对象更重要,而现在人们往往度强调对象本身的概念
从这个角度来看,“面向对象”思想应该叫“面向消息”思想。

C++在面向对象设计上的弯路

C++“独辟蹊径”的面向对象设计,也走了一些弯路。例如C++是一第一个支持多重继承的编程语言,它允许一个类继承自多个基类。然而,多重继承引入了一些设计上的复杂性和困难,其中最著名的就是“菱形继承”问题。

菱形继承(Diamond Inheritance)

菱形继承是当两个派生类继承自同一个基类,而另一个类又从这两个派生类继承时出现的结构。在这种情况下,最底部的派生类将通过两条不同的路径继承顶部基类的属性和方法,这可能会导致多个副本的出现,以及不确定性和冗余。

   A
  / \
 B   C
  \ /
   D

在这个菱形结构中,如果类A定义了一个方法或属性,类B和类C都会继承它,而类D则会从两个路径继承相同的方法或属性。如果类A中的方法或属性不是静态的,那么在类D的对象中就会存在两份相同的方法或属性,这会导致歧义,因为编译器或运行时不知道应该使用哪一个。

虚继承(Virtual Inheritance)

为了解决菱形继承问题,C++引入了虚继承的概念。通过将基类声明为虚拟(virtual)继承,C++确保在继承层次结构中只有一个基类的实例。

虚继承通过引入一个间接层来管理对共享基类的访问,确保即使基类通过多个路径被继承,派生类也只有一份基类的成员。虽然这解决了菱形继承带来的问题,但它也引入了新的复杂性:

  1. 性能开销:虚继承可能会增加额外的运行时开销,因为它需要间接访问基类成员。

  2. 设计复杂性:虚继承使得类的设计和维护变得更加复杂,因为它增加了类之间关系的复杂度。

  3. 初始化复杂性:在虚继承的情况下,初始化共享基类的责任通常落在最派生的类上,这可能导致初始化顺序的问题。

由于这些问题,虚继承通常被认为是C++中一个复杂且难以正确使用的特性。事实上,许多面向对象设计原则和模式都是为了避免使用多重继承和虚继承而提出的。

总之,C++中的多重继承、菱形继承问题以及虚继承的解决方案,反映了C++在实现面向对象概念时遇到的一些设计挑战。这些挑战给后来许多语言带来启示,大部分语言如Java、C#等选择不支持多重继承。

为什么C++不采用正统的面向对象思想设计

C++最初的的设计目标是在C语言的基础上添加面向对象编程的特性,同时保持与C的兼容性以及C的性能优势。C++对面向对象的采用受到其C语言血统的影响,这种影响体现在以下几个方面:

  1. 性能优先:C语言是为了系统级编程而设计的,强调对内存和处理器效率的精细控制,这些特性直接影响了C++的设计。C++保留了这种对性能的追求,这意味着在实现面向对象特性时,需要平衡语言的高层抽象和底层效率。

  2. 兼容性:C++的设计强调与C的向后兼容性。因此,C++程序员可以在面向对象的C++代码中使用纯C代码。这种兼容性是C++广泛采用的一个原因,但它也限制了C++在实现纯面向对象概念上的自由度。

  3. 直接硬件访问:像C语言一样,C++允许程序员直接操作内存和硬件资源,例如通过指针。虽然这提供了极大的灵活性和控制能力,但它也使得C++的面向对象特性更加复杂,因为它需要在易用性和控制之间找到平衡。

  4. 复杂的特性集合:为了在保持C的性能和兼容性的同时支持面向对象编程,C++引入了类、继承、多态、封装等特性。然而,这些特性与C的过程式特性结合在一起,导致了一个复杂的特性集合,这使得C++的面向对象模型比纯面向对象语言更为复杂。

  5. 编译时与运行时的权衡:C++的很多面向对象特性在编译时就已经确定,比如模板和内联函数。这些特性有利于性能优化,但它们也限制了一些面向对象语言中常见的动态行为,例如Smalltalk和Python等语言支持的运行时类型信息和动态绑定。

总之,C++的面向对象特性与其C语言的血统密切相关。C++需要在保持与C的兼容性和性能优势的同时引入面向对象概念,这导致了一个既包含底层系统编程特性又包含高级面向对象特性的语言。这种设计决策使得C++成为一种功能强大但学习和使用起来相对复杂的语言。在实际应用中,这要求开发者仔细考虑如何最有效地利用C++的多范式特性来设计和实现软件系统。

辩证看待C++与面向对象思想

辩证地看待C++与面向对象思想(OOP)的关系意味着要认识到它们之间的紧密联系,以及C++在实现OOP理念时的优势和局限。

C++的优势:

  1. 多范式支持:C++是一种多范式编程语言,不仅支持面向对象编程,还支持过程式编程、泛型编程等。这种多样性使得C++非常灵活,可以适用于各种不同的编程问题和设计选择。

  2. 系统程序设计:C++保留了C语言高效操作硬件资源的能力,使其成为开发系统软件(如操作系统、游戏引擎、嵌入式系统)的理想选择。OOP提供的封装、继承和多态特性可以帮助构建模块化和可维护的系统软件。

  3. 性能优化:C++提供了与硬件操作几乎一样高效的OOP实现,使得开发者能够在不牺牲性能的情况下应用OOP原则。

C++的局限性:

  1. 复杂性:C++的多范式特性和向后兼容C语言的目标使得语言本身非常复杂。这种复杂性可能使得学习和正确使用C++的OOP特性变得困难。

  2. 不纯粹的OOP实现:一些OOP的理想化特性在C++中并没有完全体现,比如C++中对象的生命周期管理通常需要程序员手工处理,而不是完全自动化。

  3. 安全性和可靠性:C++允许直接访问内存和底层系统资源,这虽然提供了极大的灵活性,但也增加了编写出不安全或者不可靠代码的风险。

辩证看待的观点:

  1. 理解设计决策:了解C++设计中的历史和背景,可以帮助我们理解为什么C++以某种方式实现OOP,以及这些设计决策如何影响实际编程。

  2. 选择正确的工具:C++可能不是所有OOP需求的最佳选择。对于需要更纯粹OOP特性的项目,其他语言如Java或Python可能更合适。相应地,对于需要高性能或系统级访问的项目,C++可能是更好的选择。

  3. 权衡利弊:使用C++时,开发者应该根据具体的项目需求和约束来权衡C++的OOP特性与其它特性之间的利弊,以便做出合适的技术选择。

总的来说,辩证看待C++与OOP的关系要求我们既要理解C++如何支持OOP理念,也要意识到C++实现这些理念的独特方式及其带来的优势和挑战。深入理解C++的优劣和历史,我们可以更有效地使用这个强大但复杂的工具。

相关推荐

  1. 闲聊C++面向对象思想

    2024-07-10 22:42:04       8 阅读
  2. C#面:C#面向对象思想主要包括什么?

    2024-07-10 22:42:04       10 阅读
  3. 一种c语言利用面向对象思想编程示例

    2024-07-10 22:42:04       25 阅读
  4. 三、C#面向对象编程(类对象

    2024-07-10 22:42:04       41 阅读
  5. C++面向对象程序设计之类对象(1)

    2024-07-10 22:42:04       16 阅读
  6. 面向对象——类对象

    2024-07-10 22:42:04       19 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 22:42:04       3 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 22:42:04       3 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 22:42:04       2 阅读
  4. Python语言-面向对象

    2024-07-10 22:42:04       2 阅读

热门阅读

  1. 路由器中 RIB 与 FIB 的区别

    2024-07-10 22:42:04       13 阅读
  2. 生成日志系统和监控

    2024-07-10 22:42:04       11 阅读
  3. Apache Spark详解

    2024-07-10 22:42:04       8 阅读
  4. qt opencv 应用举例

    2024-07-10 22:42:04       8 阅读
  5. Pytorch中分类回归常用的损失和优化器

    2024-07-10 22:42:04       8 阅读
  6. 【Rust】Cargo介绍

    2024-07-10 22:42:04       8 阅读
  7. 搭建Spring Cloud项目思路

    2024-07-10 22:42:04       8 阅读
  8. C语言从头学32——字符串数组

    2024-07-10 22:42:04       8 阅读
  9. 7. 有奖猜谜

    2024-07-10 22:42:04       7 阅读
  10. 关于redis的运维面试题-2

    2024-07-10 22:42:04       7 阅读
  11. socketserver和WSGI服务端实现教程

    2024-07-10 22:42:04       10 阅读
  12. 数组常用的方法

    2024-07-10 22:42:04       7 阅读
  13. 设计模式实现思路介绍

    2024-07-10 22:42:04       10 阅读