8
votes

Opérateur privé Supprimer en C ++

Je travaille sur un mécanisme de collecte des ordures pour une famille d'objets dans l'un de mes projets. Ce que je veux, c'est allouer ces objets de manière dynamique avec nouveau et ne jamais avoir à appeler Supprimer .

Ceci est possible en surcharge NOUVEAU pour appeler un objet d'allocator spécialisé qui implémente GC pour ces objets (Collection de déclenchement lorsque trop de mémoire a été allouée). Cependant, j'ai un problème: l'utilisateur peut toujours simplement faire Supprimer sur ces objets et je ne le veux pas.

Fabrication Supprimer Privé est problématique à cause de la façon dont C ++ poignées échecs dans la construction - si opérateur nouveau est public, opérateur supprime devrait être trop. L'alternative qui est parfois suggérée est simplement de faire les deux nouvel opérateur et Supprimer privé et n'expose que les méthodes de création d'usine à l'utilisateur. Je peux faire cela, mais cela se sent moins propre et nécessite un code supplémentaire pour écrire.

EDIT: Une autre approche est de faire Supprimer vide (ou lancez une exception). Ensuite, pour libérer réellement les objets, mon GC appellera le destructeur explicitement, puis relâchez la mémoire avec l'opérateur global :: Supprimer .

Toute autre idée?


10 commentaires

Juste une idée: jetez une erreur lorsque quelqu'un essaie de supprimer un objet, qui devrait leur apprendre à ne pas;)


Offtopic: Comment suivez-vous cet objet est hors de portée? Pourquoi juste ne pas utiliser de pointeur partagé?


@Stormenet: Je pourrais faire cela bien que je souhaite utiliser Supprimer dans le collecteur des ordures (aurait pu en faire un un ami si cet opérateur était privé).


@RMFLOW: Je mettez la collection de portes Mark & ​​Sweep, avec le traçage des racines connues et tout ce que


"Make Operateur Supprimer vide" - mais comment la mémoire est-elle libérée lorsque le constructeur jette dans une nouvelle expression? Il y a une raison il doit être public, il ne s'agit pas simplement de le rendre symétrique ;-) [Edit - d'oh, je peux répondre à cela moi-même. Assurez-vous simplement que le GC peut l'obtenir même s'il n'a pas été construit. Je pense que si vous le faites, cependant, vous devez vous assurer que vous n'appelez pas le destructeur deux fois dans le cas où l'utilisateur supprime l'objet, l'expression de suppression appelle le destructeur, puis l'opérateur Supprimer, quel temps, et quelque temps plus tard, vous gc l'objet]


@Steve: Pouvez-vous rehrasser (ou écrire une réponse plus complète)? - Je ne suis pas sûr de te suivre.


Il existe une proposition N2670 pour Prise en charge de la collecte des ordures pour C ++ 0X, mais il ne discute pas de la manière dont la suppression doit être mise en œuvre. Peut-être que la question devrait être portée à l'attention du WG21 avant que C ++ 11 soit finalisé.


@Eli: Supposons que vous implayiez l'opérateur Supprimer pour ne rien faire, et l'utilisateur écrit le code suivant: Supprimer le nouveau GCEDFOO (); . Ensuite, l'expression de suppression appellera le destructeur avant d'appeler l'opérateur Supprimer. Cependant, vous avez dit que lorsque le GC vient à relâcher la mémoire, elle appellera d'abord le destructeur. Donc, le destructeur est appelé deux fois.


@Steve: Tu as raison (comme @jan Hudec dans son commentaire à la réponse de Benjamin) ... Je vais effacer la modification


@Eli: Il serait toujours possible de pirater cela. Par exemple, si votre système GC conserve comme méta-données si le destructeur a été appelé ou non, et tous les objets GC doivent hériter d'une classe de base dont les propres destructeurs définissent, puis lorsque vous relâchez la mémoire, vous pouvez éviter la destruction. J'essayais juste de dire qu'il y en a plus que simplement «l'opérateur Supprimer ne fait rien».


3 Réponses :


-2
votes
boost::shared_ptr<Type> ptr = boost::make_shared<Type>(); 
You never call new, you never call delete. Why reinvent the wheel?  Smart pointers really are the way to go.

8 commentaires

+1; Faites-le au niveau Smart-Pointer, pas le niveau nouveau / supprime.


Chris, je suis bien conscient de l'existence de partagé_ptr . Ce n'est pas ce que ma question est sur la question, bien que


@Chris: Comptage de référence! = Collection des ordures. Par exemple, partagé_ptr a de vrais problèmes de cycles.


@Ben: 'Shared_Ptr a de vrais problèmes avec les cycles' - Pourriez-vous élaborer s'il vous plaît? Je n'essayerais pas de réinventer la roue en 2011, je suppose que c'était mon point.


