C ++ standard dit que la modification d'un objet déclaré à l'origine const code> est un comportement indéfini. Mais alors comment fonctionnent les constructeurs et les destructeurs?
class Class {
public:
Class() { Change(); }
~Class() { Change(); }
void Change() { data = 0; }
private:
int data;
};
//later:
const Class object;
//object.Change(); - won't compile
const_cast<Class&>( object ).Change();// compiles, but it's undefined behavior
5 Réponses :
La norme permet explicitement les constructeurs et les destructeurs de traiter les objets Un constructeur peut être invoqué pour un et 12.4 / 2 "destructeurs": p>
Un destructeur peut être invoqué pour un comme arrière-plan, Stroustrup dit dans "Conception et évolution de C ++" (13.3.2 Raffinement de la définition de la définition de Pour vous assurer que des objets, mais pas tous, ... p>
Un objet déclaré Lors de la conception initiale Notez que, avec ce raffinement des règles, la signification de const code>. à partir de 12.1 / 4 "Constructeurs": P>
const code>,
ou
const volatile code> objet. ...
const code> et
volatile code> sémantique (7.1.5.1) ne sont pas appliqués sur un objet en construction. Une telle sémantique n'entre en vigueur que lorsque le constructeur de l'objet le plus dérivé (1.8) se termine. P>
blockQuote>
const code>,
volatile code> ou
const volatile code> objet. ...
const code> et
volatile code> sémantique (7.1.5.1) ne sont pas appliqués sur un objet sous la destruction. Cette sémantique arrête d'être en vigueur une fois que le destructeur de l'objet le plus dérivé (1.8) commence. P>
blockQuote>
Const code>): p>
const code> pourraient être placés la mémoire en lecture seule (ROM), j'ai adopté la règle que tout objet comportant un constructeur (c'est-à-dire une initialisation d'exécution requise ) Ne peut pas être placé dans la ROM, mais d'autres objets
const code> peuvent. P>
const code> est considéré immuable à partir de l'achèvement du constructeur jusqu'au début de son destructeur. Le résultat d'une écriture à l'objet entre ces points est jugé non défini. P>
const code>, je me souviens de discuter que l'idéal
const code> serait un objet qui est écritable jusqu'à ce que le constructeur soit exécuté, puis devient en lecture seule par du matériel. La magie, et enfin sur l'entrée dans le destructeur devient à nouveau enrichie. On pourrait imaginer une architecture marquée qui a effectivement fonctionné de cette façon. Une telle mise en œuvre provoquerait une erreur d'exécution si quelqu'un pouvait écrire sur un objet défini
const code>. D'autre part, quelqu'un pourrait écrire à un objet non défini
const code> qui avait été passé sous forme de référence
const code> ou pointeur. Dans les deux cas, l'utilisateur devra jeter
const code> en premier. L'implication de cette vue est celle de la création
const code> pour un objet qui a été défini à l'origine
const code>, puis écrit à elle est au mieux non définie, alors que de faire la même chose à un objet qui n'a pas été défini à l'origine
const code> est légal et bien défini. p>
const code> ne dépend pas de si un type a un constructeur ou non; En principe, ils font tous. Tout objet déclaré
const code> peut maintenant être placé dans ROM, être placé dans des segments de code, être protégé par le contrôle d'accès, etc., afin de vous assurer qu'il ne mutation après avoir reçu sa valeur initiale. Toutefois, une telle protection n'est pas nécessaire, car les systèmes actuels ne peuvent en général pas protéger tous les
const code> de toutes les formes de corruption. P>
blockQuote>
@Stingraysc: Qu'en est-il de mutable code>?
MUTABLE CODE> Composants de
Const CODE> Les objets peuvent être modifiés - ces parties ne sont donc pas
const code>.
+1! Je trouve qu'il est encore plus important de dire que «la Semantitique Constitution et volatilité (7.1.5.1) ne sont pas appliquées sur un objet en construction / destruction» dans le même paragraphe.
@LitB: J'ai ajouté ces bits plus importants.
@Michael Burr: Je vais poser que pour la plupart, sinon toutes, les implémentations const code> ne sont pas attribuées dans la ROM, en particulier non pas le non-
mutable code> parties d'entre eux seul. Pourquoi une mise en œuvre subirait-elle des frais généraux de «verrouillage» des octets de mémoire individuels (pour faire face au boîtier
mutable code>)? Cela peut ne pas être le cas pour les types intrinsèques. La phrase clé est "const sémantique i>". C'est une construction logique appliquée par le compilateur sur aide i> Garantie de la correction de la correction, rien de plus.
@Stingraysc: Vous avez raison pour laquelle une implémentation ne doit pas nécessairement mettre un objet const code> en mémoire en lecture seule (ou la mémoire pouvant être commutée pour être en lecture seule). Je ne pensais pas que c'est ce que je disais; Cependant, Stroustrup envisageait que cela pourrait être possible et que cela a permis de se produire si une mise en œuvre pourrait le retirer. La partie la plus importante de la réponse (même si ce n'est pas la partie la plus longue) est que la norme permet à CTORS et à DTors d'agir sur des objets
const code>. Le comportement est tel que l'objet n'allait pas '
const code>' jusqu'à ce que la CTOR soit terminée et cesse d'être
'const code>' pour le DTOR.
Cela me semble assez intuitif; Dans le même sens qu'un int code> ne va pas
const code> jusqu'à ce qu'il soit initialisé. Le boîtier destructeurs est spécial bien sûr, mais il devrait rarement être nécessaire de modifier la mémoire de l'objet pendant la destruction, non? (Réduction destructeurs amateurs qui pensent avoir besoin de zéro les membres ou à une autre chose). Mais il est certainement logique que le
const code> d'un objet se termine au début du destructeur. L'objet n'est pas utilisable après ce point et la mémoire est sur le point d'être libérée.
@Stingraysc: Je ne considère pas ces amateurs. J'envisageai la mise à zéro des pointeurs dans un destructeur d'être une bonne programmation défensive. Cette pratique peut attraper l'utilisation d'un objet à travers un pointeur erroné après la destruction d'une manière relativement sûre et évidente à travers un segfault par opposition à l'attente de la mémoire à corrompre.
@Omnifarious: Eh bien, vous allez avoir la faute SEG que vous nulez ou non la mémoire! Vous allez devoir essayer d'expliquer cela à nouveau ...
@Stingraysc: Lorsque vous accédez à la mémoire de l'objet mort et suivez l'un de ses pointeurs, vous obtiendrez une erreur de segmentation si ce pointeur a été mis à zéro lorsque l'objet a été détruit.
@Omnifarious: Vous obtiendrez probablement une faute SEG accéder à la mémoire de l'objet d'abord i> en premier lieu. Deuxièmement, si vous êtes religieusement des pointeurs de zéro sur les destructeurs, il n'est même pas possible d'accéder à la mémoire de cet objet de mort Premier i>, sauf si vous avez un design horrible en premier lieu!
Il serait intéressant et cool Si un compilateur pouvait exécuter des formes de constructeurs pendant la phase de compilation et écrire l'objet construit dans la mémoire en lecture seule.
@Zanlynx: Je suppose que avec C ++ 11 consexpr code>, de bons compilateurs devraient faire exactement cela.
Constenness pour un type défini par l'utilisateur est différent de la Constance d'un type intégré. Constenness lorsqu'il est utilisé avec des types définis par l'utilisateur, est dit "Constance logique". Le compilateur applique que seules les fonctions membres déclarées "Cons" peuvent être appelées objet (ou pointeur, ou référence). Le compilateur ne peut pas allouer l'objet dans une zone de mémoire en lecture seule, car les fonctions membres non-const em> doit pouvoir modifier l'état de l'objet (et même const em> les fonctions membres doivent être capable de quand une variable de membre est déclarée Pour les types intégrés, je pense que le compilateur est autorisé à allouer l'objet en lecture seule (si la plate-forme prend en charge une telle chose). Donc, je jette la const et la modification de la variable pourrait entraîner une défaillance de protection de la mémoire de course. P> mutable code>). p>
La norme n'en dis pas vraiment sur la manière dont la mise en œuvre le fait fonctionner, mais l'idée de base est assez simple: le const code> s'applique à l'objet em>, pas (nécessairement) à la mémoire dans laquelle l'objet est stocké. Puisque le CTOR fait partie de ce qui crée l'objet, ce n'est pas vraiment un objet avant (peu de temps après), le CTOR revient. De même, puisque le DTOR participe à la destruction de l'objet, il ne fonctionne plus vraiment sur un objet complet non plus. P>
Voici une façon d'ignorer la norme pourrait entraîner un comportement incorrect. Considérons une situation comme celle-ci:
void cheat(const Value &cv) { Value &v = const_cast<Value &>(cv); v.set(v.get() + 2); }
Pouvez-vous nommer une plate-forme dans laquelle ce code ne fonctionnera pas correctement?
Le comportement indéfini de @Hkbattousai IIT, alors réfléchissant aux spécificités de l'endroit où il pourrait arriver de faire ce que vous pensez être "approprié" (ce qui est?) est inutile. Les termes tels que «correctement» perdent toute la signification lorsque UB est impliqué: il n'ya aucun moyen de dire quel résultat de drawit () code> serait correct, car le code est fondamentalement brisé et auto-contradictoire. C'est pourquoi UB est un concept.
Élaborer sur ce que Jerry Coffin a déclaré: la norme permet d'accéder à un objet de const indéfini, uniquement si cet accès se produit pendant la durée de vie de l'objet. P>
7.1.5.1/4: p>
Sauf que tout membre de classe déclaré mutable (7.1.1) peut être modifié, toute tentative de modification d'un objet Const au cours de sa durée de vie (3.8) entraîne un comportement non défini. P> blockQuote>
La durée de vie de l'objet commence qu'après la fin du constructeur. P>
3.8 / 1: p>
La durée de vie d'un objet de type T commence lorsque: p>
- Stockage avec l'alignement et la taille appropriés pour le type T est obtenu et LI>
- Si T est un type de classe avec un constructeur non trivial (12.1), l'appel du constructeur est terminé. Li> ul> blockQuote>
Vous pouvez également jeter un coup d'œil aux réponses de cette question Stackoverflow.com/ Questions / 755196 / Suppression-A-Cons-Pointer concernant pourquoi il est requis.