Pourquoi avons-nous même besoin de la & # 8220; supprimer [] & # 8221; opérateur?
-
05-07-2019 - |
Question
C’est une question qui me harcèle depuis quelque temps. J'ai toujours pensé que C ++ aurait dû être conçu de manière à ce que "& supprime" opérateur (sans crochets) fonctionne même avec le "nouveau []" opérateur.
À mon avis, en train d'écrire ceci:
int* p = new int;
devrait être équivalent à l'allocation d'un tableau de 1 élément:
int* p = new int[1];
Si cela est vrai, la fonction "supprimer" L’opérateur peut toujours supprimer des tableaux et nous n’aurions pas besoin de l’option " delete [] " opérateur.
Existe-t-il une raison quelconque pour laquelle l'option " delete [] " opérateur a été introduit en C ++? La seule raison pour laquelle je peux penser est que l'allocation de tableaux a un faible encombrement mémoire (vous devez stocker la taille du tableau quelque part), de sorte que la distinction "supprimer" vs " delete [] " était une petite optimisation de la mémoire.
La solution
C’est pour que les destructeurs des éléments individuels soient appelés. Oui, pour les tableaux de POD, il n'y a pas beaucoup de différence, mais en C ++, vous pouvez avoir des tableaux d'objets avec des destructeurs non triviaux.
Maintenant, votre question est la suivante: pourquoi ne pas créer nouveau
et supprimer
se comporter comme nouveau []
et supprimer []
et se débarrasser de new []
et de delete []
? Je reviendrais sur "Design and Evolution" de Stroustrup livre où il a dit que si vous n'utilisez pas les fonctionnalités C ++, vous ne devriez pas les payer (au moins au moment de l'exécution). En l'état actuel des choses, un nouveau
ou un delete
se comportera aussi efficacement que malloc
et libre
. Si delete
avait la signification delete []
, il y aurait une surcharge supplémentaire au moment de l'exécution (comme l'a souligné James Curran).
Autres conseils
Zut, j'ai raté toute la question, mais je vais laisser ma réponse originale en sidenote. La raison pour laquelle nous avons delete [], c’est qu’il ya longtemps que nous avions delete [cnt]. Même aujourd’hui, si vous écrivez delete [9] ou delete [cnt], le compilateur ignore tout ce qui se trouve entre [] mais compile bien. A cette époque, le C ++ était d'abord traité par un frontal, puis transmis à un compilateur C ordinaire. Ils ne pouvaient pas faire le tour de stocker le compte quelque part sous le rideau, peut-être qu'ils ne pouvaient même pas y penser à ce moment-là. Et pour la compatibilité ascendante, les compilateurs ont probablement utilisé la valeur donnée entre [] comme nombre de tableaux. S'il n'y en a pas, ils ont obtenu le nombre à partir du préfixe, ce qui a fonctionné dans les deux sens. Plus tard, nous n’avons rien saisi entre [] et tout a fonctionné. Aujourd'hui, je ne pense pas que "supprimer [] " est nécessaire mais les implémentations l'exigent ainsi.
Ma réponse originale (ça manque le point) ::
" supprimer " supprime un seul objet. "supprimer [] " supprime un tableau d'objets. Pour que delete [] fonctionne, l'implémentation conserve le nombre d'éléments dans le tableau. Je viens de vérifier cela en déboguant du code ASM. Dans l’implémentation (VS2005) que j’ai testée, le compte était stocké comme préfixe du tableau d’objets.
Si vous utilisez " delete [] " sur un seul objet, la variable count est un déchet, de sorte que le code se bloque. Si vous utilisez " supprimer " pour un tableau d'objets, à cause d'une certaine incohérence, le code se bloque. J'ai testé ces cas tout à l'heure!
"delete" supprime simplement la mémoire allouée au tableau. " déclaration dans une autre réponse n'est pas juste. Si l'objet est une classe, delete appellera le DTOR. Il suffit de placer un point d'arrêt dans le code DTOR et de supprimer l'objet. Le point d'arrêt apparaîtra.
Ce qui m’est venu à l’esprit, c’est que, si le compilateur & amp; Les bibliothèques supposaient que tous les objets alloués par " new " sont des tableaux d'objets, vous pouvez appeler " delete " pour des objets simples ou des tableaux d'objets. Un seul objet serait juste le cas particulier d’un tableau d’objets ayant un compte de 1. Peut-être qu’il me manque quelque chose, de toute façon ...
Puisque tout le monde semble avoir oublié le sens de votre question, j'ajouterai simplement que j'avais la même pensée il y a un an et que je n'ai jamais pu obtenir de réponse.
La seule chose à laquelle je peux penser, c’est qu’il existe un très petit supplément de temps système qui permet de traiter un seul objet comme un tableau (inutile "& code; pour> int (= i; 0; i < 1; ++ i ) ")
Ajouter ceci car aucune autre réponse ne l'aborde actuellement:
Le tableau delete []
ne peut jamais être utilisé sur une classe pointeur vers base - pendant que le compilateur enregistre le nombre d'objets lorsque vous appelez new []
, il ne stocke pas les types ou les tailles des objets (comme l'a souligné David, en C ++, vous payez rarement pour une fonctionnalité que vous n'utilisez pas). Cependant,
struct Base { virtual ~Base(); };
struct Derived : Base { };
int main(){
Base* b = new Derived;
delete b; // this is good
Base* b = new Derived[2];
delete[] b; // bad! undefined behavior
}
Cependant, dans le cas contraire - destructeur non virtuel - scalaire delete
devrait être aussi économique que possible - il ne devrait pas vérifier le nombre d'objets, ni le type d'objet en cours. supprimé. Cela rend la suppression d'un type intégré ou d'un type de données ordinaire très ancien très économique, car le compilateur n'a qu'à appeler :: operator delete
et rien d'autre:
int main(){
int * p = new int;
delete p; // cheap operation, no dynamic dispatch, no conditional branching
}
Bien que le traitement de l'allocation de mémoire ne soit pas exhaustif, j'espère que cela contribuera à clarifier l'étendue des options de gestion de la mémoire disponibles en C ++.
Marshall Cline a des informations sur ce sujet .
delete []
garantit que le destructeur de chaque membre est appelé (si applicable au type) tandis que delete
supprime simplement la mémoire allouée pour le tableau.
En voici une bonne lecture: http: //www.informit. com / guides / content.aspx? g = cplusplus & amp; seqNum = 287
Et non, les tailles de tableau ne sont stockées nulle part en C ++. (Merci à tous d'avoir souligné que cette déclaration est inexacte.)
La réponse d'Aaron me laisse un peu perplexe et admet franchement que je ne comprends pas tout à fait pourquoi et où supprimer [] est nécessaire.
J'ai fait quelques expériences avec son exemple de code (après avoir corrigé quelques fautes de frappe). Voici mes résultats. Typos: ~ Base avait besoin d'un corps de fonction La base * b a été déclarée deux fois
struct Base { virtual ~Base(){ }>; };
struct Derived : Base { };
int main(){
Base* b = new Derived;
delete b; // this is good
<strike>Base</strike> b = new Derived[2];
delete[] b; // bad! undefined behavior
}
Compilation et exécution
david@Godel:g++ -o atest atest.cpp
david@Godel: ./atest
david@Godel: # No error message
Programme modifié avec supprimer [] supprimé
struct Base { virtual ~Base(){}; };
struct Derived : Base { };
int main(){
Base* b = new Derived;
delete b; // this is good
b = new Derived[2];
delete b; // bad! undefined behavior
}
Compilation et exécution
david@Godel:g++ -o atest atest.cpp
david@Godel: ./atest
atest(30746) malloc: *** error for object 0x1099008c8: pointer being freed was n
ot allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
Bien sûr, je ne sais pas si delete [] b fonctionne réellement dans le premier exemple; Je sais seulement que cela ne donne pas un message d'erreur du compilateur.