Modern
C++ Memory
Management
By Alan Uthoff
Who is Alan
Uthoff?
• Founder and CEO of Swordpoint Studio, LLC
• Professional game developer 10+ years in C++
• Runs the C++ 17 Advanced Mentoring and Study
Group - Austin (CppMSG) and Austin Qt and QML
Developers Meetup
Modern Memory
Management
• Scoped base management 

• Clear intent and owner 

• Reduce errors and make errors detectable
C++ 98 Memory
Management
class myClass
{
public:
myClass()
{ m_dataPtr = new dataClass();}
virtual ~myClass()
{
if(m_dataPtr != NULL )
delete m_dataPtr;
}
private:
dataClass * m_dataPtr;
};
C++ 14 Memory
Management
// 98 way
class myClass
{
public:
myClass()
{ m_dataPtr = new
dataClass();}
virtual ~myClass()
{
if(m_dataPtr != NULL )
delete m_dataPtr;
}
private:
dataClass * m_dataPtr;
};
// 14 way
class myClass
{
public:
myClass(){ }
virtual ~myClass(){
}
private:
auto m_dataPtr =
std::make_unique<dataClass>()
;
};
std::unique_ptr
• #include <memory>

• Can not be copied 

• Can be moved 

• make_unique
std::unique_ptr
• std::unique_ptr 

• operator bool

• operator=

• operator->

• operator*

• operator[]

• get

• release
C++ 98 Factory
//With double pointers
void myClassFactory(myClass ** myDoublePtr)
{
*myDoublePtr = new myClass();
}
//return by pointer
myClass * myClassFactory()
{
return new myClass();
}
//Creates a lot of copies not very performant function
std::string myStringFactory()
{
//Creates two strings here
return "Hello World";
}
C++ 14 Factory
// 98 way
//With double pointers

void myClassFactory(myClass **
myDoublePtr)
{
*myDoublePtr = new myClass();
}
//return by pointer
myClass * myClassFactory()
{
return new myClass();
}
//Creates a lot of copies not very
performant function
std::string myStringFactory()
{
//Creates two strings here
return "Hello World";
}
// 14 way
std::unique_ptr<myClass> myClassFactory()
{
return make_unique<myClass>();
}
std::string myStringFactory()
{
//Returned by move
return "Hello World";
}


std::string myStringFactory()
{
std::string temp("Hello World");
//Returned by move or
//Copy Elision for prvalues in C++
//17 optional in C++ 11 and 14
return std::move(temp);
}
Move Semantics
• Adds new constructor and assignment operator 

• myClass(myClass && other) //move constructor

• myClass & operator=(myClass && other ) //move assignment
operator

• std::move cast lvalue to rvalue
class myClass{
public:
myClass() :
m_dataPtr(std::make_unique<dataClass>()) {}
virtual ~myClass(){}
myClass(myClass && other):
m_dataPtr(std::move(other.m_dataPtr)) {
other.m_dataPtr = nullptr;
}
myClass & operator=(myClass && other ) {
if(this != &other){
m_dataPtr = std::move(other.m_dataPtr);
other.m_dataPtr = nullptr; }
return *this;
}
private:
std::unique_ptr<dataClass> m_dataPtr = nullptr;
};
myClass myClassFactory()
{
myClass temp;
return std::move(temp);
}
int main()
{
myClass myObj = myClassFactory();
}
Copy Elision
• Constructs the object in place avoiding extra copies 

• T x = T(T(T()));// only one call to default constructor of T, to initialize 

• T f() { return T{}; }

• T x = f(); // only one call to default constructor of T, to initialize x

• T* p = new T(f()); // only one call to default constructor of T, to
initialize *p

• Guaranteed for prvalues in C++ 17 

• Optional in C++ 11 and 14
Examples taken from http://en.cppreference.com/w/cpp/language/copy_elision
std::shared_ptr
• #include <memory>

• make_shared

• Copy constructor or copy assignment of the shared_ptr
increments refcount 

• Decrements when the shard pointer instance is destroyed
std::shared_ptr
• std::shared_ptr

• use_count

• get

• operator=

• operator*

• operator->

• operator bool
std::shared_ptr example
void processData(std::shared_ptr<dataClass> p)
{
// thread-safe, even though the shared use_count is
incremented
std::shared_ptr<dataClass> lp = p;
lp->threadSafeProcessData();
}
auto p = std::make_shared<dataClass>();
std::thread t1(processData, p), t2(processData, p),
t3(processData, p);
p.reset(); // release ownership from main
t1.join(); t2.join(); t3.join();
std::cout << "All threads completed, the last one deleted
Derivedn";
Example taken http://en.cppreference.com/w/cpp/memory/shared_ptr
std::weak_ptr
• #include <memory>

• std::weak_ptr

• user_count 

• expired

• rest

• lock

