May 21, 2009, 3:19 am by Scott Wheeler

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.

" ) ; } static void second_listener ( void ) { printf ( "Whoopee.

" ) ; } 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 ; } #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.

"); } static void second_listener(void) { printf("Whoopee.

"); } 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 ; } #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 ( ) ; } } } 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 ( ) ; } } 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: