Supposons que j'ai le code suivant:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
Est-ce sûr? Ou doit-il ptr
être casté char*
avant la suppression?
c++
memory-management
casting
void-pointers
An̲̳̳drew
la source
la source
Réponses:
Cela dépend de «sûr». Cela fonctionnera généralement parce que les informations sont stockées avec le pointeur sur l'allocation elle-même, de sorte que le désallocateur puisse les renvoyer au bon endroit. En ce sens, il est "sûr" tant que votre allocateur utilise des balises de limite internes. (Beaucoup le font.)
Cependant, comme mentionné dans d'autres réponses, la suppression d'un pointeur vide n'appellera pas de destructeurs, ce qui peut être un problème. En ce sens, ce n'est pas «sûr».
Il n'y a aucune bonne raison de faire ce que vous faites comme vous le faites. Si vous souhaitez écrire vos propres fonctions de désallocation, vous pouvez utiliser des modèles de fonction pour générer des fonctions avec le type correct. Une bonne raison de le faire est de générer des allocateurs de pool, qui peuvent être extrêmement efficaces pour des types spécifiques.
Comme mentionné dans d'autres réponses, il s'agit d' un comportement non défini en C ++. En général, il est bon d'éviter les comportements indéfinis, bien que le sujet lui-même soit complexe et rempli d'opinions contradictoires.
la source
sizeof(T*) == sizeof(U*)
for allT,U
suggère qu'il devrait être possible d'avoir unevoid *
implémentation de garbage collector non basée sur un modèle . Mais alors, lorsque le gc doit effectivement supprimer / libérer un pointeur, exactement cette question se pose. Pour le faire fonctionner, vous avez soit besoin de wrappers de destructeurs de fonctions lambda (urgh), soit d'une sorte de "type en tant que données" dynamique qui permet des allers-retours entre un type et quelque chose de stockable.La suppression via un pointeur void n'est pas définie par le standard C ++ - voir section 5.3.5 / 3:
Et sa note de bas de page:
.
la source
NULL
quoi faire une différence pour la gestion de la mémoire d'application?Ce n'est pas une bonne idée et pas quelque chose que vous feriez en C ++. Vous perdez vos informations de type sans raison.
Votre destructeur ne sera pas appelé sur les objets de votre tableau que vous supprimez lorsque vous l'appelez pour des types non primitifs.
Vous devez à la place remplacer nouveau / supprimer.
La suppression du void * libérera probablement votre mémoire correctement par hasard, mais c'est faux car les résultats ne sont pas définis.
Si, pour une raison inconnue de moi, vous avez besoin de stocker votre pointeur dans un vide * alors libérez-le, vous devez utiliser malloc et free.
la source
La suppression d'un pointeur vide est dangereuse car les destructeurs ne seront pas appelés sur la valeur vers laquelle il pointe réellement. Cela peut entraîner des fuites de mémoire / ressources dans votre application.
la source
la question n'as pas de sens. Votre confusion peut être en partie due au langage bâclé que les gens utilisent souvent avec
delete
:Vous utilisez
delete
pour détruire un objet qui a été alloué dynamiquement. Pour ce faire, vous formez une expression de suppression avec un pointeur vers cet objet . Vous ne "supprimez jamais un pointeur". Ce que vous faites réellement, c'est "supprimer un objet identifié par son adresse".Maintenant, nous voyons pourquoi la question n'a aucun sens: un pointeur vide n'est pas "l'adresse d'un objet". C'est juste une adresse, sans aucune sémantique. Il peut provenir de l'adresse d'un objet réel, mais cette information est perdue, car elle a été codée dans le type du pointeur d'origine. La seule façon de restaurer un pointeur d'objet est de convertir le pointeur void en un pointeur d'objet (ce qui oblige l'auteur à savoir ce que signifie le pointeur).
void
lui-même est un type incomplet et donc jamais le type d'un objet, et un pointeur vide ne peut jamais être utilisé pour identifier un objet. (Les objets sont identifiés conjointement par leur type et leur adresse.)la source
delete
peut être une valeur de pointeur nul, un pointeur vers un objet non-tableau créé par une nouvelle expression précédente , ou un pointeur vers un sous-objet représentant une classe de base d'un tel objet. Sinon, le comportement n'est pas défini. " Donc si un compilateur accepte votre code sans diagnostic, ce n'est rien d'autre qu'un bogue dans le compilateur ...delete void_pointer
. C'est un comportement indéfini. Les programmeurs ne doivent jamais invoquer un comportement indéfini, même si la réponse semble faire ce que le programmeur voulait faire.Si vous devez vraiment le faire, pourquoi ne pas couper l'homme du milieu (les
new
etdelete
opérateurs) et appeler le mondeoperator new
etoperator delete
directement? (Bien sûr, si vous essayez d'instrumenter les opérateursnew
etdelete
, vous devez en fait réimplémenteroperator new
etoperator delete
.)Notez que contrairement à
malloc()
,operator new
jettestd::bad_alloc
en cas d'échec (ou appelle lenew_handler
si on est enregistré).la source
Parce que char n'a pas de logique destructrice spéciale. CECI ne fonctionnera pas.
L'acteur ne sera pas appelé.
la source
Si vous souhaitez utiliser void *, pourquoi n'utilisez-vous pas simplement malloc / free? new / delete est plus qu'une simple gestion de la mémoire. Fondamentalement, new / delete appelle un constructeur / destructeur et il se passe plus de choses. Si vous n'utilisez que des types intégrés (comme char *) et que vous les supprimez via void *, cela fonctionnerait mais ce n'est toujours pas recommandé. En fin de compte, utilisez malloc / free si vous souhaitez utiliser void *. Sinon, vous pouvez utiliser les fonctions de modèle pour votre commodité.
la source
Beaucoup de gens ont déjà commenté en disant que non, il n'est pas sûr de supprimer un pointeur vide. Je suis d'accord avec cela, mais je voulais également ajouter que si vous travaillez avec des pointeurs vides afin d'allouer des tableaux contigus ou quelque chose de similaire, vous pouvez le faire avec
new
afin que vous puissiez utiliser endelete
toute sécurité (avec, ahem , un peu de travail supplémentaire). Cela se fait en attribuant un pointeur vide à la région mémoire (appelée «arène»), puis en fournissant le pointeur vers l'arène à nouveau. Consultez cette section dans la FAQ C ++ . Il s'agit d'une approche courante pour l'implémentation de pools de mémoire en C ++.la source
Il n'y a guère de raison de faire cela.
Tout d'abord, si vous ne connaissez pas le type de données, et tout ce que vous savez c'est que c'est
void*
, alors vous devriez vraiment simplement traiter ces données comme un blob sans type de données binaires (unsigned char*
), et utilisermalloc
/free
pour les gérer . Cela est parfois nécessaire pour des éléments tels que les données de forme d'onde, etc., où vous devez transmettre desvoid*
pointeurs vers des apis C. C'est très bien.Si vous faites connaître le type des données (il a un cteur / dtor), mais pour une raison quelconque vous retrouviez avec un
void*
pointeur (pour une raison quelconque vous avez) alors vous devriez vraiment jeter de nouveau le type que vous savez à être , et invoquez-delete
le.la source
J'ai utilisé void *, (aka types inconnus) dans mon framework pendant la réflexion de code et d'autres exploits d'ambiguïté, et jusqu'à présent, je n'ai eu aucun problème (fuite de mémoire, violations d'accès, etc.) de la part des compilateurs. Uniquement les avertissements dus au fait que l'opération n'est pas standard.
Il est parfaitement logique de supprimer un inconnu (void *). Assurez-vous simplement que le pointeur suit ces instructions, sinon il peut ne plus avoir de sens:
1) Le pointeur inconnu ne doit pas pointer vers un type qui a un déconstructeur trivial, et donc lorsqu'il est converti en pointeur inconnu, il ne doit JAMAIS ÊTRE SUPPRIMÉ. Ne supprimez le pointeur inconnu qu'APRÈS l'avoir renvoyé dans le type ORIGINAL.
2) L'instance est-elle référencée comme un pointeur inconnu dans la mémoire liée à la pile ou au tas? Si le pointeur inconnu fait référence à une instance sur la pile, il ne doit JAMAIS ÊTRE SUPPRIMÉ!
3) Êtes-vous sûr à 100% que le pointeur inconnu est une région de mémoire valide? Non, alors il ne devrait JAMAIS ÊTRE SUPPRIMÉ!
En tout, il y a très peu de travail direct qui peut être fait en utilisant un type de pointeur inconnu (void *). Cependant, indirectement, le void * est un excellent atout sur lequel les développeurs C ++ peuvent s'appuyer lorsque l'ambiguïté des données est requise.
la source
Si vous voulez juste un tampon, utilisez malloc / free. Si vous devez utiliser new / delete, considérez une classe wrapper triviale:
la source
Pour le cas particulier de char.
char est un type intrinsèque qui n'a pas de destructeur spécial. Les arguments relatifs aux fuites sont donc sans objet.
sizeof (char) est généralement un, donc il n'y a pas non plus d'argument d'alignement. Dans le cas d'une plate-forme rare où le sizeof (char) n'est pas un, ils allouent une mémoire suffisamment alignée pour leur char. L'argument de l'alignement est donc également théorique.
malloc / free serait plus rapide dans ce cas. Mais vous perdez std :: bad_alloc et devez vérifier le résultat de malloc. Il serait peut-être préférable d'appeler les opérateurs globaux new et delete car cela contournerait l'intermédiaire.
la source
new
réellement défini pour lancer. Ce n'est pas vrai. Il dépend du compilateur et du commutateur du compilateur. Voir par exemple les/GX[-] enable C++ EH (same as /EHsc)
commutateurs MSVC2019 . De plus, sur les systèmes embarqués, beaucoup choisissent de ne pas payer les taxes de performance pour les exceptions C ++. Donc la phrase commençant par "Mais vous perdez std :: bad_alloc ..." est discutable.