170

As a Linux (server side) developer, I don't know where and why should I use C++.

When I'm going for performance, the first and last choice is C.

When "performance" isn't the main issue, programming languages like Perl and Python would be good choices.

Almost all open source applications I know in this area have been written in C, Perl, Python, Bash script, AWK or even PHP, but no one uses C++.

I'm not discussing other areas like GUI or web applications, I'm just talking about Linux, CLI and daemons.

Is there any satisfactory reason to use C++?

Omid
  • 103
Ehsan
  • 853
  • 3
  • 8
  • 9

18 Answers18

312

When I'm going to performance, the first and last choice is C.

And that’s where you should back up. Now, I cannot, at all, speak for server development. Perhaps there is indeed no compelling reason to prefer C++ over the alternatives.

But generally speaking, the reason to use C++ rather than other languages is indeed performance. The reason for that is that C++ offers a means of abstraction that has, unlike all other languages that I know, no performance overhead at runtime.

This allows writing very efficient code that still has a very high abstraction level.

Consider the usual abstractions: virtual functions, function pointers, and the PIMPL idiom. All of these rely on indirection that is at runtime resolved by pointer arithmetic. In other words, it incurs a performance cost (however small that may be).

C++, on the other hand, offers an indirection mechanism that incurs no (performance) cost: templates. (This advantage is paid for with a (sometimes hugely) increased compile time.)

Consider the example of a generic sort function.

In C, the function qsort takes a function pointer that implements the logic by which elements are ordered relative to one another. Java’s Arrays.sort function comes in several variants; one of them sorts arbitrary objects and requires a Comparator object be passed to it that works much like the function pointer in C’s qsort. But there are several more overloads for the “native” Java types. And each of them has an own copy of the sort method – a horrible code duplication.

Java illustrates a general dichotomy here: either you have code duplication or you incur a runtime overhead.

In C++, the sort function works much like qsort in C, with one small but fundamental difference: the comparator that is passed into the function is a template parameter. That means that its call can be inlined. No indirection is necessary to compare two objects. In a tight loop (as is the case here) this can actually make a substantial difference.

Not surprisingly, the C++ sort function outperforms C’s sort even if the underlying algorithm is the same. This is especially noticeable when the actual comparison logic is cheap.

Now, I am not saying that C++ is a priori more efficient than C (or other languages), nor that it a priori offers a higher abstraction. What it does offer is an abstraction that is very high and incredibly cheap at the same time so that you often don’t need to choose between efficient and reusable code.

173

I see way too many C programmers that hate C++. It took me quite some time (years) to slowly understand what is good and what is bad about it. I think the best way to phrase it is this:

Less code, no run-time overhead, more safety.

The less code we write, the better. This quickly becomes clear in all engineers that strive for excellence. You fix a bug in one place, not many - you express an algorithm once, and re-use it in many places, etc. Greeks even have a saying, traced back to the ancient Spartans: "to say something in less words, means that you are wise about it". And the fact of the matter, is that when used correctly, C++ allows you to express yourself in far less code than C, without costing runtime speed, while being more safe (i.e. catching more errors at compile-time) than C is.

Here's a simplified example from my renderer: When interpolating pixel values across a triangle's scanline. I have to start from an X coordinate x1, and reach an X coordinate x2 (from the left to the right side of a triangle). And across each step, across each pixel I pass over, I have to interpolate values.

When I interpolate the ambient light that reaches the pixel:

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

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

When I interpolate the color (called "Gouraud" shading, where the "red", "green" and "blue" fields are interpolated by a step value at each pixel):

  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;

When I render in "Phong" shading, I no longer interpolate an intensity (ambientLight) or a color (red/green/blue) - I interpolate a normal vector (nx, ny, nz) and at each step, I have to re-calculate the lighting equation, based on the interpolated normal vector:

  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;

Now, the first instinct of C programmers would be "heck, write three functions that interpolate the values, and call them depending on the set mode". First of all, this means that I have a type problem - what do I work with? Are my pixels PixelDataAmbient? PixelDataGouraud? PixelDataPhong? Oh, wait, the efficient C programmer says, use a union!

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

..and then, you have a function...

  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;
          }
...

Do you feel the chaos slipping in?

First of all, one typo is all that is needed to crash my code, since the compiler will never stop me in the "Gouraud" section of the function, to actually access the ".a." (ambient) values. A bug not caught by the C type system (that is, during compilation), means a bug that manifests at run-time, and will require debugging. Did you notice that I am accessing left.a.green in the calculation of "dgreen"? The compiler surely didn't tell you so.