• Does not increment the shared_ptr only the control block
std::weak_ptr example
auto sharedptr =
std::make_shared<int>(42);
std::weak_ptr<int> weakptr = sharedptr;
if (auto validSharedptr =
weakptr.lock())
{
//good ptr use validSharedptr
}else{
//memory is gone nullptr
}
Working with Legacy Code
void legacyFunction(const myClass * ptr )
{//do something …}
void legacyFunctionTakesOwnership(const myClass * ptr )
{//do something …}
myFactoryClass * legacyFactory( )
{return new myFactoryClass();}
void legacyFactoryDestroy(myFactoryClass * )
{delete myFactoryClass;}
int main()
{
auto uniquePtr = make_unique<myClass>();
legacyFunction(uniquePtr.get());
auto uniquePtr2 = make_unique<myClass>();
legacyFunctionTakesOwnership(uniquePtr2.release());
//can add a deleter as the second argument unique_ptr to have legacyFactoryDestroy
//called automaticity
std::unique_ptr<myFactoryClass> myFactoryPtr(legacyFactory());
legacyFactoryDestroy(myFactoryPtr.release());
}
std::any
• A type safe object that can hold any type of object

• Is a replacement for void pointers

• #include <any>

• std::any

• operator=

• emplace

• reset

• has_value

• type

• any_cast

• make_any

• bad_any_cast
std::any
bool processAny(std::any & valueToProcess)
{
if(!valueToProcess.has_value())
{
return false;
}
try{
if( valueToProcess.type() == typeid(A))
{
std::any_cast<A>(valueToProcess).process();
}
else if(valueToProcess.type() == typeid(B))
{
std::any_cast<B>(valueToProcess).process();
}
else
{
return false;
}
}
catch(const std::bad_any_cast)
{
//process bad any cast
return false;
}
return true;
}
std::optional
• May contain a value

• #include <optional>

• nullopt //type nullopt_t indicate that the optional is uninitialize
std::optional
• std::optional 

• operator->

• operator*

• operator bool / has_value

• value

• value_or

• reset

• emplace
std::optional
std::optional<unsigned int> myFactory(float value)
{
if(value < 0)
{
return std::nullopt;
}
return static_cast<unsigned int>(value);
}
int main()
{
std::optional<unsigned int> returnValue = myFactory(2.0f);
if(returnValue)
{
std::cout<<returnValue.value()<<std::endl;
}
else
{
std::cout<<"Failed to get value"<<std::endl;
}
}
http://alan.uthoff.us/presentations/ModernMemoryManagment
Questions?

Modern c++ Memory Management

  • 1.
  • 2.
    Who is Alan Uthoff? •Founder and CEO of Swordpoint Studio, LLC • Professional game developer 10+ years in C++ • Runs the C++ 17 Advanced Mentoring and Study Group - Austin (CppMSG) and Austin Qt and QML Developers Meetup
  • 3.
    Modern Memory Management • Scopedbase management • Clear intent and owner • Reduce errors and make errors detectable
  • 4.
    C++ 98 Memory Management classmyClass { public: myClass() { m_dataPtr = new dataClass();} virtual ~myClass() { if(m_dataPtr != NULL ) delete m_dataPtr; } private: dataClass * m_dataPtr; };
  • 5.
    C++ 14 Memory Management //98 way class myClass { public: myClass() { m_dataPtr = new dataClass();} virtual ~myClass() { if(m_dataPtr != NULL ) delete m_dataPtr; } private: dataClass * m_dataPtr; }; // 14 way class myClass { public: myClass(){ } virtual ~myClass(){ } private: auto m_dataPtr = std::make_unique<dataClass>() ; };
  • 6.
    std::unique_ptr • #include <memory> •Can not be copied • Can be moved • make_unique
  • 7.
    std::unique_ptr • std::unique_ptr •operator bool • operator= • operator-> • operator* • operator[] • get • release
  • 8.
    C++ 98 Factory //Withdouble pointers void myClassFactory(myClass ** myDoublePtr) { *myDoublePtr = new myClass(); } //return by pointer myClass * myClassFactory() { return new myClass(); } //Creates a lot of copies not very performant function std::string myStringFactory() { //Creates two strings here return "Hello World"; }
  • 9.
    C++ 14 Factory //98 way //With double pointers void myClassFactory(myClass ** myDoublePtr) { *myDoublePtr = new myClass(); } //return by pointer myClass * myClassFactory() { return new myClass(); } //Creates a lot of copies not very performant function std::string myStringFactory() { //Creates two strings here return "Hello World"; } // 14 way std::unique_ptr<myClass> myClassFactory() { return make_unique<myClass>(); } std::string myStringFactory() { //Returned by move return "Hello World"; } 
 std::string myStringFactory() { std::string temp("Hello World"); //Returned by move or //Copy Elision for prvalues in C++ //17 optional in C++ 11 and 14 return std::move(temp); }
  • 10.
    Move Semantics • Addsnew constructor and assignment operator • myClass(myClass && other) //move constructor • myClass & operator=(myClass && other ) //move assignment operator • std::move cast lvalue to rvalue
  • 11.
    class myClass{ public: myClass() : m_dataPtr(std::make_unique<dataClass>()){} virtual ~myClass(){} myClass(myClass && other): m_dataPtr(std::move(other.m_dataPtr)) { other.m_dataPtr = nullptr; } myClass & operator=(myClass && other ) { if(this != &other){ m_dataPtr = std::move(other.m_dataPtr); other.m_dataPtr = nullptr; } return *this; } private: std::unique_ptr<dataClass> m_dataPtr = nullptr; };
  • 12.
    myClass myClassFactory() { myClass temp; returnstd::move(temp); } int main() { myClass myObj = myClassFactory(); }
  • 13.
    Copy Elision • Constructsthe object in place avoiding extra copies • T x = T(T(T()));// only one call to default constructor of T, to initialize • T f() { return T{}; } • T x = f(); // only one call to default constructor of T, to initialize x • T* p = new T(f()); // only one call to default constructor of T, to initialize *p • Guaranteed for prvalues in C++ 17 • Optional in C++ 11 and 14 Examples taken from http://en.cppreference.com/w/cpp/language/copy_elision
  • 14.
    std::shared_ptr • #include <memory> •make_shared • Copy constructor or copy assignment of the shared_ptr increments refcount • Decrements when the shard pointer instance is destroyed
  • 15.
    std::shared_ptr • std::shared_ptr • use_count •get • operator= • operator* • operator-> • operator bool
  • 16.
    std::shared_ptr example void processData(std::shared_ptr<dataClass>p) { // thread-safe, even though the shared use_count is incremented std::shared_ptr<dataClass> lp = p; lp->threadSafeProcessData(); } auto p = std::make_shared<dataClass>(); std::thread t1(processData, p), t2(processData, p), t3(processData, p); p.reset(); // release ownership from main t1.join(); t2.join(); t3.join(); std::cout << "All threads completed, the last one deleted Derivedn"; Example taken http://en.cppreference.com/w/cpp/memory/shared_ptr
  • 17.
    std::weak_ptr • #include <memory> •std::weak_ptr • user_count • expired • rest • lock • Does not increment the shared_ptr only the control block
  • 18.
    std::weak_ptr example auto sharedptr= std::make_shared<int>(42); std::weak_ptr<int> weakptr = sharedptr; if (auto validSharedptr = weakptr.lock()) { //good ptr use validSharedptr }else{ //memory is gone nullptr }
  • 19.
    Working with LegacyCode void legacyFunction(const myClass * ptr ) {//do something …} void legacyFunctionTakesOwnership(const myClass * ptr ) {//do something …} myFactoryClass * legacyFactory( ) {return new myFactoryClass();} void legacyFactoryDestroy(myFactoryClass * ) {delete myFactoryClass;} int main() { auto uniquePtr = make_unique<myClass>(); legacyFunction(uniquePtr.get()); auto uniquePtr2 = make_unique<myClass>(); legacyFunctionTakesOwnership(uniquePtr2.release()); //can add a deleter as the second argument unique_ptr to have legacyFactoryDestroy //called automaticity std::unique_ptr<myFactoryClass> myFactoryPtr(legacyFactory()); legacyFactoryDestroy(myFactoryPtr.release()); }
  • 20.
    std::any • A typesafe object that can hold any type of object • Is a replacement for void pointers • #include <any> • std::any • operator= • emplace • reset • has_value • type • any_cast • make_any • bad_any_cast
  • 21.
    std::any bool processAny(std::any &valueToProcess) { if(!valueToProcess.has_value()) { return false; } try{ if( valueToProcess.type() == typeid(A)) { std::any_cast<A>(valueToProcess).process(); } else if(valueToProcess.type() == typeid(B)) { std::any_cast<B>(valueToProcess).process(); } else { return false; } } catch(const std::bad_any_cast) { //process bad any cast return false; } return true; }
  • 22.
    std::optional • May containa value • #include <optional> • nullopt //type nullopt_t indicate that the optional is uninitialize
  • 23.
    std::optional • std::optional •operator-> • operator* • operator bool / has_value • value • value_or • reset • emplace
  • 24.
    std::optional std::optional<unsigned int> myFactory(floatvalue) { if(value < 0) { return std::nullopt; } return static_cast<unsigned int>(value); } int main() { std::optional<unsigned int> returnValue = myFactory(2.0f); if(returnValue) { std::cout<<returnValue.value()<<std::endl; } else { std::cout<<"Failed to get value"<<std::endl; } }
  • 25.