Stefano Lusardi, Software Developer
The owner is responsible for the management of a given resource (i.e. thread or file handle)
The main problem arises on memory deallocation.
Golden rule:
Don't allocate things on the Heap if you don't have to.
Always prefer *Stack allocations with respect to **Heap allocations.
*Automatic object deallocation occurs going out of scope.
You do not need to take care of that.
**Object deallocation needs to be handled in some ways.
You do must take care of that.
Stack vs. Heap example
Foo foo;
// Stack allocation. No need for manual cleanup.
Foo* foo = new Foo();
// Heap allocation. Needs to be deleted at some point.
...
delete foo;
// Delete operation is required to avoid a Memory Leak
So, why do we need Heap allocations?
Polymorphism and much more.
class Shape { virtual ~Shape() = default;};
class Circle : public Shape { /* ... */ };
class Square : public Shape { /* ... */ };
Shape* circle = new Circle();
Shape* square = new Square();
// ...
delete circle;
delete square;
At some point you must delete explicitly both circle and square objects in order to avoid memory leaks.
A real case scenario
ShapeManager* manager = new ShapeManager();
Point2d* pointA = manager->CreatePoint2d(0, 0);
Point2d* pointB = manager->CreatePoint2d(1, 1);
Segment* segment = manager->CreateSegment(pointA, pointB);
// ... delete?
Who is in charge to call delete on these Heap-allocated objects?
Note that the ownership hierarchy on the previous example is not clear at all:
You must read each class implementation to understand the objects hierarchy.
So, why do we care?
Readability, Testability, Expressivity.Does the modern standards provide any utility for (smart) resource management?
Smart Pointers is the answer to our question.The common idea is that each class provides a wrapper around a pointer to a class T.
All the "Smart Pointer" classes mimic the behavior of a "Raw Pointer" (also providing same syntax)
A great fatures is the so-called "Reference counting" mechanism that allows automatic deallocation of the inner pointer as soon as its last reference gets deleted.
Reference Counting
Roughly speaking it is the number of instances that have a reference to the same object
It is safe to assume that is possible to delete an object when it is no more referenced by any other objects (i.e. it reference count is zero)
Reference Counting
This technique is commonly used in managed languages such as Python, Java and C# in which the memory management is not directly handled by the programmer.
Completely transparent for the user, with nearly zero overhead.
std::unique_ptr
std::unique_ptr examples
#include <memory>
// Ordinary initialization
std::unique_ptr<Shape> smartCircle(new Circle());
// Polymorphic creation using thread-safe helper function
auto smartSquare = std::make_unique<Square>();
// Move assignment
auto smartestSquare = std::move(smartSquare);
std::shared_ptr
std::shared_ptr examples
Square* square = new Square();
{
std::shared_ptr<Square> smartSquare1(square);
}
std::shared_ptr<Square> smartSquare2(square);
// crash! (double delete on square pointer)
auto smartCircle = std::make_shared<Circle>();
Circle* circle = smartCircle.get(); // get the inner pointer
// to be avoided, but legal
std::weak_ptr
std::weak_ptr examples
struct Node{
std::vector<std::shared_ptr<Node>> mChildNodes;
std::weak_ptr<Node> mParentNode;
};
// can hold a pointer to the parent object
// in a tree-like structure avoiding a dangling reference
auto smartSquare = std::make_shared<Square>();
std::weak_ptr<Square> weakSquare;
auto weakSquare = smartSquare; // assign shared_ptr to weak_ptr
auto innerSquare = smartSquare.lock();
// can be converted back to the original shared_ptr
Smart Pointers F.A.Q.
Shall I pass by reference?
Shall I get the internal pointer?
Helper functions std::make_unique, std::make_shared, can't access your class private constructor.
Beware of Ownership Stealing policies.
An lvalue is an object or expression whose address can be taken.
lvalue example
std::string x;
x = "42"s; // x is an lvalue
In fact it is possible both to take its address and to assign a value to it
An rvalue is an unnamed object or expression...
which is not an lvalue.
rvalue example
std::string y;
y = "42"s + "43"s; // the expression: "42"s + "43"s is an rvalue
Another rvalue example
std::string getValue () {
return "Fourty-Two"s;
}
...
std::string s = getValue();
Note that whenever there is an assignment we have a useless copy
So, can't we avoid useless copies and boost performances?
From C++11 it is possible to "use" (or better, "consume") rvalues in order to avoid useless copies.
What does it really means to move an object?
Starting from C++11 the STL introduces std::move(). It is just a static cast to an rvalue refenrece:
It is now possible to convert an lvalue into a temporary object (rvalue) avoiding useless copies
Foo myFoo;
auto myRvalueFoo1 = std::move(myFoo);
auto myRvalueFoo2 = static_cast<Foo&&>(myFoo);
// equivalent definitions
"It's going to die anyway" state of mind.
After you move an object it is going to be in a non-valid state
This is the key to get optimal benefit from move semantics: move all the time you do not need the temporary any longer.lvalue vs. rvalue move example
void foo(int& i) { std::cout << "lvalue"; }
void foo(int&& i) { std::cout << "rvalue"; }
int main(){
int i = 42;
foo(i); // lvalue called
foo(42); // rvalue called
foo(std::move(i)); // rvalue called
return 0;
}
Move Constructor and Move Assignement example
class Foo {
int* value = nullptr;
public:
Foo(Foo&& other) { // Note non-const &&
value = other.value;
other.value = nullptr;
}
Foo& operator=(Foo&& other) {
if(this != &other) { // Prevent self-destruction
if(value) { delete value; }
value = other.value;
other.value = nullptr;
}
return *this;
}
~Foo() {
if(value) { delete value; }
}
};
"Smart" Move Constructor and Move Assignement example
class Foo {
// unique_ptr already embeds move semantics behavior
std::unique_ptr<int> value = nullptr;
public:
Foo(Foo&& other) = default;
Foo& operator=(Foo&& other) = default;
~Foo() = default;
};
Usage example
struct Bar {
Bar(std::unique_ptr<int>& v) : barValue{std::move(v)} { }
std::unique_ptr<int> barValue;
};
int main() {
auto value = std::make_unique<int>(42);
std::cout << *value.get(); // OK
// Now bar has unique ownership of value
auto bar = Bar(value);
// value is in a non-valid state (nullptr)
std::cout << *value.get(); // Crash!
return 0;
}
To avoid such a situation call std::make_unique<int> directly in Bar constructor
Move operations into STL container
std::string str = "Hello"; // std::string defines move operations
std::vector<std::string> stringVector;
stringVector.push_back(str); // Copy here
stringVector.push_back(std::move(str)); // NO copy here
// Now str is empty. Do not use it anymore.
Note that STL provides overloaded functions for its container in order to avoid useless copies
"Smart" move operations into STL container
class Foo {
Foo(int a, std::string b, double c){ /* ... */}
};
std::vector<Foo> fooVector;
fooVector.emplace(42, "Fourty-Two", 4.2); // NO copy here.
// emplace provides in-place construction & push_back operations
Note that emplace creates the object by means of arguments forwarding.