Then, there is repetition everywhere - the for loop is there for as many times as there are rendering modes, we keep doing "right minus left divided by steps". Ugly, and error-prone. Did you notice I compare using "i" in the Gouraud loop, when I should have used "j"? The compiler is again, silent.

What about the if/else/ ladder for the modes? What if I add a new rendering mode, in three weeks? Will I remember to handle the new mode in all the "if mode==" in all my code?

Now compare the above ugliness, with this set of C++ structs and a template function:

  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;
      }
  }

Now look at this. We no longer make a union type-soup: we have specific types per each mode. They re-use their common stuff (the "x" field) by inheriting from a base class (CommonPixelData). And the template makes the compiler CREATE (that is, code-generate) the three different functions we would have written ourselves in C, but at the same time, being very strict about the types!

Our loop in the template cannot goof and access invalid fields - the compiler will bark if we do.

The template performs the common work (the loop, increasing by "step" in each time), and can do so in a manner that simply CAN'T cause runtime errors. The interpolation per type (AmbientPixelData, GouraudPixelData, PhongPixelData) is done with the operator+=() that we will add in the structs - which basically dictate how each type is interpolated.

And do you see what we did with WorkOnPixel<T>? We want to do different work per type? We simply call a template specialization:

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


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

That is - the function to call, is decided based on the type. At compile-time!

To rephrase it again:

  1. we minimize the code (via the template), re-using common parts,
  2. we don't use ugly hacks, we keep a strict type system, so that the compiler can check us at all times.
  3. and best of all: none of what we did has ANY runtime impact. This code will run JUST as fast as the equivalent C code - in fact, if the C code was using function pointers to call the various WorkOnPixel versions, the C++ code will be FASTER than C, because the compiler will inline the type-specific WorkOnPixel template specialization call!

Less code, no run-time overhead, more safety.

Does this mean that C++ is the be-all and end-all of languages? Of course not. You still have to measure trade-offs. Ignorant people will use C++ when they should have written a Bash/Perl/Python script. Trigger-happy C++ newbies will create deep nested classes with virtual multiple inheritance before you can stop them and send them packing. They will use complex Boost meta-programming before realizing that this is not necessary. They will STILL use char*, strcmp and macros, instead of std::string and templates.

But this says nothing more than... watch who you work with. There is no language to shield you from incompetent users (no, not even Java).

Keep studying and using C++ - just don't overdesign.

ttsiodras
  • 201
  • 1
  • 2
  • 4
81

RAII for the win baby.

Seriously, deterministic destruction in C++ makes code much clearer and safer with no overhead whatsoever.

Motti
  • 323
  • 2
  • 8
71

Is there any satisfiable reason to use C++?

  1. Templates and the STL. You trade a little build time (and some potentially incomprehensible error messages) for a lot of useful abstraction and labor-saving tools, with no appreciable run-time performance hit (although the binary footprint may be a little larger).

    It takes a while to wrap your head around (took me a couple of years before it clicked), but once you do it can make life a lot simpler.

  2. Text processing in C++ is orders of magnitude less painful than it is in C.

John Bode
  • 11,004
  • 1
  • 33
  • 44
41

Yes.

If you're looking for executable efficiency, you're down to C or C++, so I'll focus on that.

Even before templates were common, I preferred using C++ over C for the kinds of executables you discuss as early as the mid 1990s for two very simple reasons: object polymorphism and RAII.

I've used polymorphic C++ objects for all kinds of interesting things. For instance, I was working on an embedded Linux system with frame buffer overlays on OMAP and XScale ARM CPUs. The two hardware architectures have different overlay features with very different APIs. I used a common virtual "Overlay" base class to expose an idealized view of overlays, and then wrote "OmapOverlay" and "XScaleOverlay" classes which were instantiated appropriately at runtime, depending on which architecture the code detected it was running on.

To oversimplify, RAII is the idea that you allocate resources connected to an object during the object's constructor, or maybe later on in the object's lifetime, and the resources get deallocated or released in the object's destructor. That's really nice in C++, because objects which are automatic variables are destructed when they go out of scope. For someone who's equally competent in C and C++, it's far easier to avoid resource and memory leaks in C++. You also don't see much C++ code with the very common C meme of a label at the end of a function preceding a bunch of calls to free(), and various goto's in the function block jumping there.

I'm fully aware that you can do all these things with C - it's just a lot more work, way more lines of code, and what you wind up with is a lot uglier and often harder to understand. There's polymorphism code all through the X server internals, and man, is it fugly and weird and frequently hard to trace through.

