0
votes

Quelle est la pratique pour la gestion d'un pointeur de ressources complexe en C ++ lors de l'attribution d'une nouvelle version de la ressource?

Dans la plupart de mes programmations maintenant un jour, je mets tout dans un pointeur intelligent et oubliez-le . La ressource est correctement gérée 99,9% du temps. C'est vraiment grand et meilleur qu'un mécanisme de collecte des ordures.

Cependant, une fois de temps en temps, la ressource détenue par un pointeur intelligent doit être explicitement libérée avant de pouvoir le réaffecter une nouvelle instance. Quelque chose comme ceci: xxx

si je manque l'appel r.reset () , la ressource peut soit utiliser une grande monture de mémoire ou de disque L'espace et réaffectant sans la première réinitialisation est susceptible de causer des problèmes sur des ordinateurs plus petits. Soit cela, soit la ressource est verrouillée de sorte qu'elle ne peut donc pas être réaffectée avant de libérer explicitement.

existe un motif / algorithme / quelque chose qui gère un tel cas de manière plus propre?


5 commentaires

Le problème avec une "quelle est la pratique de la manutention" x> en C ++ "de question est que, invariablement, lorsque vous posez cette question à trois développeurs C ++, vous obtiendrez quatre réponses différentes.


Je ne comprends pas ça. r = std :: make_shared (avec_this_id); fait r-> réinitialiser (); . Cela fait partie de ce qui le rend intelligent. Si vous appelez manuellement la libération, c'est la solution pour vous, alors il y a de gros problèmes quelque part.


@ Nathanoliver-Reinstatetemonica sur une réaffectation, le R-> réinitialiser () L'appel se produit lorsque la variable est attribuée, ce qui signifie que l'instance actuelle de la ressource est conservée jusqu'à une fois la nouvelle instance allouée. Si la ressource détient une serrure contre la ressource, sans la réinitialisation explicite, elle échoue. Un autre problème potentiel est lorsque vous allouez un très grand tampon. Cela va être alloué deux fois jusqu'à ce que la mission se produise (bien que cela fonctionne à moins que vous ne manquiez pas de mémoire).


Voulez-vous vraiment appeler r-> réinitialiser () (donc my_resource :: réinitialiser ) ou r.reset () (donc Shared_ptr :: réinitialiser )?


@ N314159 AH, désolé, oui r.reset () , j'ai fait une mise à jour. Merci d'avoir fait remarquer cela.


3 Réponses :


0
votes

Normalement, vous venez de mettre le réinitialiser () à l'intérieur du destructeur du My_Resource Classe.

Si cela ne convient pas à quelque raison que ce soit, lors de l'instanciation, vous pouvez mettre une fonction de destructeur personnalisée dans le std :: Shared_ptr qui appelle le réinitialiser () puis Supprime la ressource.

Si le partagé_ptr n'atteint pas le refcount 0 - Êtes-vous sûr de l'utiliser correctement? Il y a std :: faible_ptr - une classe d'utilitaire pour Shared_ptr - faite dans l'ensemble du but de la sécurité et de la répartition en temps utile.

aussi, pourquoi utiliser make_shared et instatiez le nouveau my_resource au lieu d'utiliser simplement une fonction init de my_resource qui appellera automatiquement () si nécessaire?


3 commentaires

Le réinitialiser () fonctionne. Je n'ai pas de problème loop . Il s'agit davantage de type de ressource spécifique qui nécessite une instance de courant avant de pouvoir être réaffectée. (Par exemple, parce que l'instance actuelle a une serrure qui empêcherait une nouvelle allocation jusqu'à ce que le verrou soit libéré.)


@Alexiswilke J'ai mal compris votre question. Il semblait que vous devez appeler une fonction spécifique des ressources avant la destructeur de la ressource. Il serait plus clair si vous avez écrit r = {}; au lieu de r.reset (); partiellement, car dans un autre endroit que vous avez écrit r-> réinitialiser ();


Ouais, j'ai corrigé le deuxième cas dans la question (au moins). Malheureusement , vous ne pouvez pas modifier et corriger les commentaires ...



1
votes

Bien que cela puisse sembler bizarre, je pense que cela peut être mieux exprimé comme une sémantique de déplacement. Voici pourquoi:

Le nouvel objet de ressource que vous créez est un remplacement de l'ancien. Supposons que le remplacement est transparent à vos utilisateurs (c'est-à-dire qu'ils perçoivent le nouvel objet de ressources et l'ancien comme identique). Par conséquent, c'est comme si vous avez effectué une copie imaginée de l'ancien objet pour en obtenir le nouveau, puis détruisez l'ancien. Cela correspond à l'intention de déplacer la sémantique.

donc my_resource doit avoir un constructeur de déplacement: xxx


0 commentaires

1
votes

Je vois fondamentalement deux façons de s'approcher cela. Le premier consiste à envelopper la séquence de réinitialisation-Attribuer une seule fonction et ne jamais attribuer directement. Je ferais probablement cela comme tel xxx

c'est assez facile et rapide à faire, mais il ne vous évitera pas d'appeler accidentellement l'affectation directement et je ne vois pas un moyen de faire cela Si vous continuez à utiliser partagé_ptr .

L'autre alternative écrit une enveloppe autour de partagée_ptr qui vient d'envoyer la plupart des appels de fonction et des modifications réinitialiser et affectation de telle sorte qu'il fonctionnera d'abord puis crée la nouvelle ressource. C'est un peu de travail à faire et il est facile d'obtenir un bogue dans ceci (surtout si vous faites gâcher des références universelles si vous essayez de vous épargner des constructeurs). Il sera également gênant d'interagir avec un autre code qui utilise des pointeurs STD Smart et constituera un processus de refactorisation majeur. Mais vous ne pouvez pas le gâcher en appelant accidentellement une mission (au moins probablement pas).

Notez également que la bibliothèque standard réinitialise intentionnellement dans cet ordre de sorte que nous ne supprimons pas l'ancienne ressource si la nouvelle allocation jette.


2 commentaires

Pour éviter les appels directs vers le constructeur, nous pouvons rendre cette fonction statique et cacher le constructeur ordinaire comme privé. Ce serait proche d'avoir un motif d'usine.


Cela ressemble plus à la typographie forte, comme lorsque vous créez des types d'emballage pour des mètres et des secondes, même s'ils ne sont que des chiffres de telle sorte que vous ne puissiez pas les ajouter par erreur.