172 votes

Comment utiliser un supprimeur personnalisé avec un membre std::unique_ptr ?

J'ai une classe avec un membre unique_ptr.

class Foo {
private:
    std::unique_ptr bar;
    ...
};

Bar est une classe tierce qui a une fonction create() et une fonction destroy().

Si je voulais utiliser un std::unique_ptr avec cela dans une fonction autonome, je pourrais faire :

void foo() {
    std::unique_ptr bar(create(), [](Bar* b){ destroy(b); });
    ...
}

Y a-t-il un moyen de faire cela avec std::unique_ptr en tant que membre d'une classe?

4voto

mkaes Points 6867

Vous pouvez simplement utiliser std::bind avec une fonction de destruction.

std::unique_ptr> bar(create(), std::bind(&destroy,
    std::placeholders::_1));

Mais bien sûr, vous pouvez aussi utiliser une lambda.

std::unique_ptr> ptr(create(), [](Bar* b){ destroy(b);});

4voto

skyhawk Points 51
#include "fmt/core.h"
#include 

class example {};

void delete_example(example *)
{
    fmt::print("delete_example\n");
}

using example_handle = std::unique_ptr;

int main()
{
    example_handle handle(new example);
}

Just my two cents, using C++20.

https://godbolt.org/z/Pe3PT49h4

3voto

johv Points 1108

Avec une lambda, vous pouvez obtenir la même taille qu'un simple std::unique_ptr. Comparez les tailles :

plain: 8
lambda: 8
fpointer: 16
std::function: 40

Quel est le résultat du code suivant. (J'ai déclaré la lambda en dehors de la portée de la classe. Pas sûr si vous pouvez la déclarer à l'intérieur de la classe.)

#include 
#include 
#include 

struct Bar {};
void destroy(Bar* b) {}
Bar* create() { return 0; }

auto lambda_destroyer = [](Bar* b) {destroy(b);};

class Foo {

    std::unique_ptr ptr_;

public:

    Foo() : ptr_(create(), lambda_destroyer) { /* ... */ }
};

int main()
{
    std::cout << "plain: "         << sizeof (std::unique_ptr) << std::endl
              << "lambda: "        << sizeof (std::unique_ptr) << std::endl
              << "fpointer: "      << sizeof (std::unique_ptr) << std::endl
              << "std::function: " << sizeof (std::unique_ptr>) << std::endl;
}

0voto

user240515 Points 1282

Je suis assez convaincu que c'est la meilleure façon actuelle de le faire :

#include 
#include 

template 
struct Deleter
{
  void operator()(T *ptr)
  {
    fn(ptr);
  }
};

template 
using handle = std::unique_ptr>;

using file = handle;

int main()
{
  file f{fopen("a.txt", "w")};
  return 0;
}

Parce que vous avez spécifié un foncteur comme le supprimeur dans les arguments de modèle du unique_ptr, vous n'avez pas besoin de définir un supprimeur lors de l'appel de son constructeur.

Le foncteur Deleter utilise "template auto" pour prendre une fonction de suppression (dans cet exemple: fclose) en tant qu'argument de modèle, donc cela nécessite C++17.

L'élargir pour prendre en charge d'autres types nécessite juste une ligne "using" supplémentaire par type.

0voto

IceFire Points 159

Simple is also:

class Foo {};
class Bar
{
public:
    Bar()
    {
        // initialisation réelle à un moment donné
    }

private:
    std::unique_ptr foo = {{}, {}}; // ou = {nullptr, {}}
};

Bien sûr, vous pouvez également créer une fonction d'aide pour effectuer le travail afin de ne pas avoir l'état initial à tout moment.

En fait, dans votre scénario spécifique, la façon la plus propre est effectivement de placer votre Bar (pas le mien, désolé pour la confusion) dans une classe enveloppe simple, ce qui facilite la réutilisation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X