Eu escrevi um wrapper c ++ extensível em torno de uma biblioteca c muito difícil de usar, mas também muito útil. O objetivo é ter a conveniência de c ++ para alocar o objeto, expor suas propriedades, desalocar o objeto, copiar semântica, etc ...
O problema é o seguinte: às vezes a biblioteca c deseja o objeto subjacente (um ponteiro para o objeto) e o destruidor de classe não deve destruir a memória subjacente. Enquanto na maioria das vezes, o destruidor deve desalocar o objeto subjacente. Eu experimentei definir um bool hasOwnership
sinalizador na classe para que o destruidor, o operador de atribuição, etc ... saiba se deve ou não liberar a memória subjacente. No entanto, isso é complicado para o usuário e, também, às vezes não há como saber quando outro processo estará usando essa memória.
Atualmente, eu tenho a configuração onde, quando a atribuição vem de um ponteiro do mesmo tipo que o tipo subjacente, defino o sinalizador hasOwnership. Eu faço o mesmo quando o construtor sobrecarregado é chamado usando o ponteiro da biblioteca c. No entanto, isso ainda não lida com o caso em que o usuário criou o objeto e o passou para uma das minhas funções que chama c_api e a biblioteca armazena o ponteiro para uso posterior. Se eles fossem excluir seu objeto, sem dúvida causaria um segfault na biblioteca c.
Existe um padrão de design que simplifique esse processo? Talvez algum tipo de contagem de referência?
fonte
unique_ptr
na maioria dos casos, já pode lidar com esse tipo de recursos, portanto, você não precisa implementar o gerenciamento de recursos.unique_ptr
usa orelease
método para abandonar a propriedade do objeto armazenado.Respostas:
Se a responsabilidade de limpar as coisas alocadas dinamicamente mudar entre sua classe de wrapper e a biblioteca C com base em como as coisas são usadas, você está lidando com uma biblioteca C mal projetada ou está tentando fazer muito em sua classe de wrapper.
No primeiro caso, tudo o que você pode fazer é acompanhar quem é responsável pela limpeza e esperar que nenhum erro seja cometido (por você ou pelos mantenedores da biblioteca C).
No segundo caso, você deve repensar o design do seu invólucro. Toda a funcionalidade pertence à mesma classe ou pode ser dividida em várias classes. Talvez a biblioteca C use algo semelhante ao padrão de design do Facade e você deve manter uma estrutura semelhante no seu wrapper C ++.
De qualquer forma, mesmo que a biblioteca C seja responsável por limpar algumas coisas, não há nada errado em manter uma referência / ponteiro para essas coisas. Você só precisa se lembrar de que não é responsável por limpar a que o ponteiro se refere.
fonte
Muitas vezes você pode usar um padrão como este:
Ao usar,
release
você pode transferir explicitamente a propriedade. No entanto, isso é confuso e você deve evitá-lo, se possível.A maioria das bibliotecas C possui um ciclo de vida do objeto semelhante ao C ++ (alocação, acessadores, destruição de objetos) que mapeia bem o padrão C ++ sem transferência de propriedade.
Se os usuários precisarem de propriedade compartilhada, eles deverão usar
shared_ptr
com suas classes. Não tente implementar nenhuma propriedade compartilhada por você mesmo.Atualização: se você deseja tornar a transferência de propriedade mais explícita, pode usar um qualificador de referência:
Em seguida, os usuários devem chamar
bar
lvalues como este:fonte
unique_ptr
, ele não armazena mais o ponteiro que ele contém. Modelar a transferência de propriedade e ao mesmo tempo permitir o acesso a recursos renegados parece ser bastante confuso para os usuários. Como você controla quando a biblioteca C libera os objetos que agora possui?Há uma resposta direta ao seu problema, indicadores inteligentes. Ao usar um ponteiro inteligente para reter a memória da biblioteca C e adicionar uma referência quando o ponteiro também estiver na biblioteca (e soltar a referência quando a biblioteca C retornar), você liberará automaticamente a memória quando a contagem de referência cair para zero (e apenas então)
fonte
Se a biblioteca puder liberar as coisas internamente, e os cenários em que isso acontecer estiver bem documentado, tudo o que você poderá fazer é definir um sinalizador, como você já está fazendo.
fonte