normalmente encontro esse trecho aparecendo em meu código, onde dois objetos que não possuem um ao outro precisam ter um ponteiro um para o outro, e esse ponteiro deve ser "limpo" quando um deles for destruído da seguinte maneira.
struct bar;
struct foo;
struct foo
{
bar* bar_item = nullptr;
~foo();
};
struct bar
{
foo* foo_item = nullptr;
~bar()
{
if (foo_item)
{
foo_item->bar_item = nullptr;
}
}
};
foo::~foo()
{
if (bar_item)
{
bar_item->foo_item = nullptr;
}
}
int main()
{
foo obj1;
bar obj2;
obj1.bar_item = &obj2;
obj2.foo_item = &obj1;
}
o problema aqui é que eu também tenho que
- exclua o construtor/atribuição de cópia e implemente o construtor/atribuição de movimento (muito clichê, espere pelo menos 5 membros por classe)
- converta todos os objetos em shared_ptr e os objetos terão fraco_ptr entre si. (talvez nem sempre seja viável, pois posso não possuir a vida útil dos objetos, também me obriga a usar o heap)
existe alguma maneira de evitar isso? simplificá-lo? tem um wrapper RAII para um membro específico para evitar a implementação das operações de copiar/mover?
um exemplo simples que encontrei uma vez é um sistema gráfico onde preciso de uma linha para conectar duas esferas, a linha deve ter ponteiros para ambas as esferas e as esferas devem ter um ponteiro para a linha, todas elas pertencem à "cena" , mas perguntar a "cena" sobre eles requer uma longa pesquisa, os ponteiros evitam essa pesquisa.
Geralmente é um indicador de que se pode fazer melhor. Não deveríamos definir funções-membro especiais por causa dos membros de dados. Esses membros de dados devem definir funções de membros especiais.
Por exemplo, pode-se definir um tipo de ponteiro que redefine automaticamente um ponteiro vinculado.
E então use-o:
O
link_ptr
código é genérico e escrito apenas uma vez. O único clichê verdadeiro são os getters, o que provavelmente é aceitável:Aqui está um exemplo: https://godbolt.org/z/5feKdKcMr .
Como você mencionou e como os comentários mencionaram, existem designs alternativos (por exemplo, usar
weak_ptr
ou indexar em uma arena, entre outros), mas esta opção mostra que o ponteiro do observador pode não precisar de tantos padrões quanto se imagina.