C and C++ are not the same language.

In the web world, a world I’ve made my way back to in the last year, pointers are an exotic relic of the past.  Seriously.

I never got the memo; there was no going away party, I was just sitting there, minding my own structs and boom, next thing I know, I was that guy that hadn’t realized that those mallocs are so 90s.

But there is a certain reverence for the old ways.  You’re supposed to learn C or C++ (often just written as “C / C++”) to be a well rounded dork, much like you’re supposed to have read Shakespeare.  This is all fine and well, and I agree.  But there’s one sticking point.

C and C++ aren’t the same language.

Let’s first take a tour of the major reasons that are given for learning C or C++:

  • You learn how to do manual memory management
  • You get “closer to the hardware”
  • They offer performance benefits over most languages
  • There’s a wealth of code written in both that you may want to understand

All of these are true in both languages.

However, learning to think in C versus C++ is a completely different story.

Modern C++ is usually written in an object-oriented style.  Even modern C is usually written in a pseudo-object-oriented style using opaque types.  But the mechanisms for working with the two vary widely.  Notably, in C you have to build up the higher level abstractions on your own that are built into the language in C++.

It’s beyond the scope of this entry to get into the many differences between the two, but I decided to implement a common pattern in programming, the observer pattern in C, C++ and Java to illustrate the differences.

Observer Pattern in C:

#include <stdlib.h>
#include <stdio.h>
 
typedef void(* FoomaticListener)(void);
 
struct Foomatic
{
    FoomaticListener *listeners;
    int listener_count;
};
 
struct Foomatic *foomatic_create(void)
{
    return (struct Foomatic *) calloc(1, sizeof(struct Foomatic));
}
 
void foomatic_destroy(struct Foomatic *foomatic)
{
    free(foomatic->listeners);
    free(foomatic);
}
 
void foomatic_add_listener(struct Foomatic *foomatic, FoomaticListener listener)
{
    int count = ++foomatic->listener_count;
 
    foomatic->listeners =
        (FoomaticListener *) realloc(foomatic->listeners,
                                     sizeof(FoomaticListener) * count);
 
    foomatic->listeners[count - 1] = listener;
}
 
void foomatic_activate(const struct Foomatic *foomatic)
{
    int i = 0;
 
    for(i = 0; i < foomatic->listener_count; i++)
    {
        (*foomatic->listeners[i])();
    }
}
 
static void first_listener(void)
{
    printf("Whoopee.\n");
}
 
static void second_listener(void)
{
    printf("Whoopee.\n");
}
 
int main(void)
{
    struct Foomatic *foomatic = foomatic_create();
 
    foomatic_add_listener(foomatic, first_listener);
    foomatic_add_listener(foomatic, second_listener);
 
    foomatic_activate(foomatic);
 
    foomatic_destroy(foomatic);
 
    return 0;
}

Observer Pattern in C++:

#include <set>
#include <iostream>
 
class Foomatic
{
public:
    class Listener
    {
    public:
        virtual void activate() = 0;
    };
 
    void addListener(Listener *listener)
    {
        m_listeners.insert(listener);
    }
 
    void activate()
    {
        for(ListenerSet::const_iterator it = m_listeners.begin();
            it != m_listeners.end(); ++it)
        {
            (*it)->activate();
        }
    }
 
private:
    typedef std::set<listener *> ListenerSet;
    ListenerSet m_listeners;
};
 
class FooListener : public Foomatic::Listener
{
public:
    virtual void activate()
    {
        std::cout << "Whoopee." << std::endl;
    }
};
 
int main()
{
    Foomatic foomatic;
    FooListener first;
    FooListener second;
 
    foomatic.addListener(&first);
    foomatic.addListener(&second);
 
    foomatic.activate();
 
    return 0;
}

Observer Pattern in Java:

Foomatic.java

import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
 
public class Foomatic
{
    private Set<listener> listeners;
 
    public interface Listener
    {
        public void activate();
    }
 
    public Foomatic()
    {
        listeners = new HashSet<listener>();
    }
 
    public void addListener(Listener listener)
    {
        listeners.add(listener);
    }
 
    public void activate()
    {
        Iterator<listener> it = listeners.iterator();
 
        while(it.hasNext())
        {
            it.next().activate();
        }
    }
}

Callback.java

class FooListener implements Foomatic.Listener
{
    public void activate()
    {
        System.out.println("Whoopee.");
    }
}
 
public class Callback
{
    public static void main(String [] args)
    {
        Foomatic foomatic = new Foomatic();
        FooListener first = new FooListener();
        FooListener second = new FooListener();
 
        foomatic.addListener(first);
        foomatic.addListener(second);
 
        foomatic.activate();
    }
}

