it-swarm.cn

是否有任何理由使用C ++代替C,Perl,Python等?

作为Linux(服务器端)开发人员,我不知道在哪里以及为什么应该使用C++。

当我追求性能时,第一个和最后一个选择是C。

当“性能”不是主要问题时,Perl和Python等编程语言将是不错的选择。

我在这方面几乎了解的所有开源应用程序都是用C,Perl,Python,Bash脚本, [〜#〜] awk [〜#〜] 甚至PHP编写的,但是没有人使用C++ 。

我不是在讨论GUI或Web应用程序之类的其他领域,而是在谈论Linux,CLI和守护程序。

使用C++是否有令人满意的理由?

166
Ehsan

当我要表演时,第一个和最后一个选择是C。

这就是您应该备份的地方。现在,我根本不能为服务器开发发言。也许确实没有令人信服的理由偏爱C++而不是其他选择。

但是总的来说,使用C++而不是其他语言的原因确实是性能。这样做的原因是C++提供了一种抽象手段,与all我所知道的其他语言不同,在运行时没有性能开销。

这样可以编写非常高效的代码,使still具有很高的抽象水平。

考虑通常的抽象:虚函数,函数指针和PIMPL习惯用法。所有这些都依赖于在运行时由指针算法解决的间接。换句话说,这会产生性能成本(但是可能会很小)。

另一方面,C++提供了一种间接机制,该机制导致no(性能)成本:模板。 (这种优势是通过(有时极大地)增加了编译时间来弥补的。)

考虑一个通用排序函数的例子。

在C语言中,函数qsort带有一个函数指针,该指针实现了元素相对于彼此进行排序的逻辑。 Java的Arrays.sort函数有多种变体;其中之一对任意对象进行排序,并要求向其传递Comparator对象,该对象的工作方式与C语言的qsort中的函数指针非常相似。但是,“本机” Java=)类型还有更多的重载。它们每个都有一个自己的sort方法的副本–可怕的代码重复。

Java在这里说明了一般的二分法:要么代码重复,要么产生运行时开销。

在C++中,sort函数的工作原理与C中的qsort相似,但有一个小但基本的区别:传递给该函数的比较器是模板参数。这意味着它的调用可以是内联。比较两个对象不需要间接调用。在紧密的循环中(如此处的情况),这实际上可以产生很大的不同。

毫不奇怪,即使基础算法相同,C++ sort函数也优于C的sort。当实际的比较逻辑比较便宜时,这一点尤其明显。

现在,我不是不是说C++比C(或其他语言)更高效,也不是说它提供更高的抽象性。它提供的是一个既很高又非常便宜的抽象,因此您通常不需要在高效和可重用的代码之间进行选择。

310
Konrad Rudolph

我看到太多讨厌C++的C程序员。我花了相当长的时间(数年)才慢慢了解它的优点和缺点。我认为最好的表达方式是:

更少的代码,没有运行时开销,更安全。

我们写的代码越少越好。在所有追求卓越的工程师中,这一点很快就变得显而易见。您只在一个地方修复了一个错误,却不是很多-您只表达一次算法,然后在许多地方重复使用,依此类推。希腊人甚至有一种说法,可以追溯到古代的斯巴达人:你对此很明智”。问题的实质是如果正确使用,C++允许您用比C少得多的代码来表达自己,而无需花费运行时间,而比C更安全(即在编译时捕获更多错误)。

这是我的 renderer 的简化示例:在三角形的扫描线上插入像素值时。我必须从X坐标x1开始并到达X坐标x2(从三角形的左侧到右侧)。在每个步骤中,在我经过的每个像素中,我都必须内插值。

当我插入到达像素的环境光时:

_  typedef struct tagPixelDataAmbient {
      int x;
      float ambientLight;
  } PixelDataAmbient;

  ...
  // inner loop
  currentPixel.ambientLight += dv;
_

当我插入颜色时(称为“ Gouraud”底纹,其中“红色”,“绿色”和“蓝色”字段通过每个像素的步长值进行插入):

