别看: 免争议声明
本文主要是为了避免打算学习 C++ 的同学采用 "先学 C 再学 C++" 的错误方法. 在文内可能因对 C 语言了解不足而隐含贬低, 但这种贬低主要针对大学教学而非对 C 语言有偏见:
- K&R 和 Bjarne Stroustrup 是朋友, 《The C Programming Language》所使用的编译器是 C++ 前身 C with Classes 的编译器 cfront;
- C++ 基于 Classic C 和 C89, 在发明过程中又为 C 带来了 const、现在采用的函数声明风格、:cpp:`//` 注释、:cpp:`for` 循环里的初始化部分等;
- C99 和 C++ 是并行关系, 两个标准委员会成立有专门的小组负责联络 C 和 C++ 之间的互通和相互学习特性.
- 2010 年起, GCC 已转为用 C++ 编写.
这是我对 C 和 C++ 的态度.
如果那教的就是 C++, 我也不会喜欢!
—— C++ 创造者 Bjarne Stroustrup
通常, 选 C 课程和选 C++ 课程 没有区别, 在第一学期你甚至会学到几乎一样的内容.
很多老师并没有教现代 C 也没有教现代 C++, 而是采用一种 仅仅为量化考试而非学习程序设计服务的、困难的教学方法. 针对 C 课程, 这被命名为 "谭语言" 教学; 针对 C++ 课程, 这被命名为 "C 风格 C++" 教学.
这种教学方法,
- 自底向上
课程硬塞给初学者很多他们 并不感兴趣而且可能很长时间内用不上的技术细节 (如计算机结构、码制等), 并在教学初期训练初学者如何玩弄语法游戏 (将 :cpp:`*` 放在各个地方有什么不同含义), 令初学者感到厌烦.
这也意味着如果涉及某个内容, 必须教授它们的全部. 因此, 初学者在初期所能使用的编程语言和库的支持明显不足. 他们不能写出有意思的程序, 而不得不认为用 C 和 C++ 编程就是这样枯燥乏味.
- 以抽象理论优先
- 课程通常并不命名为 C 语言课程或 C++ 语言课程, 而是自诩教授程序设计, 因而通常在几页幻灯片上单独给出一些程序设计理论. 此外, 不少老师甚至不会在课上实际编写程序, 整节课上只会展示幻灯片上的代码并贴出程序输出. 这种方法将初学者的注意力从程序设计的实践层面转移开, 迫使学生学习语言语法或程序设计的概念, 却不能实际理解这些概念的重要性.
- 以 C 语言自带功能优先
为什么说选 C 课程和选 C++ 课程没有区别? 课程将 C++ 解释为 C 加上面向对象程序设计风格: 第一学期学 C 语言, 第二学期学一些类、异常、标准库. 这种方法倾向于先以 C 的特性或库做到一个功能, 而在几个月后又给出能做出同等功能的 C++ 特性或库, 并在此时才指出 (或不指出) 不应该使用 C 的特性或库来做.
- C 风格数组被放在了教学的开头, 与 :cpp:`int` 使用起来没有多少区别的 :cpp:`array`、:cpp:`vector`、:cpp:`string` 却被放在了很久之后.
- 总是让人手动编写查找、排序等等算法, C++ 标准库中直接可用的上百种算法却只在教学末尾简单提及.
- 被认为最让 C++ 独具一格的析构函数 [5] 完全被教学忽视: 不断地训练人手动使用 :cpp:`new` 和 :cpp:`delete`, 无知地要求手动关闭文件 :cpp:`fstream.close()` 否则期末考试扣分.
- 总是强调区别内置类型与自定义类型, 误以为 C++ 是面向对象语言而只有自定义类型才算 "类与对象", 完全不了解 C++ 的对象模型和长期以来 "为自定义类型提供内置类型同等支持 (甚至更多支持)" 的努力.
- ……
[5] | Bjarne Stroustrup、Herb Sutter、Kate Gregory、Nicolai Josuttis、Scott Meyers、Sean Parent、Timur Doumler 等均有这样的认识. |
我对它的了解不够, 没办法比较客观地解释它.
这并不是说它不好. 学习一门编程语言并不是学那么一点语法, 还包括其设计哲学、最佳实践、设计模式、惯用法等. 这需要通过实践学习来认识, 并随时间推移通过进一步实践学习不断发展认识. 懂得 C 语言并不会教会你 C++; 懂得 C++ 也不会教会你 C; 懂得一门编程语言并不会教会你如何教授它.
C 相信程序员, 而 C++ 相信编译器.
—— Corentin Jabot
C++ 不是 C. 初学者用先学 C 再学 C++ 的方式学习 C++ 反而会让学习 C++ 变得困难: 用 C 语言的思维去看 C++ 只会觉得 C++ 疯得离谱才加了一堆特性或库 [6].
[6] | 所谓的 "语法糖" |
现代 C++ 的学习并不枯燥乏味, 它允许初学者在学习初期就编写出有意思的程序而得到正反馈. 例如, 在 C++ 创造者 Bjarne Stroustrup 基于自己执教经验编写的用 C++ 进行程序设计的入门书 [7] 中,
- 第一部分在教人 C++ 基础内容的同时, 用编译原理的文法写出一个 计算器程序 从而展示程序设计的基本思路;
- 第二部分在教人 C++ 输入输出、类层次的同时, 教授了如何 画出三角形等图形, 怎么设计出一个 图形用户界面 (例如, 你的 QQ 聊天界面就是一个图形用户界面), 以此引入面向对象程序设计风格的基本原理.
- 第三部分在教人 C++ 标准库容器和算法的同时, 教授人如何自己定义这些容器和算法, 通过泛型编程展示 C++ 与离散数学之间的联系.
能做到这一点, 是因为 C++ 提供了更高抽象层次的特性和库, 支持泛型编程, 且更加类型安全、资源安全进而降低了关注底层技术的要求. 例如,
- 使用 :cpp:`cout`、:cpp:`cin`、:cpp:`print` 进行输入输出, 初学者不需要考虑自己要输出什么类型的变量.
- 使用 :cpp:`string` 而非 C 风格字符串, 初学者不需要考虑输入的文字有多少, 也不需要考虑 :doc:`末尾有没有加上终止字符 <faq/c_string_output/main>`.
- 使用 :cpp:`vector<T>` 而非 C 风格数组, 初学者不需要考虑数组大小够不够, 也不需要担心内存有没有释放.
- 使用标准库容器, 初学者可以像 :cpp:`int` 一样使用数组等数据结构, 不需要在学习值语义的过程中, 突然学习指针的引用语义.
- 使用标准库算法, 初学者在还不清楚怎么编写算法时就能写出进行查找、排序等功能的实用程序.
[7] | :ref:`《Programming: Principles and Practice Using C++》(个人译为《程序设计:使用C++的原理与实践》, 天鹅书) <学习大纲>` |
请至少阅读一下本文的标题, 谢谢. 本文是为了避免打算学习 C++ 的同学采用 "先学 C 再学 C++" 的错误方法.
我们说教授一门编程语言, 绝不仅仅是教授其语法, 还包括程序设计所需的最基本概念、技术和工具, 包括
- 程序组织
- 调试和测试
- 类设计
- 计算
- 函数和算法设计
- 图形用户界面 (GUI)
- 文本处理
- 正则表达式匹配
- 文件和流输入输出 (I/O)
- 内存管理
- 科学/数值/工程计算
- 设计和编程思想
- 标准库
- 软件开发策略
- ……
你当然可以学其他语言, 但如果你志在剥开洋葱, 构建高抽象层次却高效的软件, 那么你最终会学习 C++ 或其代表的设计原理.
Python、Java 等面向对象语言 [8] 用引用语义和垃圾回收机制保护了程序员, 但让初学者很难学习到底层内容. C++ 提供了轻量级的抽象工具和直接而高效的硬件模型, 并且支持多种程序设计风格 (面向过程、面向对象、面向数据、数据抽象、泛型编程、函数式编程……), 因而能让初学者了解尽可能广泛的程序设计内容, 且在初期用抽象工具保护他们接触过于底层.
[8] | 实际上这些语言也支持多种程序设计风格, 只是相较于 C++ 的面向对象程序设计风格是可选的, 它们因其设计理念而无法避免使用面向对象程序设计风格. |
- :doc:`/theory`
- 《The C++ Programming Language, 4th Edition》chapter 1
- Stroustrup: FAQ
- The problem with C
- Why Aren't There C Conferences?