Pointeurs intelligents (smart pointers)
Erreur typique en C
void f() {
int* p = malloc(sizeof(int));
*p = 5;
}
Oh no... On oublier de libérer la mémoire. On ne respecte pas le principe RAII.
Pointeur intelligent
Les pointeurs intelligents sont proposés par la librairie standard pour gérer les pointeurs avec le principe RAII.
#include <memory>
void f() {
int* rawp = new int(5);
shared_ptr<int> p(rawp);
...
}
Pas de soucis car l'objet shared_ptr<int>
est propriétaire du pointeur rawp
. Quand on quitte la fonction, l'objet est détruit et c'est dans le destructeur de l'objet que le pointeur est désalloué, si le nombre de références vaut 0.
- Qu'imprime le programme ci-dessous ?
#include <memory>
void f() {
int* rawp = new int(5);
shared_ptr<int> p(rawp);
auto q = p;
std::cout << q.use_count();
}
Mieux
#include <memory>
void f() {
auto p = std;;make_shared<int>(5);
auto q = p;
std::cout << q.use_count();
}
A l'intérieur d'un shared_ptr
Est-ce que ça ça marche ?
En C++
template<class T>
class my_shared_ptr
{
private:
T * ptr = nullptr;
unsigned int * refCount = nullptr;
public:
//ptr should non nullptr
my_shared_ptr(T * ptr) : ptr(ptr), refCount(new unsigned int(1))
{
}
/*** Copy Semantics ***/
my_shared_ptr(const my_shared_ptr & obj)
{
this->ptr = obj.ptr;
this->refCount = obj.refCount;
(*this->refCount)++;
}
my_shared_ptr& operator=(const my_shared_ptr & obj) // copy assignment
{
__cleanup__(); // cleanup any existing data
this->ptr = obj.ptr;
this->refCount = obj.refCount;
(*this->refCount)++;
}
/*** Move Semantics ***/
my_shared_ptr(my_shared_ptr && dyingObj)
{
this->ptr = dyingObj.ptr;
this->refCount = dyingObj.refCount;
dyingObj.ptr = dyingObj.refCount = nullptr;
}
my_shared_ptr& operator=(my_shared_ptr && dyingObj)
{
__cleanup__();
this->ptr = dyingObj.ptr;
this->refCount = dyingObj.refCount;
dyingObj.ptr = dyingObj.refCount = nullptr;
}
unsigned int get_count() const
{
return *refCount;
}
T* get() const
{
return this->ptr;
}
T* operator->() const
{
return this->ptr;
}
T& operator*() const
{
return this->ptr;
}
~my_shared_ptr() // destructor
{
__cleanup__();
}
private:
void __cleanup__()
{
if(refCount == nullptr)
return;
(*refCount)--;
if (*refCount == 0)
{
if (nullptr != ptr)
delete ptr;
delete refCount;
}
}
};
En Rust
Unique ptr
A unique pointer cannot be "copied". The arrow to the data is unique.
En C++ :
std::shared_ptr<int> p = std::make_shared<int>(5);
En Rust :
let b = Box::new(5);
Weak ptr
To avoid loops in the pointer graph, we can use weak pointers. A weak pointer is an object having a reference to some data, or maybe not if the data has been deleted.
En C++ :
std::shared_ptr<int> p = std::make_shared<int>(5);
std::weak_ptr wp = p;
if(std::shared_ptr<int> strong_p = wp.lock()) {
//here you can use strong_p
}
else
{
//the data does not exist anymore
}
En Rust, it is std::rc::Weak
, see the manual.