...
I appreciate this advice, but I'd probably still advise people first learn to use STL/RAII/etc (
before going back to keeping it simple) 
Personally, I learned C++ first in a "
c with classes" way, then pure C++ with STL/boost/metaprogramming, then back to something more like "
c++ with structs".
e.g.
My current game only has 1
malloc (
and some new's in middleware bindings) that places a large address range into a
scopestack, in which you can allocate objects or other
scopes. It's still proper C++ with reliable constructors/destructors/etc and RAII, but instead of the "smart pointer" type only maintaining the lifetime of 1 link, it is instead a
container of links (
or actually, a linked-list of destructors to call when the scope is unwound). When using POD types, allocation and deallocation are just pushing/popping from a stack of bytes (
almost free, like regular stack/local allocation), but with a 'scope' object representing their lifetime.
This makes "dynamic memory allocation" as easy and predictable (
bug-friendly) as regular C/C++ stack allocation rules, without ever worrying about using the heap (
new/
malloc).
It also means that you can use raw arrays and pointers much more safely and easily than with C++ smart pointers. Raw "unsafe" code can really be easier and simpler
in the right conditions -- IM, the right way to learn about those conditions is to first learn the standard (STL) way to manage things, and then see what junk you can strip out once you grok it.
To illustrate, if a Foo class had to maintain a Bar member using dynamic allocation:
In C with classes:
class Foo // verbose, explicit
{
public:
Bar* m;
void Init() { m = (Bar*)malloc(sizeof(Bar)); Bar::Init(m); }
void Deinit() { free(m); }//oh damn this verbosity, I forgot to call Bar::Deinit(m)
};
Foo* test = (Foo*)malloc(sizeof(Foo));
Foo::Init(m);
Foo::Deinit(m);
free(test);In basic C++:
class Foo : NonCopyable //copy constructor/assignment operator need to be implemented if copyable (rule of three)
{
Bar* m;
public:
Foo() m(new Bar) {}
~Foo() { delete m; }
};
Foo* test = new Foo;
delete test;In modern C++:
class Foo
{//N.B. actually smart_pointer, auto_ptr, etc
smart_pointer<Bar> m;// handles destructor and copy (via reference counting)
public:
Foo() m(new Bar) {}
};
smart_pointer<Foo> test( new Foo );With a simple stack allocator:
class Foo
{
Bar* m;
public:
Foo(Scope& a) m(myNew(a,Bar)) {}//member should have same scope as parent, no need for destructor or copy
// copies are weak (possibly dangling, not ref-counted) pointers just like the basic C++ / C examples, so should only be passed to objects *closer to the top of the stack*.
};
Stack stack( malloc(GB(1)), GB(1) );
Scope a( stack );
Foo* test = myNew(a, Foo)(a);//increments a stack alloc, constructs, adds destructor to scope
//@ Cornstalks -- struct Image // Oh shiz, what now?!
struct Image
{
int width, height;
int size() {return sizeof(Image) + sizeof(pixel)*width*height;}
pixel* begin() { return (pixel*)(this+1); }
pixel* end() { return begin() + width*height; }
}
Image* test = myAlloc(a, Image::size());//increments a stack alloc, valid until 'a' is destructed