请记住,我说的是ASFAC++B。 :) 我将最重要的差异化因素放在首位。
垃圾收集
垃圾回收 (GC) 是区分这些语言的最重要因素。
虽然 C 和 C++ 可以与 GC 一起使用,但它是事后才想到的,不能很好地工作 (the best known is here) - 它必须是“保守的”,这意味着它不能收集所有未使用的内存.
C# 从一开始就被设计为在 GC 平台上工作,标准库也以这种方式设计。它对开发人员的生产力产生了绝对根本性的影响,必须亲身体验才能相信。
C/C++ 用户普遍认为 GC 等同于“糟糕的性能”。但这是过时的民间传说(即使是 C/C++ 上的 Boehm 收集器也比大多数人预期的要好得多)。典型的恐惧是程序停止的“长时间停顿”,以便 GC 可以做一些工作。但实际上这些长时间的停顿发生在非 GC 程序上,因为它们运行在虚拟内存系统之上,该系统偶尔会中断以在物理内存和磁盘之间移动数据。
也普遍认为 GC 可以替换为shared_ptr,但它不能;具有讽刺意味的是,在多线程程序中,shared_ptr 比基于 GC 的系统慢。
有些环境如此节俭以至于 GC 不实用 - 但这些环境越来越少见。手机通常有 GC。 C# 通常在其上运行的 CLR 的 GC 似乎是最先进的。
自从大约 18 个月前采用 C# 以来,我已经经历了几个使用分析器进行纯性能调优的阶段,GC 非常高效,在程序运行期间几乎不可见。
GC 不是万能的,它并不能解决所有的编程问题,它只是真正清理内存分配,如果你分配非常大的内存块,那么你仍然需要小心,它仍然是可能的在足够复杂的程序中发生内存泄漏 - 然而,GC 对生产力的影响使其非常接近于灵丹妙药!
未定义的行为
C++ 建立在未定义行为的概念之上。也就是说,语言规范定义了语言特征的某些狭义用法的结果,并将所有其他用法描述为导致未定义的行为,这意味着原则上该操作可能会产生任何结果(实际上这意味着难以诊断的错误涉及明显的非确定性数据损坏)。
几乎所有关于 C++ 的内容都涉及未定义的行为。即使是非常好的即将推出的功能,如 lambda 表达式,也可以很容易地用作破坏堆栈的便捷方式(通过引用捕获本地,允许 lambda 实例比本地更长寿)。
C# 建立在所有可能的操作都应该具有定义的行为的原则之上。可能发生的最坏情况是抛出异常。这彻底改变了软件构建的体验。
(存在不安全模式,它具有指针,因此具有未定义的行为,但强烈建议不要将其用于一般用途 - 将其视为类似于嵌入式汇编语言。)
复杂性
就复杂性而言,C++ 必须单独列出,尤其是考虑到即将标准化的新版本。 C++ 绝对尽其所能使自己有效,而不是假设 GC,因此它有一个很棒的学习曲线。语言设计者通过说“这些功能仅适用于库作者,而不是普通用户”来为这一点辩解——但要在任何语言中真正有效,您需要将代码构建为可重用的库。所以你逃不掉的。
从积极的方面来说,C++ 非常复杂,就像书呆子的游乐场!我可以向您保证,学习如何将它们组合在一起会很有趣。但我不能认真地推荐它作为主流平台上富有成效的新工作(哦,浪费的岁月......)的基础。
C 使语言保持简单(在“编译器易于编写”的意义上简单),但这使得编码技术更加神秘。
请注意,并非所有新的语言功能都等同于增加复杂性。一些语言特性被描述为“语法糖”,因为它们是编译器为您扩展的简写。这是考虑近年来 C# 的大量改进的好方法。语言标准甚至通过将翻译提供给速记来指定一些功能,例如using 语句扩展为 try/finally。
在某一时刻,可以以同样的方式考虑 C++ 模板。但从那以后,它们变得如此强大,以至于它们现在构成了该语言整个独立维度的基础,并拥有自己的enthusiastic user communities and idioms。
库
C 和 C++ 最奇怪的地方在于它们没有标准的可互换形式的预编译库。将其他人的代码集成到您的项目中总是有点繁琐,要做出关于如何链接到它的模糊决定。
此外,标准库非常基础——C++ 有一套完整的数据结构和一种表示字符串的方法 (std::string),但这仍然是最小的。是否有在目录中查找文件列表的标准方法?令人惊讶的是,不!是否有用于解析或生成 XML 的标准库支持?不。访问数据库怎么样?严肃点!写一个网站后端?你疯了吗?等等
所以你必须去更远的地方打猎。对于 XML,请尝试 Xerces。但它是否使用std::string 来表示字符串?当然不是!
所有这些第三方库在命名类和函数时都有自己的奇怪习惯吗?你打赌!
C# 中的情况完全不同;基础从一开始就到位,所以一切都可以很好地互操作(而且因为基础是由 CLR 提供的,所以有跨语言支持)。
这并不完美;泛型应该从一开始就存在,但没有,这确实在一些较旧的库上留下了明显的伤痕;但是从外部解决这个问题通常是微不足道的。此外,还有一些流行的库是从 Java 移植而来的,这并不像最初出现的那么合适。
闭包(带有局部变量捕获的匿名方法)
Java 和 C 实际上是最后剩下的缺少闭包的主流语言,使用它们可以设计和使用库比不使用它们时更加整洁(这就是移植的 Java 库有时对 C# 用户来说显得笨拙的原因之一)。
关于 C++ 的有趣之处在于,它的标准库被设计为好像该语言中提供了闭包(容器类型,<algorithm>,<functional>)。然后十年过去了,现在他们终于被添加了!它们将产生巨大的影响(尽管如上所述,它们泄露了欠精细的行为)。
C# 和 JavaScript 是使用最广泛的语言,其中“惯用地建立”闭包。 (这些语言之间的主要区别在于 C# 是静态类型的,而 JavaScript 是动态类型的)。
平台支持
我把它放在最后只是因为它似乎不像你想象的那样区分这些语言。所有这些语言都可以在多种操作系统和机器架构上运行。 C 是最广泛支持的,然后是 C++,最后是 C#(尽管由于名为 Mono 的开源实现,C# 可以在大多数主要平台上使用)。
我在 Windows 和各种 Unix 风格之间移植 C++ 程序的经历令人不愉快。我从未尝试将 C# 中非常复杂的任何东西移植到 Mono,所以我无法对此发表评论。