_  typedef struct tagPixelDataGouraud {
      int x;
      float red;
      float green;
      float blue;  // The RGB color interpolated per pixel
  } PixelDataGouraud;

  ...
  // inner loop
  currentPixel.red += dred;
  currentPixel.green += dgreen;
  currentPixel.blue += dblue;
_

当我在“ Phong”阴影中渲染时,我不再插值强度(ambientLight)或颜色(红色/绿色/蓝色)-我插值法线向量(nx,ny,nz),并且在每一步中,我都必须重新-根据插值法线向量计算照明方程:

_  typedef struct tagPixelDataPhong {
      int x;
      float nX;
      float nY;
      float nZ; // The normal vector interpolated per pixel
  } PixelDataPhong;

  ...
  // inner loop
  currentPixel.nX += dx;
  currentPixel.nY += dy;
  currentPixel.nZ += dz;
_

现在,C程序员的第一个本能是““,编写三个插值的函数,然后根据设置模式调用它们”。首先,这意味着我遇到类型问题-我该如何处理?我的像素PixelDataAmbient吗? PixelDataGouraud? PixelDataPhong?哦,等等,高效的C程序员说,使用联合!

_  typedef union tagSuperPixel {
      PixelDataAmbient a;
      PixelDataGouraud g;
      PixelDataPhong   p;
  } SuperPixel;
_

..然后,您有一个功能...