@Chris: Si deux objets contiennent des pointeurs les uns sur les autres, partagé_ptr ne sera jamais libre. Mais la collecte des ordures de marque et de balayage libérera les deux (supposant qu'aucun pointeurs du monde extérieur).


@Chris: Si l'objet c a un partage_ptr pour s'opposer à un, qui contient un Shared_Ptr à B, qui détient un Shared_Ptr à c, aucun d'entre eux ne sera jamais détruit. Je pense que c'est ce que Ben voulait dire des cycles.


@Chris: Pour ce numéro, "Ne pas réinventer la roue" utilise le collecteur de poubelles C ++ de Boehm, qui marque le balayage. Mais la GC de Boehm utilise beaucoup de tondeuses dépendantes de la plate-forme pour être aussi discrètes que possible - la collection portable de balayage de marque C ++ nécessite de l'aide de l'utilisateur, ce qui semble être ce que ELI va.


Je conviens que Shared_Ptr n'était pas la question, mais la déclaration Shared_Ptr a de véritables problèmes avec les cycles ne tient pas, car c'est exactement ce qu'est exactement ce que Faible_Ptr est là. Un design approprié avec Shared_ptr et Faid_Ptr est très prévisible sur la destruction de l'objet et ne produit pas de fuites de mémoire.



0
votes

Surcharge Supprimer comme non OP. (plus de caractères nécessaires)


3 commentaires

Cela laisse un problème: comment le GC supprime-t-il ces objets? J'ai planifié d'utiliser Supprimer , alhtugh je suppose que je peux simplement appeler le destructeur explicitement, puis relâcher la mémoire


Je suppose que tu as raison - je l'ai ajouté dans "Edit", je cherche toujours d'autres idées ;-)


Ne fonctionnera pas réellement. Le compilateur convertit un Supprimer x instruction sur x-> ~ Type (); Type :: Opérateur Supprimer (x) . C'est à dire. L'objet sera détruit par sera détruit d'abord et que la mémoire ne sera pas publiée par l'opérateur No-op Supprimer .



2
votes

Personnellement, je pense que l'idée de faire à la fois privée et à l'aide de l'usine est l'approche plus propre. Utilisation de nouveaux mais non supprimés (ou attribuer au pointeur intelligent) va confondre beaucoup de mainteneurs du code.

Si vous pouvez indiquer qu'un pointeur provient d'une usine collectée GC (ou appartient à une usine collectée GC) Ensuite, cela rendra le code moins déroutant de maintenir. En utilisant une usine, vous indiquez explicitement que l'usine GC est le propriétaire et doit donc maintenir la durée de vie de l'objet: xxx


5 commentaires

Le problème avec cette approche est lorsque j'ai des objets différents pour allouer de cette façon, chacun avec son propre constructeur (un nécessitant deux arguments, un autre pointeur et une référence, etc.) - Il s'agit de nombreuses duplications de code (méthodes d'usine invoquant constructeurs correspondants)


@Eli: Avec le bon support pour les fonctionnalités C ++ 0X, vous pourrez peut-être résoudre celui-ci avec des modèles de transfert et de variateur parfaits. Voir Vecteur :: emplace_back


@Steve: Ah, C ++ 0x, la promesse qui ne l'est pas ... Malheureusement, j'aimerais vraiment que cela traverse plusieurs compilateurs C ++ (MSVC inclus, soupirez.) Ce n'est donc probablement pas une option actuellement. Espérons que dans un avenir proche, ce sera!


@Eli Bendersky: Vous pouvez vous éloigner d'une méthode pour n paramètres en templatiquant tous les paramètres. Ainsi, vous n'avez besoin que d'une seule fonction pour tous les constructeurs qui prennent un paramètre. Une fonction pour tous les constructeurs qui prennent deux paramètres, etc. Pas une solution parfaite, mais il va réduire beaucoup le code.


@Eli: La prise en charge de MSVC pour C ++ 0X Les fonctionnalités peuvent être meilleures que ce que vous ne le pensez - ce n'est pas comme C99, MS est positif à propos de C ++ 0X et des plans plus ou moins une mise en œuvre complète. Mais bien sûr, le support de fonctionnalité n'est pas fiable sur plusieurs implémentations. Vous pouvez également jeter un coup d'oeil à ce que Boost fait, il existe des endroits où il souhaite utiliser des modèles variadiques et qu'il s'agit là où ils sont soutenus, sinon ce que Martin dit dans le commentaire ci-dessus, qui doit définir N modèles avec 0 ... N-1 Paramètres. La valeur typique de l'IIRC pour N est 10.