All three of these do the same thing (with the one proviso that the C version uses a simple list rather than a set for concision).

The first thing that should jump out at you is that two of these look very similar.  And it’s not the C and C++ versions.  Modern C++ is much more similar to Java than it is to C.  Learning to think in C++ is much closer to learning to think in Java.

In the C++ and Java examples a callback is achieved by defining an interface with an abstract method that’s implemented in a concrete subclass.  In C a function pointer is used.

Now, let’s get back to the root of the confusion.

C++ is backwards compatible to C.  Good C++ developers, especially those doing systems programming, tend to be familiar enough with the important parts of C to exploit its lower-level primitives and pack them into an object oriented structure.

But someone working on, say, GUI development in C++ might never come into contact with the C underworld.  You can be a goodly C++ developer and never use function pointers, rarely use macros (and then rarely in a particularly interesting way) and most of all be completely clueless on how to define a reasonable C API that does proper encapsulation.

Really knowing a programming language is much more about knowing how to wield it to solve problems rather than being able to write code that the compiler doesn’t barf on.

Let’s look back at the goals we had for learning one of these languages:

You learn to do manual memory management

In C++ memory management is often less opaque and handled by abstraction layers.  We didn’t have to think about memory being allocated to insert an element to a set or free it when we were done.  That was handled by the language and its standard library.  This holds somewhat generally for C++, so I believe if your goal is educational — simply to learn about memory management, C is probably closer to fitting the bill.

You get closer to the hardware

Again, C is probably a win.  Not because you can’t do systems programming in C++ (in fact, that’s most of what I do) but because when doing systems programming in C++ it tends to come out looking like blocks of C neatly organized into classes.  The bulk of the code that you can also use as a learning reference (glibc and the Linux kernel are both good here) is written in C.

They offer performance benefits over other languages

This is true for both, but C forces most of the time-consuming stuff to pass in front of your eyes.  There’s less magic happening behind the scenes.  When writing performance critical C++ understanding that it’s built on the same runtime as C is useful for understanding what’s actually happening when you call a virtual function.  (Answer:  Classes which have virtual functions have a “vtable” that’s created by the compiler which is simply an array of function pointers.)

There’s a wealth of code written in both that you may want to understand

This naturally has less of a clear winner.  C tends to be more dominant at the lower levels, C++ creeps in closer to the middle of the technology stack.  Systems libraries and kernels are usually written in C, things like web browsers and office suites are more often C++.

But wait … so I said all of that nice stuff about C, why do I still prefer C++?

If your goals are purely educational, C is probably a better fit.  At an atomic level it’s harder to understand, but there’s much less of it to understand.  C++’s syntax is everything from C plus a myriad of advanced compiler-fu that takes a good long while to get your head around.  Many C++ programmers who have been working with the language for half a decade still couldn’t tell you how to use partial template specialization.

But if you’re writing real code — something you’re going to be curling up with night after night until unemployment do you part, I’ll take C++ any day of the week.

If you want to learn about the innards of programming, filleting a program in C teaches you how.  If 95% of the time, you’d like to have that handled with the abstractions you’re working with, classes, templates, exceptions and other modern encapsulation mechanisms supported by C++ make working on a large code-base more palatable.  I’ve been writing C for 15 years and in the first version of the C example above, I forgot to free the list of function pointers.  C++ is also more concise and expressive.

Now, anticipating the reaction of the high-level crew, aren’t most of the arguments that I just made for C++ even more true for, say, Python, or Ruby?

Of course.  But C++ often hits a sweet-spot between languages where high-level abstractions are available with the raw power of C when you need it.

At Directed Edge we use a mix of languages, and even a mix of C++ styles, trying to hit their comparative sweet-spots.  The rough break down is:

  • Database:  C-ish C++.  Looks like C packaged into classes and mostly is.  Binary operators for the win.
  • Engine:  More “pure” C++.  Less low-level gobblety-gook.  Works mostly with abstractions built up in the database level.  Optimized like it’s nobody’s business.
  • Web Services:  Java.  It’s hardly the cool-kid on the block these days, but does well with throughput and fault-tollerance (which C and C++ are bad at).
  • Web Front-ends:  Ruby.  Horrible performance, but rapid development time.