_  RasterizeTriangleScanline(
      enum mode, // { ambient, gouraud, phong }
      SuperPixel left,
      SuperPixel right)
  {
      int i,j;
      if (mode == ambient) {
          // handle pixels as ambient...
          int steps = right.a.x - left.a.x;
          float dv = (right.a.ambientLight - left.a.ambientLight)/steps;
          float currentIntensity = left.a.ambientLight;
          for (i=left.a.x; i<right.a.x; i++) {
              WorkOnPixelAmbient(i, dv);
              currentIntensity+=dv;
          }
      } else if (mode == gouraud) {
          // handle pixels as gouraud...
          int steps = right.g.x - left.g.x;
          float dred = (right.g.red - left.g.red)/steps;
          float dgreen = (right.g.green - left.a.green)/steps;
          float dblue = (right.g.blue - left.g.blue)/steps;
          float currentRed = left.g.red;
          float currentGreen = left.g.green;
          float currentBlue = left.g.blue;
          for (j=left.g.x; i<right.g.x; j++) {
              WorkOnPixelGouraud(j, currentRed, currentBlue, currentGreen);
              currentRed+=dred;
              currentGreen+=dgreen;
              currentBlue+=dblue;
          }
...
_

您是否感到混乱进入?

首先,要使我的代码崩溃,只需输入一个错字,因为编译器永远不会在函数的“ Gouraud”部分中阻止我访问实际的“ .a”。 (环境)值。没有被C类型系统捕获的错误(即,在编译期间),意味着在运行时出现的错误,需要进行调试。您是否注意到我在计算“ dgreen”时正在访问_left.a.green_?编译器肯定没有告诉您。

然后,到处都有重复-for循环存在的次数与存在渲染模式的次数相同,我们不断执行“右减去左除以步长”。丑陋且容易出错。当我应该使用“ j”时,您是否注意到我在Gouraud循环中使用“ i”进行了比较?编译器再次保持沉默。

模式的if/else /阶梯怎么样?如果我在三周内添加了新的渲染模式怎么办?我会记得在所有代码中的所有“ if mode ==“”中处理新模式吗?

现在,将上述丑陋与这组C++结构和一个模板函数进行比较:

_  struct CommonPixelData {
      int x;
  };
  struct AmbientPixelData : CommonPixelData {
      float ambientLight;
  };
  struct GouraudPixelData : CommonPixelData {
      float red;
      float green;
      float blue;  // The RGB color interpolated per pixel
  };
  struct PhongPixelData : CommonPixelData {
      float nX;
      float nY;
      float nZ; // The normal vector interpolated per pixel
  };

  template <class PixelData>
  RasterizeTriangleScanline(
      PixelData left,
      PixelData right)
  {
      PixelData interpolated = left;
      PixelData step = right;
      step -= left;
      step /= int(right.x - left.x); // divide by pixel span
      for(int i=left.x; i<right.x; i++) {
          WorkOnPixel<PixelData>(interpolated);
          interpolated += step;
      }
  }
_

现在来看这个。我们不再制作联合类型汤:每个模式都有特定的类型。他们通过从基类(CommonPixelData)继承来重用其常用的东西(“ x”字段)。模板使编译器创建(即代码生成)我们将用C语言编写的三个不同函数,但同时要严格限制类型!

我们在模板中的循环无法执行操作并无法访问无效字段-如果这样做,编译器将吠叫。

模板执行共同的工作(循环,每次都以“步长”递增),并且可以以不会导致运行时错误的方式进行。每种类型(AmbientPixelDataGouraudPixelDataPhongPixelData)的插值都是通过operator+=()完成的,我们将在结构中添加-这基本上决定了如何内插每种类型。

您是否看到我们对WorkOnPixel <T>所做的工作?我们要为每种类型做不同的工作吗?我们简单地称呼模板专业化:

_void WorkOnPixel<AmbientPixelData>(AmbientPixelData& p)
{
    // use the p.ambientLight field
}


void WorkOnPixel<GouraudPixelData>(GouraudPixelData& p)
{
    // use the p.red/green/blue fields
}
_

也就是说,要调用的函数是根据类型决定的。在编译时!

重述一下:

  1. 我们最小化代码(通过模板),重复使用通用部分,
  2. 我们不会使用丑陋的骇客,我们会保留严格的类型系统,以便编译器可以随时检查我们。
  3. 最重要的是:我们所做的一切都不会对运行时产生任何影响。该代码的运行速度几乎与等效的C代码一样快-实际上,如果C代码使用函数指针来调用各种WorkOnPixel版本,则C++代码将比C更快,因为编译器将内联特定于类型的WorkOnPixel模板特化调用!

更少的代码,没有运行时开销,更安全。

这是否意味着C++是语言的全部和全部?当然不是。您仍然必须权衡取舍。无知的人应该在编写Bash/Perl/Python脚本时使用C++。满足触发器要求的C++新手将创建具有虚拟多重继承的深层嵌套类,然后才能停止它们并将其打包。在意识到这是不必要的之前,他们将使用复杂的 Boost 元编程。他们将仍然使用_char*_,strcmp和宏,而不是_std::string_和模板。

但这无非是...观察与谁共事。没有语言可以保护您免受不称职的用户的侵害(没有,甚至没有Java)。

继续学习和使用C++-只是不要过度设计。

167
ttsiodras

[〜#〜] raii [〜#〜] 赢了宝贝。

严重的是,C++中的确定性销毁使代码更清晰,更安全,而没有任何开销。

79
Motti

使用C++是否有令人满意的理由?

  1. 模板和STL。您只需花费少量的构建时间(以及一些可能无法理解的错误消息)就可以使用lot来使用有用的抽象和省力的工具,而运行时的性能却不会受到明显的影响(尽管二进制足迹可能很小)更大)。

    缠头需要一些时间(点击之前花了我几年时间),但一旦这样做,生活就会变得更简单很多

  2. 与C语言相比,C++中的文本处理不那么痛苦数量级

70
John Bode

是的

如果您要寻找可执行的效率,那么您可以考虑使用C或C++,因此我将重点介绍它。

甚至在模板通用之前,我就比较喜欢使用C++代替C,因为您早在1990年代中期就讨论过这种可执行文件,这有两个很简单的原因: 对象多态性[〜#〜] raii [〜#〜]

我已经将多态C++对象用于各种有趣的事情。例如,我正在一个嵌入式Linux系统上使用OMAP和XScale ARM CPUs)上的帧缓冲区覆盖。这两个硬件体系结构具有不同的覆盖功能,并且具有非常不同的API。我使用了通用的虚拟“覆盖”基类以显示理想的覆盖视图,然后编写“ OmapOverlay”和“ XScaleOverlay”类,这些类在运行时适当地实例化,具体取决于检测到其运行代码的体系结构。

为了简化起见,RAII的想法是在对象的构造函数期间或对象生命周期的稍后阶段分配连接到对象的资源,然后在对象的析构函数中释放资源或释放资源。在C++中,这确实不错,因为自动变量的对象在超出范围时会被破坏。对于同时具备C和C++能力的人来说,避免C++中的资源和内存泄漏要容易得多。您也看不到太多的C++代码,在函数的末尾,在调用free()以及在函数块跳转中调用各种goto之前,在标签的末尾看到了非常常见的C符号。那里。

我完全意识到,您可以使用C来完成所有这些事情-它要做的工作很多,代码行更多,并且最终得到的结果更加丑陋,而且通常很难理解。整个 X服务器 内部都有多态代码,伙计,它是丑陋又怪异的,而且经常很难追踪。

我还使用GNOME技术(例如GTK +和Clutter)做了很多工作,所有这些技术都是使用GObject系统用C编写的。 GObject就像C++对象系统,其中的“ Nice”封面被剥开,所有丑陋的内部结构都暴露了出来,它通常需要六行代码来完成单行C++方法调用所要做的工作。我目前正在写一些ClutterActors,尽管数学真的很有趣,但我一直在思考:“在C++中,这一切将更加简洁和易于理解”。

我还经常想:“如果我用C++而不是C编写代码,我会和我妻子一起在客厅看 MythBusters 而不是晚上9点坐在我的办公室。”

41
Bob Murphy

C++与C一样快(有些东西更快,有些慢),并且它提供了更好的抽象和组织。类的工作方式与原始类型相似,从而无需记住即可使用大量代码。操作员重载和模板使编写代码的功能成为可能,如果数据表示形式发生更改,代码的功能会更好。异常可以简化错误处理。编译器可用于在编译时检查更多内容。

您为此付出的代价是一个相当讨厌的学习曲线,并且比我熟悉的大多数其他语言更容易犯一些细微的错误。

因此,我无法确定您现在正在做的事情是否值得您去学习。当然可以使用Python或Perl和C的组合)来解决,但是C++在一个难于使用的包中同时提供了抽象和性能。

29
David Thornley

我认为C++是1990年代的语言,是过去时代的语言。

当时的规模很大,因为它以较低的性能成本提供了更高级别的语言结构和机制。它是开发从通讯录应用程序到航空电子软件的所有工具的通用工具,激发了OO狂热。OOP解决了饥饿和艾滋病,是的,我责怪C++在1990年代后期尝试洗脑时,我第一次开始编程,任何非OO语言都不值得学习。

既然硬件已经取得了很大的进步,并且出现了更新的现代语言,我看不到C++对于大多数应用程序编程仍然是一个相关的选择,除了需要大量抽象的计算密集型软件(游戏,物理模拟,CAD系统等),如果您用C编写了一个紧凑的模块化引擎,并且将更高级别的应用程序逻辑委派给一种简洁的脚本语言,则甚至可以避免后者。

如果您需要精通C语言,并且需要高级使用时,请使用不宣扬封装的现代语言来完成,而您可以通过指针自由更改每个字节。

28
Blagovest Buyukliev

据Linus ,否:

当我第一次查看Git源代码时,有两件事让我感到奇怪:1.纯C而不是C++。不知道为什么。请不要谈论可移植性,它是BS。

[〜#〜]你[〜#〜]满是胡说八道。

C++是一种可怕的语言。由于许多不合格的程序员使用它,使它变得更加可怕,以至于要用它产生完全和完全废话要容易得多。坦率地说,即使选择C不会什么也不做却使C++程序员望而却步,这本身就是使用C的巨大原因。

换句话说:C的选择是唯一明智的选择。我知道Miles Bader开玩笑地说“生气”,但这是真的。我得出的结论是,比起C,更喜欢将项目用C++编写的任何程序员都可能是我真的愿意撒尿的程序员离开,这样他就不会来搞砸我参与的任何项目。

C++导致非常糟糕的设计选择。您总是开始使用该语言的“ Nice”库功能,例如STL和Boost,以及其他一些根本无法使用的功能,这些功能可能“帮助”您编程,但是会导致:

  • 当它们不起作用时会产生无穷的痛苦(而且任何告诉我STL尤其是Boost稳定且可移植的人都充满了BS,甚至都不有趣)

  • 效率低下的抽象编程模型,在过去的两年中,您注意到某种抽象不是很有效,但是现在您的所有代码都依赖于周围的所有Nice对象模型,并且如果不重写应用程序就无法修复它。

换句话说,做一个好的,高效的,系统级的和可移植的C++的唯一方法最终将自己限制在C语言中所有基本可用的东西上。而将项目限制在C语言中,则意味着人们不会拧,这也意味着您会得到很多真正了解底层问题并且不会因任何愚蠢的“对象模型”废话而搞砸的程序员。

因此,对不起,但是对于git这样的以效率为主要目标的事物,C++的“优势”只是一个巨大的错误。我们还激怒了看不见的人,这只是一个很大的额外优势。

如果要使用C++编写的VCS,请使用Monotone。真。他们使用“真实数据库”。他们使用“不错的面向对象的库”。他们使用“不错的C++抽象”。坦率地说,由于所有这些设计决定对某些CS人士来说都如此吸引人,最终结果是一团糟且难以维护的混乱。

但是我敢肯定,您比git更想要它。

      Linus
21
Jeremy Heiler

我认为没有任何令人信服的理由使用C++。如果要进行OO编程),则可以使用Python并在C中编写需要快速性能的部分。

编辑:还有其他语言可以很好地与C交互,因此,如果您不喜欢Python,则可以选择其他语言。

18
Larry Coleman

有使用C++的理由吗?当然。

有些人可能更喜欢使用C++而不是其他选项。询问是否有理由使用C++就像询问为什么我们需要数百种口味的冰淇淋。并非每个人都喜欢简单地坚持使用香草。

如果开发人员已经非常熟练使用C++,那么他们面临的问题可能不是“为什么使用它?”,而是“为什么不使用它”。现在SO)似乎正在流行这种反C++的事物,但不管您是否相信,并不是每个人都赞成它。有些人可能比其他语言更喜欢C++。

