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();
}

2 car il y a deux shared pointers qui partage l'entier.

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.