2
votes

les vérifications nulles ne sont pas requises pour const unique_ptr renvoyé par la méthode d'usine?

Nous avons beaucoup de code existant utilisant des pointeurs bruts, qui est jonché de vérifications nulles à presque chaque utilisation.

En essayant d'écrire du code plus récent plus proprement, nous essayons d'utiliser des méthodes de constructeur d'usine et des uniques_ptrs. p>

Ma question est, dans le code ci-dessous, une fois que nous avons notre objet créé en usine - sensorX - pouvons-nous l'utiliser dans le reste du code sans autres vérifications nulles, puisqu'il s'agit d'un const unique_ptr?

DeviceFactory.h

const auto sensorX = DeviceFactory::create<Sensor>(123, "sensorX");

Usage

class DeviceFactory
{
public:

    template<typename T>
    static unique_ptr<T> create(int id, std::string status)
    {
        auto device = unique_ptr<T>{ new T{ id, status } };

        if (!device) throw DeviceCreationException("device couldn't be created");

        return device;
    }
};


8 commentaires

Utilisez std :: make_unique


Nous ne sommes actuellement autorisés à utiliser que C ++ 11.


Ensuite, écrivez votre propre your_cool_namespace :: make_unique . Votre fonction est exactement la même que make_unique (seul le if (! Device) est redondant, il ne peut pas être nul, car new jette ). Pas besoin de réinventer la roue. Comment implémenter make-unique en C ++ 11 .


Votre throw est redondant car new le lancera s'il échoue à allouer.


(a) OP ne peut pas utiliser make_unique donc ils écrivent le leur. (b) Contributeur réprimande OP pour ne pas avoir utilisé make_unique . (c) OP souligne qu'ils ne peuvent pas utiliser make_unique . (d) Le contributeur demande à OP d'écrire le leur. (Vous ne pouviez pas inventer!)


Si vous pensez que votre code a plus de vérifications nulles qu'il ne devrait en avoir, voici quelques conseils de Herb Sutter sur les pointeurs, les pointeurs intelligents, les références et le passage de paramètres. herbsutter.com/2013/06/05/…


@Eljay, merci pour le lien Herb Sutter - nous prendrons évidemment ces points à bord pour des scénarios pertinents.


Voir aussi stackoverflow.com/q/55661068/94687 , où l'auteur "essaie de créer un code unique_ptr "et stackoverflow.com/a/46303030/94687 , qui montre comment" un value_ptr peut être écrit sans être nullable. (Notez qu'il y a des coûts supplémentaires lors du déplacement.) "


5 Réponses :


1
votes

Un unique_ptr peut toujours être nullptr , cela signifie simplement que cet objet gère la ressource sous-jacente, pas qu'il y a une ressource acquise.

Vous devez donc toujours vérifier si l'objet existe ou non, comme vous l'avez fait auparavant.

Remarque: qu'est-ce qui interdit à quelqu'un d'appeler votre usine et de ne pas stocker le résultat dans un unique_ptr ? Tant que cela est autorisé, vous devrez vérifier.


3 commentaires

"unique_ptr peut toujours être nullptr" - Peut-il dans mon exemple ci-dessus cependant, où j'ai déjà fait une vérification "if (! device)", et l'objet créé est const?


Vous passez ce unique_ptr en tant que const & partout?


oui, nous nous attendons à ajouter const dans la mesure du possible, là où il a été déclaré const en premier lieu. Notre ensemble d'outils comprend Klocwork et Resharper C ++ qui identifie les zones où nous pouvons ajouter const.



1
votes

Vous pouvez le faire même s'il ne s'agissait pas d'un const unique_ptr, à condition de ne jamais le réinitialiser à null.

Sur votre fonction d'usine,

        auto device = unique_ptr<T>{ new T{ id, status } };
        if (!device) throw DeviceCreationException("device couldn't be created");

