2008-09-16 11 views
10

Dire que j'ai le C++ suivant:la suppression d'un tampon à travers un type différent de pointeur?

char *p = new char[cb]; 
SOME_STRUCT *pSS = (SOME_STRUCT *) p; 
delete pSS; 

Est-ce sans danger selon la norme C++? Ai-je besoin de renvoyer vers un char* puis d'utiliser delete[]? Je sais que cela marchera dans la plupart des compilateurs C++, car il s'agit de plain-ordinary-data, sans aucun destructeur. Est-il garanti d'être sûr?

Répondre

6

Non, c'est un comportement non défini - un compilateur pourrait vraisemblablement faire quelque chose de différent, et comme le C++ d'entrée FAQ qui thudbang lié à dit, operator delete[] pourrait être surchargée pour faire quelque chose de différent à operator delete. Vous pouvez parfois vous en sortir, mais c'est aussi une bonne pratique de prendre l'habitude de faire correspondre delete [] avec new [] pour les cas où vous ne pouvez pas.

0

Bien que cette devrait travail, je ne pense pas que vous pouvez garantir à être en sécurité parce que le SOME_STRUCT est pas un char * (à moins qu'il est simplement un typedef). En outre, puisque vous utilisez différents types de références, si vous continuez à utiliser l'accès * p et que la mémoire a été supprimée, vous obtiendrez une erreur d'exécution.

2

standard C++ [5.3.5.2] déclare:

Si l'opérande a un type de classe, l'opérande est converti en un type de pointeur en appelant la conversion mentionné ci-dessus fonction, et l'opérande converti est utilisé à la place de l'opérande d'origine pour le reste de cette section. Dans l'alternative , la valeur de l'opérande de delete peut être une valeur de pointeur nulle. Si ce n'est pas une valeur de pointeur nulle, dans la première alternative (objet de suppression), la valeur de l'opérande de suppression doit être un pointeur vers un objet non-tableau ou un pointeur vers un sous-objet (1.8) représentant une base classe d'un tel objet (clause 10). Sinon, le comportement est indéfini. Dans la deuxième alternative (supprimer tableau), la valeur de l'opérande de suppression doit être la valeur du pointeur qui résultait d'une nouvelle expression de tableau précédente.7) Sinon, le comportement n'est pas défini. [Note: cela signifie que la syntaxe de l'expression de suppression doit correspondre au type de l'objet alloué par new, pas à la syntaxe de la nouvelle expression. -end note] [Note: un pointeur à un type const peut être l'opérande d'une expression de suppression; il n'est pas nécessaire de supprimer la constance (5.2.11) de l'expression de pointeur avant de l'utiliser comme opérande de l'expression de suppression. -end note]

4

J'en doute fortement.

Il y a beaucoup de façons douteuses de libérer de la mémoire, par exemple, vous pouvez utiliser delete sur votre tableau char (plutôt que delete[]) et il fonctionnera probablement très bien. I blogged en détail à ce sujet (des excuses pour l'auto-lien, mais c'est plus facile que de tout réécrire).

Le compilateur n'est pas tellement le problème que la plate-forme. La plupart des bibliothèques utilisent les méthodes d'allocation du système d'exploitation sous-jacent, ce qui signifie que le même code peut se comporter différemment sur Mac par rapport à Windows par rapport à Linux. J'ai vu des exemples de cela et chacun d'eux était un code discutable.

L'approche la plus sûre consiste à toujours allouer et libérer de la mémoire en utilisant le même type de données. Si vous allouez char s et de les retourner à tout autre code, vous pouvez être mieux fournir des méthodes spécifiques allouer/désallouer:

SOME_STRUCT* Allocate() 
{ 
    size_t cb; // Initialised to something 
    return (SOME_STRUCT*)(new char[cb]); 
} 

 

void Free(SOME_STRUCT* obj) 
{ 
    delete[] (char*)obj; 
} 

(Surcharger les opérateurs new et delete peut également être une option, mais je n'ai jamais aimé faire cela.)

0

Cela fonctionnera bien si la mémoire est pointée vers et le pointeur que vous pointez est à la fois POD. Dans ce cas, aucun destructeur ne serait appelé de toute façon, et l'allocateur de mémoire ne sait pas ou ne se soucie pas du type stocké dans la mémoire. Le seul cas est OK avec les types non-POD, si la pointe est un sous-type du pointeur (par exemple, vous pointez sur une voiture avec un véhicule *) et que le destructeur du pointeur a été déclaré virtuel.

0

Ce n'est pas sûr, et aucune des réponses jusqu'à présent ont souligné assez la folie de le faire. Tout simplement ne le faites pas, si vous vous considérez comme un vrai programmeur, ou voulez jamais travailler en tant que programmeur professionnel dans une équipe. Vous pouvez seulement dire que votre structure contient non destructeur à l'instant, mais vous êtes en train de jeter un mauvais piège possible du compilateur et du système pour l'avenir. En outre, votre code ne fonctionnera probablement pas comme prévu. Le meilleur que vous pouvez espérer est qu'il ne plante pas. Cependant je suspecte que vous obtiendrez lentement une fuite de mémoire, car les allocations de tableau par l'intermédiaire de nouveau allouent très souvent la mémoire supplémentaire dans les octets avant au pointeur retourné. Vous ne libérerez pas la mémoire que vous pensez être. Une bonne routine d'allocation de mémoire devrait prendre en compte cette inadéquation, tout comme des outils comme Lint etc.

Ne faites tout simplement pas cela, et éliminez de votre esprit tout ce qui vous a amené à penser à de telles absurdités.

0

J'ai changé le code pour utiliser malloc/free. Bien que je sache comment MSVC implémente new/delete pour plain-old-data (et SOME_STRUCT dans ce cas était une structure Win32, si simple C), je voulais juste savoir s'il s'agissait d'une technique portable.

Ce n'est pas le cas, alors je vais utiliser quelque chose qui est.

0

Si vous utilisez malloc/free au lieu de new/delete, malloc et free ne se soucient pas du type. Par conséquent, si vous utilisez un POD de type C (données anciennes, comme un type prédéfini ou struct), vous pouvez en faire un type et en libérer un autre. noter que c'est un style médiocre, même si cela fonctionne.

2

C'est une question très semblable à celui que je répondais ici: link text

En bref, non, ce n'est pas sûr selon la norme du C.Si, pour une raison quelconque, vous avez besoin d'un objet SOME_STRUCT alloué dans une zone de mémoire qui a une différence de taille de size_of(SOME_STRUCT) (et il vaut mieux être plus grand!), Alors il vaut mieux utiliser une fonction d'allocation brute comme operator new pour effectuer le allocation, puis la création de l'instance d'objet dans la mémoire brute avec un emplacement new. Le placement new sera extrêmement bon marché si le type d'objet n'a pas de constructeur.

void* p = ::operator new(cb); 
SOME_STRUCT* pSS = new (p) SOME_STRUCT; 

// ... 

delete pSS; 

Ceci fonctionnera la plupart du temps. Cela devrait toujours fonctionner si SOME_STRUCT est une structure POD. Il fonctionnera également dans d'autres cas si le constructeur de SOME_STRUCT ne lance pas et si SOME_STRUCT n'a pas de suppression d'opérateur personnalisée. Cette technique supprime également le besoin de moulages.

::operator new et ::operator delete sont les plus proches équivalent de C++ pour malloc et free et que ceux-ci (en l'absence de remplacements de classe) sont appelés, selon le cas par new et delete expressions qu'ils peuvent (avec précaution!) Être utilisés en combinaison.