but for most programmers, even game programmers, Im not really sure what they'd get out of it?
Classes are nothing more than a bunch of struct with function pointers and a "this" first formal parameter?
More seriously, I find that using pointers/references to design smart data structures is a quickly dying art.
I once had the problem of storing an automaton in Java. It has states and directed edges with an event name.
You must be able to walk forward and backward from state to state, over the edges (using the event name as a filter which edges to consider).
There are a LOT of states, several 100,000 at least, preferably 1,000,000 or more. Branching factor is about 3-6 (each state has only a few outgoing edges). Once created, the structure is quite stable, removal of states and edges must be possible but doesn't need to be fast. Walking over states and edges (both forward and backward) must be very fast.
Now if you solve this in normal Java or modern C++, you'll end up with a List<Edge> outgoing edges from each state, and a List<Edge> incoming edges into each state. So for each state which is otherwise empty, you have two lists. For each edge which has a pointer to the successor state and one to the predecessor state, and an event name, you add two list elements (one in the outgoing list of the predecessor state, and one in the incoming list of the successor state). For N states and M edges, you thus get 2N lists, and 2M list elements. In other words, 2/3 of the objects in memory is list administration. Since states and edges are very small, I am throwing away 2/3rd of my memory here.
(Note: Realizing this implies you understand how List<Edge> works!!)
In addition, iterating over edges to find eg successor states means you iterate over the outgoing list elements, dereference to the edge, and then use the successor pointer of the edge to find the next state. That's two dereferences for each edge traversal.
Now the C-ish variant:
struct State {
Edge *outgoing; // Head of a single linked list to outgoing edges
Edge *incoming; // Head of a single linked list to incoming edges
};
struct Edge {
Event *name; // Event name
State *successor; // Successor state
State *predecessor; // Predecessor state
Edge *next_outgoing; // Single linked list to next outgoing edge of the predecessor state
Edge *next_incoming; // Single linked list to next incoming edge of the successor state
};
I literally iterate over the edges from a state through a single linked list. I kill all List<Edge> objects, and all list elements in them, at the cost of two pointers to next edges in each Edge. This gives me almost 3 times as much space for my states and edges. I save one dereference (from list element to the edge object), making iterating twice as fast.
I would say, this are significant savings that you can achieve. Anyone who hasn't used single linked lists or double linked lists at the level of C (adding pointers in the struct himself) is never going to find such a solution.
I agree this is an extreme example and you don't need these things every day, but experiencing C can open your eyes to better solutions.