Understanding Memory Management, Part 3: C++ Smart Pointers