la vérification dans la ligne deux devrait être supprimé car le simple new ne renvoie jamais null (enfin, à moins que vous ne fassiez quelque chose de vraiment horrible avec les valeurs par défaut de l'implémentation, je suppose).


1 commentaires

Vous dites "vous n'avez jamais besoin de vérifier si c'est nul, si vous ne le définissez jamais sur nul"?



1
votes

Tout d'abord le test:

template<typename T>
static T create(int id, std::string status)
{
    auto device = T{ id, status };

    return device;
}

n'a pas beaucoup de sens, car au moment où cette clause if est atteinte, ce ne serait pas possible pour device code> être nullptr (du moins pas pour le code donné)

Du point de vue de l'utilisateur, on sait que la fonction renvoie un code unique_ptr > en raison de la signature, mais vous devez lire la documentation, si l'appel DeviceFactory::create garantit qu'il ne contient pas nullptr . Et même si la documentation le garantissait, cela pourrait être une bonne idée de le tester vous-même, car vous ne le savez pas, ce sera toujours le cas à l'avenir.

La question est donc de savoir si vous enregistrez-le toujours sous const unique_ptr alors pourquoi avez-vous besoin d'un pointeur au lieu d'écrire:

if (!device) throw DeviceCreationException("device couldn't be created");

Mais revenons à votre question. Si const auto sensorX = ... est initialisé avec un unique_ptr qui ne contient pas nullptr , alors il est garanti qu'il ne sera pas nullptr pour sa durée de vie.


2 commentaires

"Donc, la question est de savoir si vous l'enregistrez toujours sous const unique_ptr, alors pourquoi avez-vous besoin d'un pointeur au lieu d'écrire:" Il semble que je n'ai pas vraiment posé ma question assez clairement, mais votre réponse @ t.niese est parfait pour la plupart de nos scénarios. Connaître le périphérique est non nul et constant lors de la création est ce que nous recherchons.


Bonne question: dans ce cas, faites par valeur. Cependant, ce n'est pas toujours possible lorsque vous avez des méthodes virtuelles pures ou lorsque vous ne pouvez pas vous déplacer (avant C ++ 17?)



1
votes

Une fois que nous aurons notre objet créé en usine - sensorX - pouvons-nous l'utiliser dans le reste du code sans autres vérifications nulles?

Comme d'autres réponses l'ont indiqué, la réponse est "non". Mais ce que vous pouvez faire, c'est utiliser le modèle owner<> ( Implémentation MS ), décrit dans les directives:

  • Celui que vous avez établi un pointeur (brut) vers l'objet créé en usine est non nul, enveloppez-le dans un owner ;
  • Demandez aux méthodes de prise de possession de prendre owner plutôt que T * comme paramètres.

Cela garantira, statiquement , que vous vous êtes assuré d'être propriétaire avant de passer l'appel - vous n'avez donc pas besoin de vérifier


0 commentaires

2
votes

On dirait que vous avez écrit votre propre version de std :: make_unique avec une API moins flexible. Je recommanderais d'aligner l'API car cela facilitera les mises à niveau.

Cela dit, votre question concerne le contrôle nul. Comme indiqué à plusieurs reprises dans les commentaires, vous n'avez pas besoin de vérifier après avoir obtenu un pointeur, car std :: bad_alloc devrait être lancé. Cependant, supposons que vous ayez une autre vérification qui lève une telle exception personnalisée, que la question principale est: quelle est votre API, y compris les conditions préalables et postérieures.

Dans la base de code sur laquelle nous travaillons, std :: unique_ptr n'est pas autorisé à être nullptr à moins d'être documenté. Dans ce cas: pas besoin de contrôle nul. Vous pouvez le documenter comme étant nullptr, ou renvoyer un optionnel à la place, ce qui pourrait indiquer l'état non valide.

Mon habitude serait de m'affirmer sur le pointeur avant utilisation ou après création. Cependant, vous pouvez automatiser ce travail en utilisant les désinfectants de Clang / Gcc. Le GSL a également des vérifications disponibles pour cette utilisation de nullptr.

Ma suggestion: affirmer après avoir créé la variable (ou utiliser des désinfectants), qui devrait trouver le bogue lors des tests si les post-conditions devaient changer. Bien qu'avec vos collèges, vous devriez convenir que unique_ptr ne devrait pas être autorisé à contenir null à moins d'être documenté ce que nullptr représente.


1 commentaires

Compris qu'il s'agissait d'une version plus pauvre de make_unique, dont nous n'étions pas au courant. "assert sur le pointeur avant l'utilisation ou après la création" - voulez-vous dire juste une fois après la création, avec l'hypothèse que si const, il ne peut pas être défini sur nullptr? Ou voulez-vous dire avant CHAQUE utilisation, comme les clauses de garde de Fowler?