web-dev-qa-db-fra.com

Comment passer deleter to make_shared?

Depuis C++ 11, pour plusieurs raisons, les développeurs ont tendance à utiliser les classes de pointeurs intelligents pour les objets dynamiques à vie. Et avec ces nouvelles classes de pointeurs intelligents, les normes suggèrent même de ne pas utiliser d'opérateurs tels que new, mais suggèrent plutôt d'utiliser make_shared ou make_unique pour éviter certaines erreurs. 

Si nous aimons utiliser une classe de pointeur intelligent, comme shared_ptr, nous pouvons en construire une comme,

shared_ptr<int> p(new int(12));

Aussi, nous aimerions passer un deleter personnalisé aux classes de pointeur intelligent,

shared_ptr<int> p(new int(12), deleter);

Par contre, si on aime utiliser make_shared pour allouer, par exemple. int, au lieu d'utiliser new et shared_ptr constructeur, comme sur la première expression ci-dessus, nous pouvons utiliser

auto ip = make_shared<int>(12);

Mais que se passe-t-il si nous voulons également transmettre un suppresseur personnalisé à make_shared, existe-t-il une bonne façon de le faire? On dirait que les compilateurs, au moins gcc, donnent une erreur à,

auto ip = make_shared<int>(12, deleter);
31
cavitsinadogru

Comme d'autres l'ont dit, make_shared ne peut pas être utilisé avec un suppresseur personnalisé. Mais je veux expliquer pourquoi.

Les déléteurs personnalisés existent parce que vous avez affecté le pointeur d'une manière spéciale. Par conséquent, vous devez pouvoir le désallouer de manière correspondante. make_shared alloue le pointeur avec new. Les objets affectés de new doivent être désalloués avec delete. Le deleter standard le fait consciencieusement.

En bref, si vous pouvez vivre avec le comportement d'allocation par défaut, vous pouvez aussi vivre avec le comportement par défaut deallocation. Et si vous ne pouvez pas vivre avec le comportement d'allocation par défaut, vous devez utiliser allocate_shared , qui utilise l'allocateur fourni pour allouer et désallouer le stockage.

De plus, make_shared est autorisé (et presque certainement) à allouer la mémoire pour T et le bloc de contrôle pour le shared_ptr au sein de la même allocation. Ceci est quelque chose que votre deleter ne peut pas vraiment savoir ou traiter. Considérant que allocate_shared est capable de le gérer, puisque l’allocateur que vous fournissez peut s’acquitter de tâches d’allocation et de désaffectation.

43
Nicol Bolas

À partir de documentation , make_shared accepte une liste d'arguments avec lesquels une instance de T sera construite.
En outre, la documentation indique que:

Cette fonction est généralement utilisée pour remplacer la construction std :: shared_ptr (new T(args...)) d'un pointeur partagé à partir du pointeur brut renvoyé par un appel à new.

De ce fait, vous pouvez en déduire que vous ne pouvez pas définir un deleter personnalisé.
Pour ce faire, vous devez créer le shared_ptr à l'aide du droit constructeur .
À titre d’exemple de constructeur de la liste proposée, vous pouvez utiliser:

template< class Y, class Deleter > 
shared_ptr( Y* ptr, Deleter d );

Ainsi, le code sera quelque chose comme:

auto ptr = std::shared_ptr(new MyClass{arg1, arg2}, myDeleter);

Au lieu de:

auto ptr = std::make_shared<MyClass>(arg1, arg2);
11
skypjack

Tu ne peux pas. make_shared<T> transmet les arguments fournis au constructeur de type T. Il est utilisé pour le cas simple où vous voulez le deleter par défaut.

4
KyleKnoepfel

Si vous utilisez un suppresseur personnalisé, vous ne pouvez pas utiliser les fonctions make_unique ou make_shared lorsque vous créez un objet de pointeur intelligent. Étant donné que nous devons fournir notre suppresseur personnalisé, ces fonctions ne le supportent pas. 

N'utilisez pas make_unique ou make_shared si vous avez besoin d'un deleter personnalisé ou de l'adoption d'un pointeur brut ailleurs .

L'idée est que si vous avez besoin d'un moyen spécialisé pour supprimer votre objet, vous avez probablement besoin d'un moyen spécialisé pour le créer également. 

Disons nous un test de classe

#include <iostream>    
using namespace std;    
class Test
{
private : 
    int data; 
    public : 
    Test() :data{0}
    {
        cout << "Test constructor (" << data << ")" << endl;
    }
    Test(int d) : data{ d }
    {
        cout << "Test constructor (" << data << ")" << endl; 
    }
    int get_data() const { return data; }
    ~Test()
    { 
        cout << "Test Destructor (" << data << ')' << endl; 
    }
};

// main function. 
int main()
{
   // It's fine if you use  make_shared and custom deleter like this
   std::shared_ptr<Test> ptr(new Test{1000},
            [](Test *ptr)
            {
                cout << "some Code that you want to execute "; 
                delete ptr;
            });
         return 0;
}

Mais si vous utilisez la fonction make_shared, vous obtiendrez une erreur de compilation.

std::shared_ptr<Test> ptr = make_shared<Test>(1000,
            [](Test *ptr){
               cout << "some Code that you want to execute "; 
               delete ptr;
            });

En gros, la fonction make_shared est un wrapper pour new et delete et si vous voulez un suppresseur personnalisé, vous devez fournir vos propres new et delete 

0
user9311338