Dans Delphi 7, Pourquoi puis-je attribuer une valeur à un const?
j'ai copié du code Delphi d'un projet à l'autre, et j'ai découvert qu'il ne se compilait pas dans le nouveau projet, mais dans l'ancien. Le code ressemble à ceci:
procedure TForm1.CalculateGP(..)
const
Price : money = 0;
begin
...
Price := 1.0;
...
end;
ainsi dans le nouveau projet, Delphi se plaint que "côté gauche ne peut pas être assignée à" - compréhensible! Mais ce code compile dans l'ancien projet. Donc ma question Est, pourquoi ? Est-il un commutateur de compilateur pour permettre consts pour être réaffectés? Comment est-ce que même travail? Je pensais que les consts étaient remplacés par leurs valeurs au moment de la compilation?
4 réponses
vous devez activer les constantes dactylographiées assignables. Projet> Options> Compilateur -> Assignables tapé "Constantes de la 151930920"
vous pouvez aussi ajouter {$J+}
ou {$WRITEABLECONST ON}
au fichier pas, ce qui est probablement mieux, car cela fonctionnera même si vous déplacez le fichier vers un autre projet.
les constantes déduites par Type ne peuvent être que des valeurs scalaires, c'est-à - dire des choses comme des entiers, des doubles, etc. Pour ces types de constantes, le compilateur remplace effectivement le symbole de la constante par la valeur de la constante chaque fois qu'il les rencontre dans des expressions.
constantes dactylographiées, d'autre part, peuvent être des valeurs structurées - tableaux et enregistrements. Ces types ont besoin de stockage réel dans l'exécutable - c'est-à-dire qu'ils ont besoin de stockage alloué pour eux de telle sorte que, lorsque le système d'exploitation charge l'exécutable, La valeur de la constante tapée est physiquement contenu à un endroit dans la mémoire.
pour expliquer pourquoi, historiquement, les constantes dactylographiées dans les premiers Delphes et leur prédécesseur, Turbo Pascal, sont inscriptibles (et donc essentiellement initialisées des variables globales), nous devons revenir à L'époque du DOS.
DOS fonctionne en mode réel, en termes x86. Cela signifie que les programmes ont un accès direct à la mémoire physique sans aucune MMU faire des mappages physiques virtuels. Lorsque les programmes ont un accès direct à la mémoire, Aucune protection mémoire n'est en vigueur. En d'autres termes, s'il y a de la mémoire à une adresse donnée, elle est à la fois lisible et écrivable en mode réel.
ainsi, dans un programme Turbo Pascal Pour DOS avec une constante dactylographiée, dont la valeur est attribuée à une adresse en mémoire à l'exécution, cette constante dactylographiée sera inscriptible. Il n'y a pas de MMU matériel se mettre en travers et empêcher le programme de lui écrire. De même, parce que Pascal n'a pas la notion de 'const'ness que C++ a, il n'y a rien dans le système de type pour vous arrêter. Beaucoup de gens en ont profité, puisque Turbo Pascal et Delphi n'ont pas encore initialisé les variables globales en tant que fonctionnalité.
passant à Windows, il y a une couche entre les adresses mémoire et les adresses physiques: l'Unité de gestion de mémoire. Cette puce prend l'index de la page (un masque décalé) de l'adresse mémoire à laquelle vous essayez d'accéder, et recherche les attributs de cette page dans son tableau de page . Ces attributs incluent lisible, writable, et pour les puces x86 modernes, des options non exécutables. Avec ce support, il est possible de marquer des sections de la .EXE or .DLL avec des attributs tels que lorsque le chargeur de Windows charge l'image exécutable dans la mémoire, il assigne les attributs de page appropriés pour les pages de mémoire qui mappent vers les pages de disque dans ces sections.
lorsque la version Windows 32 bits du compilateur Delphi est venu autour, il était donc logique de faire des choses comme const-like really const, comme L'OS a également cette caractéristique.
-
pourquoi: parce que dans les versions précédentes de Delphi les constantes dactylographiées étaient assignables par défaut pour préserver la compatibilité avec les versions plus anciennes où elles étaient toujours inscriptibles (Delphi 1 jusqu'à Pascal tôt).
La valeur par défaut a été modifiée pour rendre les constantes vraiment constantes... -
commutateur de compilateur: {$J+} or {$J -} {$WRITEABLECONST on} or {$WRITEABLECONST OFF}
Ou dans les options de projet pour le compilateur: vérifier assignable constantes dactylographiées - comment ça marche: si le compilateur peut calculer la valeur au moment de la compilation, il remplace le const par sa valeur partout dans le code, sinon il tient un pointeur vers une zone mémoire contenant la valeur, qui peut être écrite ou non.
- voir 3.
comme L'a dit Barry, Les gens ont profité des consts; L'une des façons dont cela a été utilisé, était de garder une trace des cas singleton. Si vous regardez une implémentation classique de singleton, vous verrez ceci:
// Example implementation of the Singleton pattern.
TSingleton = class(TObject)
protected
constructor CreateInstance; virtual;
class function AccessInstance(Request: Integer): TSingleton;
public
constructor Create; virtual;
destructor Destroy; override;
class function Instance: TSingleton;
class procedure ReleaseInstance;
end;
constructor TSingleton.Create;
begin
inherited Create;
raise Exception.CreateFmt('Access class %s through Instance only', [ClassName]);
end;
constructor TSingleton.CreateInstance;
begin
inherited Create;
// Do whatever you would normally place in Create, here.
end;
destructor TSingleton.Destroy;
begin
// Do normal destruction here
if AccessInstance(0) = Self then
AccessInstance(2);
inherited Destroy;
end;
{$WRITEABLECONST ON}
class function TSingleton.AccessInstance(Request: Integer): TSingleton;
const
FInstance: TSingleton = nil;
begin
case Request of
0 : ;
1 : if not Assigned(FInstance) then
FInstance := CreateInstance;
2 : FInstance := nil;
else
raise Exception.CreateFmt('Illegal request %d in AccessInstance', [Request]);
end;
Result := FInstance;
end;
{$IFNDEF WRITEABLECONST_ON}
{$WRITEABLECONST OFF}
{$ENDIF}
class function TSingleton.Instance: TSingleton;
begin
Result := AccessInstance(1);
end;
class procedure TSingleton.ReleaseInstance;
begin
AccessInstance(0).Free;
end;