I also do a lot of work with GNOME technologies like GTK+ and Clutter, all of which are written in C using the GObject system. GObject is like the C++ object system with the nice cover taken off and all the ugly internals exposed, and it usually requires a half-dozen lines of code to do what a one-line C++ method call would do. I'm currently writing some ClutterActors, and while the math is really interesting, I'm constantly thinking, "This would all be so much more succinct and comprehensible in C++".

I also often think, "You know, if I were writing this in C++ instead of C, I'd be out in the living room watching MythBusters with my wife instead of sitting in my office at 9 PM."

Bob Murphy
  • 16,098
29

C++ is about as fast as C (some things are faster, some slower), and it offers better abstractions and organization. Classes work similarly to primitive types, allowing large amounts of code to be used without being held in mind. Operator overloading and templates make it possible to write code that functions better if data representations change. Exceptions can allow easier error-handling. The compiler can be used to check more stuff at compile-time.

The price you pay for this is a fairly nasty learning curve, and it's easier to make subtle mistakes in it than most other languages I'm familiar with.

So, I can't tell whether it would be worthwhile for you to learn it for what you're doing now. You can certainly get by with combinations of Python or Perl and C, but C++ offers both abstraction and performance in one hard-to-get-used-to package.

21

According to Linus, no :

When I first looked at Git source code two things struck me as odd: 1. Pure C as opposed to C++. No idea why. Please don't talk about portability, it's BS.

YOU are full of bullshit.

C++ is a horrible language. It's made more horrible by the fact that a lot of substandard programmers use it, to the point where it's much much easier to generate total and utter crap with it. Quite frankly, even if the choice of C were to do nothing but keep the C++ programmers out, that in itself would be a huge reason to use C.

In other words: the choice of C is the only sane choice. I know Miles Bader jokingly said "to piss you off", but it's actually true. I've come to the conclusion that any programmer that would prefer the project to be in C++ over C is likely a programmer that I really would prefer to piss off, so that he doesn't come and screw up any project I'm involved with.

C++ leads to really really bad design choices. You invariably start using the "nice" library features of the language like STL and Boost and other total and utter crap, that may "help" you program, but causes:

  • infinite amounts of pain when they don't work (and anybody who tells me that STL and especially Boost are stable and portable is just so full of BS that it's not even funny)

  • inefficient abstracted programming models where two years down the road you notice that some abstraction wasn't very efficient, but now all your code depends on all the nice object models around it, and you cannot fix it without rewriting your app.

In other words, the only way to do good, efficient, and system-level and portable C++ ends up to limit yourself to all the things that are basically available in C. And limiting your project to C means that people don't screw that up, and also means that you get a lot of programmers that do actually understand low-level issues and don't screw things up with any idiotic "object model" crap.

So I'm sorry, but for something like git, where efficiency was a primary objective, the "advantages" of C++ is just a huge mistake. The fact that we also piss off people who cannot see that is just a big additional advantage.

If you want a VCS that is written in C++, go play with Monotone. Really. They use a "real database". They use "nice object-oriented libraries". They use "nice C++ abstractions". And quite frankly, as a result of all these design decisions that sound so appealing to some CS people, the end result is a horrible and unmaintainable mess.

But I'm sure you'd like it more than git.

      Linus
Matthieu
  • 4,599
Jeremy
  • 4,791
19

I don't think there's any compelling reason to use C++. If you want to do OO programming, you can use Python instead and write the parts that need fast performance in C.

EDIT: There are other languages that interface well with C, so if you don't like Python, there are alternatives.

12

Is there a reason to use C++? Certainly.

Some people might simply prefer using C++ over other options. Asking if there's a reason to use C++ is like asking why we need to have hundreds of flavors of ice cream. Not everyone likes simply sticking with Vanilla.

If developers already are very competent with C++, the question for them may not be 'why use it?', but rather 'why not?'. There seems to be this trendy anti-C++ thing going on in SO right now, but believe it or not, not everyone subscribes to that. Some people may simply like C++ better than the other languages.

Does C++ need to be used for apps? Of course not. But this same exact question can also be asked for any other language. There are very, very few cases where a particular languages needs to be used for an application.

GrandmasterB
  • 39,412
12

I'm just switching from C to C++, and I think the gain is considerable, even if you don't have a need for templates and OOP.

  • Better memory management
  • Stronger type system
  • A better standard library
  • Namespaces
  • etc.
7

I'm surprised nobody has mentioned this yet, but C++ introduced us to references, which almost solve all of the problems and pitfalls of pointers:

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

int test = 4;
ModifyVar(test);

Instead of:

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

int test = 4;
ModifyVar(&test);

