Eu tenho uma classe com um membro unique_ptr.
class Foo {
private:
std::unique_ptr<Bar> bar;
...
};
A barra é uma classe de terceiros que possui uma função create () e uma função destroy ().
Se eu quisesse usar um std::unique_ptr
com ele em uma função autônoma, eu poderia fazer:
void foo() {
std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); });
...
}
Existe uma maneira de fazer isso std::unique_ptr
como membro de uma classe?
c++
c++11
move-semantics
unique-ptr
huitlarc
fonte
fonte
std::unique_ptr<Bar, decltype(&destroy)> ptr_;
unique_ptr
( todas elas devem armazenar o ponteiro da função junto com o ponteiro nos dados reais), requer passar a função de destruição todas as vezes, não pode ser incorporada (já que o modelo não pode especialize-se na função específica, apenas a assinatura) e deve chamar a função pelo ponteiro (mais caro que a chamada direta). As respostas do rici e do Deduplicator evitam todos esses custos ao se especializar em um functor.É possível fazer isso corretamente usando um lambda no C ++ 11 (testado no G ++ 4.8.2).
Dado este reutilizável
typedef
:Você pode escrever:
Por exemplo, com um
FILE*
:Com isso, você obtém os benefícios da limpeza com exceção de segurança usando RAII, sem a necessidade de tentar / capturar ruído.
fonte
std::function
na definição ou algo parecido?std::function
. A lambda ou a classe personalizada, como na resposta aceita, pode ser incorporada diferentemente desta solução. Mas essa abordagem tem vantagem no caso em que você deseja isolar toda a implementação no módulo dedicado.deleted_unique_ptr<Foo> foo(new Foo(), customdeleter);
secustomdeleter
seguir a convenção (retorna nulo e aceita ponteiro bruto como argumento).Você só precisa criar uma classe deleter:
e forneça-o como o argumento do modelo de
unique_ptr
. Você ainda precisará inicializar o unique_ptr em seus construtores:Até onde eu sei, todas as bibliotecas populares do c ++ implementam isso corretamente; como
BarDeleter
na verdade não tem nenhum estado, não precisa ocupar nenhum espaço nounique_ptr
.fonte
struct BarDeleter
) parastd::unique_ptr
(std::unique_ptr<Bar, BarDeleter>
) que permite aostd::unique_ptr
construtor criar uma instância Deleter por conta própria. ou seja, o seguinte código é permitidostd::unique_ptr<Bar, BarDeleter> bar[10];
typedef std::unique_ptr<Bar, BarDeleter> UniqueBarPtr
unique_ptr
, sem necessidade de fornecer uma instância do deleter ao construir) e agrega o benefício de poder usar emstd::unique_ptr<Bar>
qualquer lugar sem a necessidade de lembrar para usar otypedef
provedor especial ou explicitamente o segundo parâmetro do modelo. (Para ser claro, esta é uma boa solução, eu up-votado, mas ele pára um passo tímido de uma solução seamless)A menos que você precise alterar o deleter em tempo de execução, recomendo fortemente o uso de um tipo personalizado. Por exemplo, se você usar um ponteiro de função para seu deleter
sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*)
,. Em outras palavras, metade dos bytes dounique_ptr
objeto são desperdiçados.Escrever um deleter personalizado para agrupar todas as funções é um problema. Felizmente, podemos escrever um tipo de modelo na função:
Desde C ++ 17:
Antes do C ++ 17:
fonte
deleter_from_fn
é.Você pode simplesmente usar
std::bind
uma função de destruição.Mas é claro que você também pode usar um lambda.
fonte
Você sabe, usar um deleter personalizado não é o melhor caminho a percorrer, pois você precisará mencioná-lo em todo o seu código.
Em vez disso, como você pode adicionar especializações a classes no nível de namespace no
::std
, desde que tipos personalizados estejam envolvidos e você respeite a semântica, faça o seguinte:Especializar
std::default_delete
:E talvez também faça
std::make_unique()
:fonte
std
abre uma nova lata de vermes. Observe também que a especialização destd::make_unique
não é permitida após o C ++ 20 (portanto, isso não deveria ser feito antes) porque o C ++ 20 desaprova a especialização de coisas nasstd
quais não são modelos de classe (std::make_unique
é um modelo de função). Observe que você provavelmente também terminará com UB se o ponteiro passadostd::unique_ptr<Bar>
não tiver sido alocado decreate()
, mas de alguma outra função de alocação.std::default_delete
atende aos requisitos do modelo original. Eu imaginaria questd::default_delete<Foo>()(p)
seria uma maneira válida de escreverdelete p;
, portanto, sedelete p;
seria válido escrever (ou seja, seFoo
estiver completo), esse não seria o mesmo comportamento. Além disso, sedelete p;
a gravação fosse inválida (Foo
está incompleta), isso seria especificar um novo comportamento parastd::default_delete<Foo>
, em vez de manter o mesmo comportamento.make_unique
especialização é problemática, mas eu definitivamente usei astd::default_delete
sobrecarga (não modelada comenable_if
, apenas para estruturas C como o OpenSSLBIGNUM
que usam uma função de destruição conhecida, onde a subclasse não vai acontecer), e é de longe a abordagem mais fácil, pois o restante do seu código pode ser usado apenasunique_ptr<special_type>
sem a necessidade de passar o tipo de functor como o modeloDeleter
todo, nem usartypedef
/using
para dar um nome ao referido tipo para evitar esse problema.