【问题标题】:What can C++ do that is too hard or messy in any other language?C++ 能做什么在任何其他语言中都太难或太乱了?
【发布时间】:2009-01-08 05:17:58
【问题描述】:

我仍然觉得 C++ 提供了一些无法超越的东西。我无意在这里发起一场激烈的战争,如果你对不喜欢 C++ 有强烈的意见,请不要在这里发泄。我很想听听 C++ 大师们为什么坚持使用它。

我对 C++ 鲜为人知或未被充分利用的方面特别感兴趣。

编辑:各位,请至少粗略阅读一下其他回复,以确保您没有重复已经说过的话,如果您同意其他人的说法,请点赞!

【问题讨论】:

    标签: c++ unique


    【解决方案1】:

    RAII/确定性终结。不,当您处理稀缺的共享资源时,垃圾收集并没有那么好

    不受限制地访问操作系统 API

    【讨论】:

      【解决方案2】:

      我一直使用 C++,因为对于需要结合效率复杂性的应用程序来说,它仍然是性能最高的通用语言。例如,我为测量行业的手持设备编写了实时表面建模软件。鉴于资源有限,Java、C# 等...只是不提供必要的性能特征,而 C 等较低级别的语言在抽象特征较弱的情况下开发速度要慢得多。 C++ 开发人员可用的抽象级别范围非常广泛,在一个极端情况下,我可能会重载算术运算符,这样我就可以在运行一个数字的同时说出类似 MaterialVolume = DesignSurface - GroundSurface 的内容不同的堆来最有效地管理特定设备上我的应用程序的内存。将其与用于解决几乎所有常见问题的大量免费资源相结合,您就拥有了一种强大的开发语言。

      C++ 仍然是大多数领域中大多数问题的最佳开发解决方案吗?可能不会,尽管在紧要关头它仍然可以用于大多数人。它仍然是高效开发高性能应用程序的最佳解决方案吗?恕我直言,毫无疑问。

      【讨论】:

      • 我特别喜欢关于多堆的评论。您可以通过重载 new 和 delete 以及在 C++ 中使用放置 new 等来做一些非常酷的事情,非常好!
      • 杰西:说得好。我特别喜欢将它们用于内存池以实现高效的对象回收。
      • 你们中的任何人都对这些进行了基准测试吗?最近的一篇论文表明,大多数此类分配器并不比系统分配器更快,而且通常更慢。
      • @Blaisorblade,你能引用/链接你的参考资料吗?我已经分析了我的特定应用程序,并通过分配器优化发现了巨大的性能提升。请注意,性能增益是基于特定于应用程序的典型用例,它不是通用优化。
      【解决方案3】:

      在自己的脚上开枪。

      没有其他语言提供如此丰富的创意工具。指针、多重继承、模板、运算符重载和预处理器。

      一门非常强大的语言,也为足部射击提供了丰富的机会。

      编辑:如果我蹩脚的幽默尝试冒犯了一些人,我深表歉意。我认为 C++ 是我曾经使用过的最强大的语言 - 能够在需要时在汇编语言级别上进行编码,并在需要时在高级抽象级别上进行编码。自 90 年代初以来,C++ 一直是我的主要语言。

      我的回答是基于多年的射击自己的脚的经验。至少 C++ 允许我优雅地做到这一点。

      【讨论】:

      • 投反对票...叹息。我想我在深夜没有我想的那么有趣。
      • 我觉得这很有趣!我认为讽刺帖子上的大量反对票是一种荣誉徽章。你赢得了我的-1。 :)
      • 没有人想拍自己的脚,所以这不算数,我猜。但是,如果使用得不好,使用像 Spring 这样更强大的框架可能会再次变得更加常见。当然,使用 Spring 比没有使用 Spring 更容易,但使用 Spring 也并非完全容易。
      • 发这样一个有趣的帖子通常是在给自己开枪的例子。啊……讽刺。
      【解决方案4】:

      确定性对象破坏导致一些宏伟的设计模式。例如,虽然 RAII 不像垃圾收集那样通用,但它带来了一些令人印象深刻的功能,而这些功能是 GC 无法获得的。

      C++ 的独特之处还在于它具有图灵完备的预处理器。这允许您更喜欢(与 defer 相反)许多代码任务在编译时间而不是运行时间。例如,在实际代码中,您可能有一个 assert() 语句来测试从未发生过的情况。现实情况是,它迟早会发生......并且发生在凌晨 3:00,当你在度假时。 C++ 预处理器断言在编译时执行相同的测试。当您坐在电脑前观看代码构建时,编译时断言在上午 8:00 到下午 5:00 之间失败;当您在夏威夷睡着时,运行时断言在凌晨 3:00 失败。在那里很容易看到胜利。

      在大多数语言中,策略模式是在运行时完成的,并在类型不匹配的情况下抛出异常。在 C++ 中,策略可以在编译时通过预处理器工具完成,并且可以保证类型安全。

      【讨论】:

      • 您谈了很多关于“c++ 预处理器”的事情。您是指与“c 预处理器”等效/相同的那个吗?我很确定 C 和 C++ 是不同的语言,这意味着预处理器不是一种语言独有的。
      • true...虽然我认为这是一个有用的声明,因为许多 C#/Java 开发人员不知道这样的乐趣。
      • 我不知道第三段是什么意思。预处理器对类型一无所知。
      • @fizzer 第三段指的是策略模式:stackoverflow.com/questions/231318/… - C++ 实际上有两个预处理器运行;模板是类型安全的,其中#macros 是没有人喜欢的孩子。 :)
      • 预处理器在编译器之前运行,并且模板处理由编译器完成 - 模板必须在编译的各个阶段进行处理,例如类型检查。所以请停止这种关于两个预处理器运行的废话。我只是为此而否决了答案。
      【解决方案5】:

      编写内联汇编(MMX、SSE 等)。

      确定性对象销毁。 IE。真正的析构函数。使管理稀缺资源更容易。允许 RAII。

      更轻松地访问结构化二进制数据。将内存区域转换为结构比解析它并将每个值复制到结构中更容易。

      多重继承。并非所有事情都可以通过接口完成。有时你也想继承实际的功能。

      【讨论】:

        【解决方案6】:

        我想我只想称赞 C++ 能够使用模板来捕获表达式并在需要时懒惰地执行它。对于那些不知道这是怎么回事的人,here 就是一个例子。

        【讨论】:

        • 随时回复不止一次 :) 好答案
        • “还没有在其他语言中看到过。”我没有检查,所以不知道这是否属实,但 D 的模板语言也可能支持这一点。除此之外,可能一些函数式语言也能做到这一点,尤其是 Lisp 变体。对于编译的命令式语言,你可能是对的(除了 D 可能)
        • 啊,确实,我忘了D。反正我不是说没有语言支持它,我说我没看到。也许除了 D 之外还有另一个也支持它:) 但我认为没有人这样做,并且 also 与 C++ 一样主要与 C 的源代码兼容性。
        【解决方案7】:

        模板混合提供了我在其他地方没有见过的重用。使用它们,您可以构建一个具有许多行为的大型对象,就好像您是亲手写的一样。但是其功能的所有这些小方面都可以重用,它对于实现接口的一部分(或整个事物)特别好,您正在实现许多接口。生成的对象快如闪电,因为它都是内联的。

        在许多情况下速度可能并不重要,但是当您编写组件软件时,用户可能会以意想不到的复杂方式组合组件来做事,内联和 C++ 的速度似乎允许更复杂的结构已创建。

        【讨论】:

        • 内联的速度究竟如何考虑复杂性? -1
        • 它允许运行时的复杂性。如果一个组件架构没有非常轻量级、快速的组件,那么在系统变得笨拙之前,只有很少的组件能够组合起来。我说的是这里的经验。
        • @steveth45 所以你要给我 +1 回零吗?
        • 速度很重要,c++ 最擅长救援!
        【解决方案8】:

        在需要时对内存布局、对齐和访问进行绝对控制。如果您足够小心,您可以编写一些对缓存非常友好的程序。对于多处理器程序,您还可以从缓存一致性机制中消除很多减速。

        (好吧,您可以在 C、汇编和可能的 Fortran 中执行此操作。但是 C++ 可以让您在更高级别编写程序的其余部分。)

        【讨论】:

          【解决方案9】:

          这可能不是一个流行的答案,但我认为 C++ 的不同之处在于它的编译时功能,例如模板和#define。您可以使用这些功能对您的程序进行各种文本操作,其中大部分已在后来的语言中以简单的名义被放弃。对我来说,这比任何在 C++ 中更容易或更快的低级位摆弄更重要。

          例如,C# 没有真正的宏工具。您不能将另一个文件直接#include 到源代码中,或者使用#define 将程序作为文本进行操作。想想任何时候你不得不机械地输入重复的代码,你知道有更好的方法。您甚至可能已经编写了一个程序来为您生成代码。好吧,C++ 预处理器自动化了所有这些事情。

          与 C++ 模板相比,C# 中的“泛型”功能同样受到限制。 C++ 允许您盲目地将点运算符应用于模板类型 T,调用(例如)可能不存在的方法,并且仅在模板实际应用于特定类时才应用正确性检查。发生这种情况时,如果您对 T 所做的所有假设实际上都成立,那么您的代码将编译。 C# 不允许这样做...类型“T”基本上必须作为对象处理,即仅使用所有可用操作的最低公分母(赋值、GetHashCode()、Equals())。

          C# 以简单的名义取消了预处理器和真正的泛型。不幸的是,当我使用 C# 时,我发现自己正在寻找这些 C++ 结构的替代品,这些 C++ 结构不可避免地比 C++ 方法更加臃肿和分层。例如,我看到程序员以几种臃肿的方式解决#include 的缺失问题:动态链接到外部程序集、在多个位置重新定义常量(每个项目一个文件)或从数据库中选择常量等。

          正如《辛普森一家》的 Crabapple 女士曾经说过的那样,“这太糟糕了,米尔豪斯”。

          在计算机科学方面,C++ 的这些编译时特性支持诸如按名称调用参数传递之类的事情,众所周知,它比按值调用和按引用调用更强大。

          同样,这可能不是流行的答案——例如,任何介绍性的 C++ 文本都会警告您不要使用#define。但是多年来一直使用多种语言,并考虑到所有这些背后的理论,我认为很多人给出了不好的建议。在被称为“IT”的稀释子领域中似乎尤其如此。

          【讨论】:

          • 很好的答案!我的情绪完全正确。我记得问过一个 .NET (&& !C++) 开发人员如何做我会使用宏的工作,他说,哦,使用 sn-ps!片段破坏了整个目的,它们是一个编辑器工具。当重复的代码更改时,您必须重新编辑它,您不能在一个地方进行更改。在我的工作中,我们现在使用 c++ 的前端,它具有比 c++ 更强大的宏扩展器。现在使用纯 c++ 令人生畏,使用 .NET 编写重复代码时令人作呕……尤其是当您尝试为编译器生成令牌时。
          • 同上模板参数,我们一直使用模板混合(我们的前端使语法非常简洁)。这是最好的代码重用形式!
          【解决方案10】:

          以最小的开销跨进程传递 POD 结构。换句话说,它使我们能够轻松处理二进制数据块。

          【讨论】:

          • 这不是完全重复,但与 Boojum 和 Steve Rowe 对二进制数据的看法相同。
          【解决方案11】:

          C# 和 Java 强制您将“main()”函数放在一个类中。我觉得这很奇怪,因为它稀释了类的含义。

          对我来说,类是问题域中的一类对象。程序不是这样的对象。所以你的程序中不应该有一个名为“程序”的类。这相当于使用符号来表示自己的数学证明——证明——以及表示数学对象的符号。这会很奇怪和不一致。

          幸运的是,与 C# 和 Java 不同,C++ 允许使用全局函数。这让您的 main() 函数存在于外部。因此,C++ 提供了一个更简单、更一致并且可能更真实的面向对象习语的实现。因此,这是 C++ 可以做的一件事,但 C# 和 Java 做不到。

          【讨论】:

          • 奇怪的是为什么你说“幸运”,而事实上全球性的东西通常被认为是一件坏事。 Java/C#(在 C++ 之后出现,因此如果他们愿意,他们拥有执行全局函数的所有能力)比 C++(老实说,这只是对 C 的一种模仿 OOP 的黑客攻击)强制执行 OO 范式。将 main() 方法放在一个类中,因为您可以拥有多个 main() 方法,但是您可以选择在执行时从哪个类开始。它更多地执行了封装原则,即没有任何东西是全局的,一切都必须属于某个地方。全局的东西与 OOP 完全不一致。
          • @jbx 全局变量被认为是一件坏事。不是全局函数。此外,对于 OOP 是什么还没有达成共识。或者即使这是一件好事。
          • 是的,我同意,对于 OOP 的好坏没有共识,事实上,就像大多数编程范式一样,它在某些类型的应用程序中很有用,而在其他情况下,其他范式更合适。全局函数在 OOP 中被认为不好(在其他范式中可能没问题),因为 OOP 提倡封装和信息隐藏。 OOP 中使用的全局函数只是从过程编程中借来的一个技巧,因为谁在使用它们并不知道。 OOP 并不是对所有事情都有好处,但如果你要去做,就好好地去做,C++ 在这方面会惨遭失败。
          【解决方案12】:

          我认为运算符重载是一个非常好的特性。当然,它可能会被滥用(比如在 Boost lambda 中)。

          【讨论】:

          • 此外,运算符重载可以在其他面向对象语言中被伪造,例如 BigInteger.add(...)。虽然看起来比较丑,但效果是一样的。
          • 我同意 Zach 的观点,但 Raymond,我认为这属于“混乱”类别。
          • Raymond:任何图灵完备语言都可以伪造一切。运算符重载的重点在于更简洁的语法。
          • 另外,操作符不需要是成员函数,所以你可以添加一个与BigInterger一起使用的操作符,即使你不能修改类的源代码。
          【解决方案13】:

          对系统资源(尤其是内存)进行严格控制,同时可选地提供强大的抽象机制。在这方面,我所知道的唯一可以接近 C++ 的语言是 Ada。

          【讨论】:

            【解决方案14】:

            C++ 提供了对内存的完全控制,因此使程序执行流程更加可预测。 您不仅可以准确地说出内存分配和释放的时间,还可以定义自己的堆,拥有多个用于不同目的的堆,并准确地说出内存数据分配到的位置。这在嵌入式/实时系统(例如游戏机、手机、mp3 播放器等)上进行编程时通常很有用,其中:

            1. 对易于访问的内存有严格的上限(与 PC 相比,当您用完物理内存时速度会变慢)
            2. 经常有非同质的内存布局。您可能希望在一块物理内存中分配一种类型的对象,而在另一块中分配另一种类型的对象。
            3. 具有实时编程约束。在错误的时间意外调用垃圾收集器可能是灾难性的。

            AFAIK、C ​​和 C++ 是做这种事情的唯一明智选择。

            【讨论】:

            【解决方案15】:

            老实说,只要你愿意编写足够多的代码,你几乎可以做任何事情。

            所以回答你的问题,不,没有什么是你不能用另一种语言做的 C++ 做不到的。只是你有多少耐心,你愿意付出漫长的不眠之夜来让它发挥作用吗?

            C++ 包装器可以轻松完成某些事情(因为它们可以读取头文件),例如 Office 开发。但同样,这是因为有人写了很多代码来为你“包装”在 RCW 或“运行时可调用包装器”中

            编辑:你也意识到这是一个加载的问题。

            【讨论】:

            • 问题说“太难或太乱”...你看到了吗?另外,你说:“不,没有什么是你不能用另一种语言做的 C++ 做不到的”。再读一遍。它是通过什么方式加载的?我正在寻找来自优秀 C++ 程序员的信息。
            猜你喜欢
            • 2010-09-20
            • 2010-10-22
            • 2011-07-20
            • 2012-02-05
            • 2018-05-12
            • 1970-01-01
            • 2016-10-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多