是否需要将C++用于应用程序?当然不是。但是,对于其他任何语言,也可以询问相同的确切问题。在极少数情况下,需要在应用程序中使用特定语言。

13
GrandmasterB

我只是从C切换到C++,即使您不需要模板和OOP,我也认为收益是可观的。

  • 更好的内存管理
  • 加强型系统
  • 更好的标准库
  • 命名空间
  • 等等.
12
Oliver Weiler

令我惊讶的是,还没有人提到它,但是C++向我们介绍了 references ,它几乎解决了指针的所有问题和陷阱:

void ModifyVar(int& var)
{
    var = 5;
}

int test = 4;
ModifyVar(test);

代替:

void ModifyVar(int * var)
{
    *var = 5;
}

int test = 4;
ModifyVar(&test);

更加安全,容易...而且没有价值传递的开销。

8
Nathan Osman

通常在哪里以及为什么会这样:

  • 熟识
  • 所需的语言功能
  • 您要使用的特定库
  • 性能要求

对于服务器端编程,您通常可以从编译或解释的多种不同语言中进行选择。通常,语言的选择取决于您或您的团队在哪个平台上最有效。或者,如果您还没有团队,那就是市场上技能的可用性。

附带说明一下,我真的不理解基于性能来决定使用C/C++(仅),因为许多脚本语言都可以在C/C++中进行扩展。您将获得快速开发语言的好处以及将慢速部分迁移到C/C++扩展的能力。当然,如果您在进行系统编程时,每个操作都可以理解,那是可以理解的,但是在大多数应用程序开发中,我都不了解。