30 Comments

  1. Lam Luu:

    One thing that I cannot understand is why you would want to work with C++. It is a horrible language whose designers are obsessed with adding more and more features. If you want OOP (not that it is a good thing), go for something higher than C++, like Java, Python, etc. If you want performance or low level, write snippets of C, then call it through foreign function interface of some sorts. In the end, there is no reason for learning C++, except that C code happens to work with C++ compiler.

  2. Scott Wheeler:

    @Lam I don’t think it makes a lot of sense to get into taste, since that’s a debate that will be going strong long after everyone has forgotten this post, so I’ll stick to some objective positive and negative stuff:

    – C++ does have a crazy number of features, and that is both what makes it powerful and unwieldy.

    – C++ does provide tighter integration with low-level stuff and more performance than you’ll generally get out of higher-level languages. Sure, you can call into C from most any language, but it’s more natural in C++.

    – There’s a huge amount of C++ out there, like it or not. There will continue to be millions of lines of C++ behind desktop applications for a good while to come. It’s not going to be wholesale rewritten.

  3. df:

    Lam luu,

    You obviously didn’t even read the text 100 pixels above your comment. I would guess that you “can’t understand” because of organic issues not related to the merits of the C++ vs anything debate.

    Scott’s article is very well reasoned and that bulleted list that I mentioned, a few pixels above your comment, shows that Directed Edge are pragmatic about using the right language in the right context.

    Nice work Scott.

  4. John Haugeland:

    Respectfully, the reason your C++ implementation looks so much like your Java implementation is that you’ve written the natural Java version in C++. It’s no different than when C people write C++.

    The natural C++ implementation is a mixin through multiple inheritance, so that it’s reusable across classes. Not only is the end result terser, more readable and more reusable, but it’s also much easier to test, to verify, and helps draw the line between the observer plumbing and the actual observer. Alexandrescu policies are another viable approach, or one could write it as a template into which the actual observer is placed as type parameterization.

    The three implementations should all look different. Java is just as foreign to C++ as C is; it’s a language which discards useful tools because, direct quote, “they’re too difficult.”

  5. zid:

    “C++ is backwards compatible to C.”

    No, it isn’t. see: sizeof(‘a’), void *

  6. Ravi Mohan:

    I found this via HN and just wanted to say this is a *very* well written article.

  7. oblivionboy:

    I think Objective-C wins this one. Its strictly a super set of C, and makes alot more sense than C++. There is style and taste, and then there is just a better language.

  8. Ben:

    Here come the pedants… *glares at zid*

  9. Scott Wheeler:

    Hi John —

    I find that there are broadly a couple of schools in the C++ world. For convenience I’ll label them the Qt and Boost camps. I come decidedly from the former (having been a KDE developer many years). The Boost world is certainly more distinctively C++, whereas the Qt side of the fence tends to use less generic programming. That said, Java is far and away the language of the three that I’ve done the least of and the one that I learned last, so any Java-esque influences come at one remove.

  10. abc:

    @oblivionboy, Objective-C is not a “strict superset of C”.

    “Foo *bar; bar.visible = YES;” is valid Obj-C, but is invalid C.

  11. Scott Wheeler:

    @abc That would make it not a strict subset; that doesn’t invalidate it being a superset.

  12. pyrtsa:

    Might this be a more Boost-camp-like implementation of the same? http://codepad.org/3xRQNnWB

  13. Bojan:

    > throughput […] (which C and C++ are bad at)

    Care to elaborate?

  14. Filox:

    @ben Here come the people who don’t understand the difference between stdcall and cdecl.

  15. Scott Wheeler:

    @Bojan Sorry, I meant that C / C++ are bad at fault-tolerance relative to Java. They’re fine with throughput. For fault-tolerance, Java’s oft-hated (and reasonably so) heavy use of exceptions does make error recovery more reliable.

  16. Danijel:

    @Scott referring to @Bojan, And memory management, ie garbage collection.

  17. remek:

    I agree with Lam Luu. Future of C++ is dark. I do like C, I don’t like C++, I do see a position of C in programming realm, I do not see a position of C++ in programming realm. …as well as Bruce Eckel: http://www.artima.com/weblogs/viewpost.jsp?thread=252441

  18. Peter:

    You confuzled. C++ is much (10-20%) slower than C, and much larger. Once you use RTTI, you get overhead for each object created. Buncha other hidden things like that happen too for exceptions and other features. C++ isn’t there for performance. C really isn’t either anymore — VM languages are now faster.

    C is good cross-platform assembly. C++ sucks at that. C++ is rapidly obsoleting, but was a good general-purpose language.

  19. markus:

    I think that the problem is fundamentally this:

    These days people will easily pick up a “scripting language”. Now, which faster language to use? C? C++? Java? D?

    The answer is not important. What I want to point out is that it will be a LOT easier for them to PICK JUST ONE.

    Someone who will use C, will hardly use C++ AND a scripting language.

    We have too many languages already to pick upon, so if I concentrate on just two languages, I will use a language I want to use for the next … 20 or so years.

    Personally I avoid C++ – I think the OOP approach of C++ is cleaner than C, but I dont want to spend years of learning everything there is in C++. I already solve all my problems using scripting languages, except for lower stuff, but there I can pick C.

    I believe C++ in the long run will simply lose out to other languages, and scripting languages will profit enormously especially if they can be combined easily with the “faster” languages.

  20. Scott Wheeler:

    @Peter

    “C++ is slower” — not inherently, and this is naturally compiler dependent. But when writing high-performance C++, it does help to know how the features are implemented (e.g. what they’d look like in C) when using them. Note the benchmark here where C++ does the same or better than C in almost every category.

    “RTTI overhead” — hasn’t really been a problem since GCC 2.95. Post 2.95 it’s been placed in a separate section of the binary and of course you can disable RTTI with compilation flags if you really care.

    “C really isn’t either anymore. VM…” — that’s rarely the case. Occasionally JIT compilers will oust compiled languages, but not often. In benchmarks that we did internally comparing the numerics core of our recommendations engine in Java vs. C++, C++ was 2.5x faster.

  21. Zwieback:

    One thing is undeniable: of the popular languages C++ covers the most ground. I can write anything from device drivers to Enterprise apps to CAD programs in C++. Is it the best choice for any one of these? Maybe not but it comes close enough that it’s still a good choice for a first language.

  22. Kman:

    given that a c++ compiler supports the c99 keyword “restrict” and that a program was written with it in mind, 100% of the time c++ will be faster than anything running on a VM. c++ compilers have a hard time optimizing pointer related logic, namely aliasing (stores affecting loads), but with “restrict” this is no longer an issue. of course if one is really after performance, i’m sure most compilers support their own extension to c++, such as __declspec() in msvc++. with these expensions, the generated assembly will be very close to what one might hand-write.

  23. Ben:

    I mentioned this on HN, but I just wanted to support what you said about memory management being more of a hassle in C. Case in point, either you only freed one of your listeners in your C code (memory leak) or I’m still a dufus with C memory management.

    Interesting post though. I’m reading C, Interfaces and Implementation and I find it fascinating to see C as C programmers C it, instead of someone more from C++ land.

  24. Brad:

    Very nice article. I have to agree with your choice of C++. I came to the same decision a few years ago. I’m in the boost camp BTW 🙂 At the same time, I can understand why folks dislike C++. It’s powerful and complex and intimidating and thus easy to screw-up at times. I encourage folks to try it and only use the parts they need (it’s a big language) to build and ship a product. It’s been my experience that once they do that, they love it too.

  25. Scott Wheeler:

    @Ben — the listeners in the C example aren’t created by malloc — they’re just pointers to functions, so they don’t need to be freed. The thing being freed there is the array of pointers to those functions, which is created with the realloc call, and there, in fact, one free suffices.

  26. Walter:

    Nobody would write the Java iterator like that. This is more idiomatic:

    for(Listener listener : listeners)
    {
    listener.activate();
    }

    There’s no need to have a concrete FooListener class, nor a separate class to run it. Just put this in Foomatic.java and get rid of Callback.java altogether:

    /* … */
    public static void main(String[] args)
    {
    Listener listener = new Listener()
    {
    public void activate()
    { System.out.println(“Whoopee.”);
    }
    }

    Foomatic foomatic = new Foomatic();
    foomatic.addListener(listener);
    foomatic.addListener(listener);
    foomatic.activate();
    }

  27. Joseph Garvin:

    In addition to John Haugeland’s comments, I would add that anywhere a C programmer uses a function pointer, instead of doing something wildly different a C++ programmer may simply use a boost::function object. What you are calling “modern” C++ is not actually modern C++. I suggest reading the book ironically titled, “Modern C++”. Amazon link: http://www.amazon.com/Modern-Design-Programming-Patterns-Depth/dp/0201704315

  28. Bojan:

    > @Scott referring to @Bojan, And memory management, ie garbage collection.

    The big bad wolf.

    You have to have a bit of perspective on this subject. E.g. more often than not in my line of work manual memory management (optimized for particular usage pattern) has given me tremendous performance boost and predictable heap growth figures.
    Yes, writing everyday enterprisy frontends and whatnot benefits greatly from automated memory management, no argument there. Fire-and-forget is usefull.
    But I like to have a choice. Using a subset of boost libraries with a bit of RAII can significantly ease the pain of fiddling with memory management in C++, although it still requires a lot of discipline (but then again, it’s not like Java or any other VM out there will let you get away with reckless coding).

  29. COdE fr3@K:

    Observer Done Differently…

    In a recent post of Scott Wheeler’s (C and C++ are not the same language), he talked about differences between C and C++, and applications of different programming languages (C, C++, Java, Ruby) in his company.
    In the post, Wheeler implemented observe…

  30. coder:

    Thanks for this, very informative and helpful. Also thanks for posting the source code example! 🙂

Leave a comment