|
Para ver o tutorial anterior da série: Boost Shared Array e Scoped Array
Finalmente chegamos na ultima classe de smart pointer da Boost. O intrusive_ptr é semelhante ao shared_ptr, mas a diferença é que ao invés de alocar um objeto para controlar as referências, ele usa o próprio objeto que ele armazena para esse fim.
Dessa forma, o objeto apontado por ele precisa prover métodos ou funções que cuidem da contagem de referência, por isso o nome de intrusive (ou intruso), porque é necessário modificar o objeto que ele vai armazenar para que se possa usa-lo com o intrusive_ptr, devido a essa forma de implementação, já notamos aqui que não podemos usar o intrusive_ptr com qualquer tipo de objeto como fazíamos com o shared_ptr.
Quando um intrusive_ptr precisa incrementar as referências, ele chama a função intrusive_ptr_add_ref (passando como parâmetro o ponteiro do objeto). No processo inverso, quando é preciso decrementar uma referência, ele invoca a função intrusive_ptr_release, que fica responsável por decrementar as referências e destruir o objeto quando o contador chega a zero.
Vamos então modificar a classe Person para que ela possa ser usada com o intrusive_ptr:
#include <boost/intrusive_ptr.hpp>
#include <string>
class Person
{
public:
inline Person(): m_RefCount(0) { }
inline Person(const Person &p):
m_Name(p.m_Name),
m_RefCount(0)
{
}
inline const Person &operator=(const Person &p)
{
m_Name = p.m_Name;
return(*this);
}
inline void SetName(const std::string &name)
{
m_Name = name;
}
private:
inline void AddRef()
{
++m_RefCount;
}
inline void DecRef()
{
assert(m_RefCount > 0);
--m_RefCount;
if(m_RefCount == 0)
delete this;
}
std::string m_Name;
unsigned int m_RefCount;
friend inline void intrusive_ptr_add_ref(Person *p);
friend inline void intrusive_ptr_release(Person *p);
};
inline void intrusive_ptr_add_ref(Person *p)
{
p->AddRef();
}
inline void intrusive_ptr_release(Person *p)
{
p->DecRef();
}
int main(int argc, char **argv)
{
using namespace boost;
intrusive_ptr p(new Person());
p->SetName("BCS");
return(0);
}
A classe Person engordou um bocado agora, então vamos por partes:
- Foi adicionado o atributo m_RefCount que usamos como contador de referência, usamos um unsigned porque não tem o menor sentido um objeto com contagem negativa.
- Tive que adicionar um construtor para inicializar o contador com zero.
- Foi preciso também adicionar um construtor de cópia e um operador de atribuição para que o contador seja inicializado e copiado corretamente. Note que no operador de cópia, simplesmente não alteramos o contador.
- Criamos os métodos AddRef e DecRef, que incrementam e decrementam o contador, respectivamente. O DecRef possui um assert apenas para tentarmos pegar algum estado inconsistente. Note que o objeto é suicida, se o contador chegou a zero, ele se mata (delete this).
- Declaramos as funções intrusive_ptr_add_ref e intrusive_ptr_release para a classe Person, note que elas simplesmente invocam os AddRef e o DecRef. Outro detalhe foi a declaração delas como friend da classe Person, desse modo podemos deixar o acesso ao contador de referências privado e evitar uso equivocado desses métodos.
Vantagens
A primeira vista o intrusive_ptr parece ser uma versão mala do shared_ptr, mas existem muitos casos onde temos um grande numero de objetos e não podemos arcar com o peso de ter uma alocação extra para controle de referencias, nessas horas o intrusive_ptr se torna bem atraente.
Outra vantagem do intrusive_ptr é que podemos usar ele com objetos que já possuem algum controle por contagem de referencia, como por exemplo objetos COM do Windows, podemos simplesmente criar as funções intrusive_ptr_add_ref e intrusive_ptr_release adequadas.
Desvantagens
A maior desvantagem que vejo é que não existe uma implementação do weak_ptr, então é necessário tomar certo cuidado com referências circulares.
Outra desvantagem (que na verdade incomoda apenas quando vamos usa-lo pela primeira vez) é que ele se torna mais trabalhoso que o shared_ptr devido ao código extra para controle de referencias, mas novamente, isso acaba se pagando com as vantagens que temos.
-
10/01/2012 22:33:47 | Vinícius Godoy de Mendonça

A diferença é que o shared_ptr acaba com 2 variáveis.
1. O contador de referência;
2. A referência em si (um ponteiro, para o objeto que ele está apontando).O intrusive_ptr elimina a segunda, ao usar o próprio objeto.
Não faz sentido o contador ser estático, pois ele deve contar quantas referências são feitas aquele objeto, não àquela classe.
-
11/01/2012 10:01:54 | Bruno Crivelari Sanches

O Interessante é que existe uma forma de usar o shared_ptr, onde ao se alocar o objeto, se aloca o contador no mesmo segmento de memória, antes ou depois do objeto em si, assim, ambos ficam no mesmo bloco e não se tem o custo da alocação extra.
Só não sei como ele se comporta quando entra o weak_ptr, que faz falta quando só se usa o intrusive.
T+











Não vi ganhos se comparado ao Shared_ptr, pois cada instância do ponteiro conteria o contador de referencia. Agora, se o contador fosse static aí a coisa muda de figura. Concordam ?