5
dietbuddha

C++ vs Python vs Perl很难判断。这取决于项目和要求。

C++从很早以前就有大量的实用程序,可以在许多平台上运行。但是开始遍历流只是将String传递给Integer并反向进行是很痛苦的。

另一方面,C++对库的依赖项处理得很糟糕。一旦在GCC X或VC++ Y中进行了编译,就无法依靠代码将在这些工具的下一版本中运行。在Windows上也一样,在Unix上也一样。

Perl从Unix世界获得了强大的功能,但特别是作为正则表达式工具。这是大多数时候使用的。 Perl加上一些相当严肃的工具,甚至Java)也无法以正常方式做到(请查看如何将文件上传到Web服务器)),Perl就是“做到这一点”。

Python是简单,灵活和动态的语言。如此简单,您可以将整数发送给函数,脚本需要字符串,但可以得到结果!虽然出乎意料,但却是结果。因此程序员需要非常谨慎。 [〜#〜] idle [〜#〜] 提供了一些调试功能,但是当您已将TELNET连接到系统,或将SSH向下连接了三个级别,并且您想查找问题时,调试器不会在那里站在您旁边。但是,它可以快速完成一些出色的数学工作。

Java是一个由流行词,外来技术和流行词组成的生态系统,当您只想将文件上传到Web服务器时,就会发现只有在服务器具有 [〜#〜] jsp [〜# 〜] 。如果要调用系统库或诸如监视之类的系统功能,您会发现必须进行大量挖掘。也许到达 [〜#〜] jni [〜#〜] 然后确定...然后您想...“为什么,主啊?”

除此之外,Java是用于商务套件和多线程的出色工具,我非常喜欢它。

快速编写程序并向您的简历显示“哦,我也知道技术”,而您想要成为的老板也很惊讶!即使,可能也不需要该技术...(好吧,伙计们,我讨厌 Spring Framework ..。)

4
hephestos

Linux的?那么“面向对象的Pascal”或“ D”呢?

意见建议:

3
mramirez

选择语言时要牢记的一点是,使用该语言会带来什么好处,以及获得该语言需要花费多长时间。

python和Perl之类的语言之间的主要思想是,以更少的人工时间,却以更多的cpu时间来完成更多工作。实际上,您将花费更多的时间来编码python或Perl脚本,而不是将其执行,但是您的意思是正确的。

C/C++的优点是它们速度很快,但是要花语法和强大的键入功能:您必须自己做很多事情,以使计算机不必在编译时选择它。

当您编写代码时,某些行比其他行要运行得多,而这些行会带来问题。另一方面,其余所有代码(您花了很多时间编写的代码)的执行频率要低得多。您可能已经听过,但这是臭名昭著的80/20规则,您将无法绕过该规则。

解决此问题的方法是使用一种更简单的语言(通过更简单的语言,我的意思是对开发人员更友好:更少的键入,懒惰的解释,大量预先存在的例程和内容,等等)来完成所有代码。

与使用C或C++进行处理相比,这样做的速度如此之快,这会花费更多的脑部疼痛。

您的程序速度很慢,但是使用事件探查器,您可以隔离运行了80%的时间的零件,然后使用C或C++进行处理。

通过以这种方式进行编程,您节省了很多时间,并且程序效率高,速度快,泄漏内存的机会更少,并且节省了时间。

脚本语言被设计为在开发者方面,但是优化仍然是可能的。当然,您可以是设计模式的魔术师,STL伏都教徒,甚至是LISP战士,也许是Haskell的和尚。但是语言使我们与计算机对话,语言不是让我们成为计算机!

3
jokoon

我使用的C++称为带有类的C!

2
willspeak

一点都不。如果您不需要性能,并且有一个库可以使用其他语言,那么不必担心C/C++。如今,我只针对无法(轻松地)运行语言的嵌入式系统这样做。有时我使用C是因为我正在编写一个插件,但实际上没有。

但是,我不会使用Python,Perl等来避免使用C。我的偏好实际上是C#,因为我喜欢一个好的库(这是.NET的强项),而且我喜欢静态类型的语言。 是一个很好的选择。但实际上 (HaskellOCaml[〜#〜] d [〜#〜][〜#〜] ml [ 〜#〜] 这样都很好。

0
user2528

对于这样形成的所有问题,实际上只有一个答案。使用技术X而不是技术Y(其中X和Y大致处于同一水平(就像几乎所有当代编程语言一样))的最佳原因是因为您已经知道X并且不了解Y。

(但是Haskell到达后,没有理由使用其他任何东西)

0
vegai