Much safer and easier too... and without the overhead of passing-by-value.

5

The where and why are usually going to be:

  • familiarity
  • desired language features
  • specific libraries you want to use
  • performance requirements

For server side programming you can often choose from a myriad of different languages, compiled or interpreted. Usually the choice of language is driven by which platform you or your team will be most effective on. Or if you don't yet have a team, the availability of skills in the market.

On a side note I don't really understand deciding to use C/C++ based on performance (only) since many scripting languages are extensible with C/C++. You get the benefit of a rapid development language coupled with the ability to migrate the slow portions into C/C++ extensions. Certainly if your doing systems programming where every op counts it's understandable, but in most app development I don't get it.

4

C++ vs Python vs Perl can't be judged easily. It depends on the project and requirements.

C++ has an arsenal of utilities from long ago, running in many platforms. But it is painful to start walking through streams for just passing String to Integer and reverse.

C++ on the other hand, has an awful deal with dependencies on libraries. Once you compile something in GCC X or VC++ Y, then you can't rely that the code will run by the next version of those tools. The same hell is on Windows, the same hell is on Unix too.

Perl, takes its power from Unix world but especially as a regular expression tool. This is what it is used most of the time. Along with some pretty serious tools that even Java can't do it in a normal way (check out how to upload a file to a web server), Perl is "just do it".

Python is easy, flexible and a dynamic language. So easy that you can send an integer to a function, the script expects string, yet you can have a result! Unexpected though, but a result. So it the programmer needs to be very cautious. IDLE offers some debugging, but when you have TELNET'ed to a system, or SSH'ed in three levels down and you want to find your problem, the debugger won't be there to stand next to you. But, it can do some great math work fast.

Java is an ecosystem of buzzwords, alien technologies, and big words and when you want to just upload a file to webserver, you find that you can do it only if the server has JSP. If you want to call system libraries or system functions like monitoring you find that you have to dig out a lot. And perhaps to reach JNI and OK...you think then..."Why, Lord?"

Apart from that, Java is a great tool for business suites, and multithreading, I liked it a lot.

Fast to make a program and show to your CV "Oh, I know that technology too" and your want-to-be boss, be amazed! Even though, the technology might not be that needed... (OK, folks, I hate the Spring Framework....)

hephestos
  • 103
3

Linux? What about "object oriented Pascal" or "D"?

Suggestions:

3

The thing to keep in mind while choosing a language, is what benefit you will get from using it, and how long it will take to get it.

The main idea between languages like python and perl is to do more with less man time, but with more cpu time. In fact you will spend more time coding a python or perl script than it will be executed, but you get my point.

The advantage of C/C++ is that they are fast, but at a cost of syntax and strong typing: you have to do a lot of thing yourself so that the computer has not to choose it at compile time.

When you make a code, some lines will be ran a lot more than others, and those lines are the one that pose a problem. On the other hand, all the rest of the code, the one you spent a lot of time on, is executed much much less often. You might have heard it, but it is the infamous 80/20 rule, and you won't be able to bypass such rule.

The solution to this problem, is to use an easier language (by easier I mean more developer friendly: less typing, lazy interpretation, a lot of preexisting routines and stuff, etc.) to do all your code.

You will do it so quickly compared to if you would have done it with C or C++, it would have taken much more brain ache.

Your program will be slow, but with a profiler, you isolate the part which are ran 80% of the time, and you do them with C or C++.

By prorgamming this way, you saved a lot of time, and your program is as efficient, as much fast, has much less chances to leak memory, and you saved time.

Scripting languages were designed to be on the side of the developer, but optimisation is still possible. Of course you can be a design pattern magician or a STL voodoo or even a lisp warrior, and maybe an haskell monk. But languages makes us talk to computers, languages are not made for us to BE computers !

jokoon
  • 2,280
2

The C++ I use is called C with classes!

0

No, not at all. If you don't need the performance and there is a library you can use the other language then don't bother with C/C++. I only do it nowadays when I target embedded systems that can't (easily?) run languages. Sometimes I use C because I am writing a plugin but really no.

However, I wouldn't use Python, Perl, etc. to avoid using C. My preference is actually C#, because I like a good library (which is a strength of .NET), and I like statically typed languages. Boo is a good alternative. But really Haskell, OCaml, D, ML and such are all fine.

0

There's actually a single answer to all questions formed like this. The best reason to use tech X instead of tech Y (where X and Y are roughly on the same level [like just about all contemporary programming languages are]) is because you already know X and don't know Y.

(but after Haskell arrived, there has been no reason to use anything else)

vegai
  • 693
  • 3
  • 8