std::enable_shared_from_this
é uma funcionalidade em C++, adicionada a partir do C++11, que permite a uma classe criar instâncias compartilhadas shared_ptr
dela mesma.
Esse mecanismo é útil quando você precisa criar um shared_ptr
dentro de um método da própria classe, especialmente para evitar duplicação e manter a contagem de referências correta.
Antes de mais nada sua classe precisa herdar publicamente(std::enable_shared_from_this<T>
) para o tipo da sua classe, exemplo:
class MyClass : public std::enable_shared_from_this<MyClass> {
A função-membro dessa classe que possui tipo de retorno: shared_ptr
não mais retornará *this
e sim shared_from_this()
, exemplo:
*this
não é adequado, pois não incrementa a contagem de referências do shared_ptr
;*this
pode levar a problemas de gerenciamento de memória e possíveis dangling pointers(ou wild pointers, não apontam para locais adequados);*this
é somente adequado para ponteiros brutos e referências.// Péssima ideia
std::shared_ptr<MyClass> set_info() {
return *this;
}
// Boa ideia
std::shared_ptr<MyClass> set_info() {
return shared_from_this();
}
Suponhamos que você possui esse código que soma e incrementa membros de uma classe, assim como vimos nesse vídeo:
#include <iostream>
template<class T>
class Vector2 {
public:
T x, y;
Vector2(T xin, T yin) : x(xin), y(yin){}
Vector2 operator + (const Vector2& rhs){
return Vector2(x + rhs.x, y + rhs.y);
}
Vector2 & increment(int number){
x += number;
y += number;
return *this;
}
void print(){
std::cout << x << " e " << y << '\n';
}
};
int main (){
Vector2 v1(1, 2), v2(3, 4);
Vector2 v3 = v1 + v2;
std::cout << "v3.x: " << v3.x << '\n'; // 4
std::cout << "v3.y: " << v3.y << '\n'; // 6
// Ou somente:
v3.print(); // 4 e 6
v3.increment(5);
v3.print(); // 9 e 11
return 0;
}
Note que a class Vector2
possui uma função-membro: increment
que é uma referência para ela mesma e retorna um *this
!
Traduzindo esse código para uso de std::enable_shared_from_this
, ficaria assim:
#include <iostream>
#include <memory>
template<class T>
class Vector2 : public std::enable_shared_from_this<Vector2<T>> {
public:
T x, y;
Vector2(T xin, T yin) : x(xin), y(yin) {}
Vector2 operator + (const Vector2& rhs) {
return Vector2(x + rhs.x, y + rhs.y);
}
std::shared_ptr<Vector2> increment(int number) {
x += number;
y += number;
return this->shared_from_this();
}
void print() {
std::cout << x << " e " << y << '\n';
}
};
int main() {
auto v1 = std::make_shared<Vector2<int>>(1, 2);
auto v2 = std::make_shared<Vector2<int>>(3, 4);
Vector2<int> v3 = *v1 + *v2;
std::cout << "v3.x: " << v3.x << '\n'; // 4
std::cout << "v3.y: " << v3.y << '\n'; // 6
// Ou somente:
v3.print(); // 4 e 6
auto v3_ptr = std::make_shared<Vector2<int>>(v3);
v3_ptr->increment(5);
v3_ptr->print(); // 9 e 11
return 0;
}
Note que herdamos publicamente: std::enable_shared_from_this
e o tipo de increment
agora é std::shared_ptr
e retorna: shared_from_this()
.
A partir desse código temos gerenciamento automático de referências e podemos até contá-las, exemplo:
std::cout << "Qtd de referências(qts vezes instanciamos/criamos objeto/ponteiro) para v1: "
<< v1.use_count() << '\n'; // 1
std::cout << "Qtd de referências(qts vezes instanciamos/criamos objeto/ponteiro) para v2: "
<< v2.use_count() << '\n'; // 1
std::cout << "Qtd de referências(qts vezes instanciamos/criamos objeto/ponteiro) para v3_ptr: "
<< v3_ptr.use_count() << '\n'; // 1
Muito mais moderno e like a boss!
Para mais informações acesse os links abaixo!