0% encontró este documento útil (0 votos)
16 vistas268 páginas

Guía de Free Pascal 3.2.2

Guía de referencia del lenguaje de programación Pascal en español
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
16 vistas268 páginas

Guía de Free Pascal 3.2.2

Guía de referencia del lenguaje de programación Pascal en español
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Machine Translated by Google

Pascal libre
Guia de referencia

Guía de referencia para Free Pascal, versión 3.2.2


Versión del documento 3.2.2
Mayo de 2021

Michael Van Canneyt


Machine Translated by Google

Contenido

1 fichas de Pascal 12

1.1 Símbolos . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 12


1.2 Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 13
1.3 Palabras reservadas . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 14
1.3.1 Palabras reservadas de Turbo Pascal . . . . . . . . . . . . . . . . . . . . . . . . . . 14

1.3.2 Palabras reservadas de Objeto Pascal . . . . . . . . . . . . . . . . . . . . . . . . . 15


1.3.3 Modificadores . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 15
1.4 Identificadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . dieciséis

1.5 Directivas de sugerencias . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . dieciséis

1.6 Números . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 17


1.7 Etiquetas . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 19

1.8 Cadenas de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 19

2 constantes 21

2.1 Constantes ordinarias . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 21

2.2 Constantes escritas . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 22

2.3 Cadenas de recursos . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 23

3 tipos 25

3.1 Tipos de bases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 25

3.1.1 Tipos ordinales . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 26

Enteros . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 26

Tipos booleanos . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 28

Tipos de enumeración . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 29

Tipos de subrango . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 30

Tipos de personajes . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 31

3.1.2 Tipos reales . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 31

3.2 Tipos de personajes . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 32


3.2.1 Char o AnsiChar . . . ..... . . . . . . . . . . . . . . ..... . . . . . 32
3.2.2 Carácter ancho . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 32

3.2.3 Otros tipos de caracteres . . . . . . . . . . . . . . . . . . . . ..... . . . . . 32

1
Machine Translated by Google

CONTENIDO

3.2.4 Tipos de cadenas de un solo byte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

Cuerdas cortas . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 33

Ansistrings . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 34

Conversiones de páginas de códigos . . . . . . . . . . . . . . . . . . . ..... . . . . . 36

Cadena de bytes sin formato . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 38

Cadena UTF8 . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 38

3.2.5 Tipos de cadenas multibyte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

Cadenas Unicode . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 38

Cuerdas anchas . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 39

3.2.6 Cadenas constantes . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 39

3.2.7 PChar – Cadenas terminadas en nulo . . . . . . . . . . . . . . . . . . . . . . . . 40

3.2.8 Tamaños de . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 41

cadena 3.3 Tipos estructurados. . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 41

Tipos estructurados empaquetados . . . . . . . . . . . . . . . . . . . ..... . . . . . 41

3.3.1 Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 43

Arreglos estáticos . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 43

Arreglos dinámicos . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 44

Compatibilidad de tipos de matriz dinámica . . . . . . . . . . . . . ..... . . . . . 47

Constructor de matriz dinámica . . . . . . . . . . . . . . . . . ..... . . . . . 48

Expresiones constantes de matriz dinámica . . . . . . . . . . . . . . . . . . . . . . 49

Empacar y desempaquetar una matriz . . . . . . . . . . . . . . . . . . . . . . . . 50

3.3.2 Tipos de registros . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 50

Diseño y tamaño del ..... . . . . . . . . . . . . . . ..... . . . . . 51

registro Comentarios y ejemplos . . . .. .. . . . . . . . . . . . . . . . . . . . . . . 52

3.3.3 Tipos de conjuntos . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 53

3.3.4 Tipos de archivos . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 53


3.4 Consejos . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 54

3.5 Declaraciones de tipo directo . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 56

3.6 Tipos procesales . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 56

3.7 Tipos de variantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 61


3.7.1 Definición . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 61

3.7.2 Variantes en asignaciones y expresiones . . . . . . . . . . ..... . . . . . 62


3.7.3 Variantes e interfaces ..... . . . . . . . . . . . . . . ..... . . . . . 63

3.8 Alias de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 63

3.9 Tipos gestionados . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . sesenta y cinco

4 variables 66
4.1 Definición . . . . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 66
4.2 Declaración . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 66

4.3 Alcance . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 68

2
Machine Translated by Google

CONTENIDO

4.4 Variables inicializadas . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 68

4.5 Inicializando variables usando el valor predeterminado ... . . . . . . . . . . . . . . ..... . . . . . 70


4.6 Variables del hilo . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 71

4.7 Propiedades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 71

5 objetos 75
5.1 Declaración . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 75

5.2 Objetos abstractos y sellados . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 76


5.3 Campos . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 77
5.4 Clase o campos estáticos . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 78
5.5 Constructores and destructores . . . . . . . . . . . . . . . . . . . . ..... . . . . . 79
5.6 Métodos . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 80
5.6.1 Declaración . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 80
5.6.2 Invocación de método . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 80
Métodos estáticos normales ..... . . . . . . . . . . . . . . ..... . . . . . 81
Métodos virtuales . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 81
Métodos abstractos . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 83
Métodos de clase o estáticos. ..... . . . . . . . . . . . . . . ..... . . . . . 84

5.7 Visibilidad . . . . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 85

6 clases 86
6.1 Definiciones de clases . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 86
6.2 Clases abstractas y selladas . . ..... . . . . . . . . . . . . . . ..... . . . . . 90
6.3 Campos normales y estáticos . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 90
6.3.1 Campos/variables normales . . . . . . . . . . . . . . . . . . . ..... . . . . . 90
6.3.2 Campos/variables de clase . . . ... . . . . . . . . . . . . . . ..... . . . . . 91
6.4 Creación de instancias de clase . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 93
6.5 Destrucción de clase . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 93
6.6 Métodos . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 94
6.6.1 Declaración . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 94
6.6.2 Invocación . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 95
6.6.3 Métodos virtuales . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 95
6.6.4 Métodos de clase . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 96
6.6.5 Class constructores and destructores . . . . . . . . . . . . . . . . . . . . . . . 97
6.6.6 Métodos de clase estática . . . ... . . . . . . . . . . . . . . ..... . . . . . 98

6.6.7 Métodos de mensaje . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 100

6.6.8 Uso heredado . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 101

6.7 Propiedades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 102


6.7.1 Definición . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 102

6.7.2 Propiedades indexadas . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 104

6.7.3 Propiedades de la matriz . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 105

3
Machine Translated by Google

CONTENIDO

6.7.4 Propiedades predeterminadas . . . ..... . . . . . . . . . . . . . . ..... . . . . . 106

6.7.5 Propiedades publicadas . . . . . . . . . . . . . . . . . . . . ..... . . . . . 107

6.7.6 Información de . . . ... . . . . . . . . . . . . . . ..... . . . . . 107


. . . . .. . . . . . . . . . .
almacenamiento 6.7.7 Anulación y redeclaración de propiedades . . . . . 108

6.8 Propiedades de clase . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 110

6.9 Tipos, constantes y variables anidados . . . . . . . . . . . . . . . . ..... . . . . . 111

7 interfaces 114
7.1 Definición . . . . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 114
7.2 Identificación de interfaz: Un GUID . . . . . . . . . . . . . . . . . . ..... . . . . . 115

7.3 Implementaciones de interfaz . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 116


7.4 Herencia de interfaz . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 117

7.5 Delegación de interfaz . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 118


7.6 Interfaces y COM . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 121
7.7 CORBA y otras interfaces . . . ... . . . . . . . . . . . . . . ..... . . . . . 121

7.8 Conteo de referencias . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 122

8 genéricos 123
8.1 Introducción . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 123

8.2 Definición de tipo genérico . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 123

8.3 Especialización de tipo genérico . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 126

8.4 Restricciones de tipos . . . ..... . . . . . . . . . . . . . . ..... . . . . . 128


. . . .. . . . . .
genéricos 8.5 Compatibilidad con Delphi . . . . . . . . . . . . . . ..... . . . . . 129

8.5.1 Elementos de sintaxis . . . ..... . . . . . . . . . . . . . . ..... . . . . . 129

8.5.2 Restricciones de tipo de .... . . . . . . . . . . . . . . ..... . . . . . 130


. . .. . . . . .
registro 8.5.3 Sobrecargas de tipo . . . . . . . . . . . . . . ..... . . . . . 131

8.5.4 Consideraciones sobre el espacio de nombres ... . . . . . . . . . . . . . . ..... . . . . . 132

8.6 Compatibilidad de tipos . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 132

8.7 Uso del valor intrínseco predeterminado . . . ..... . . . . . . . . . . . . . . ..... . . . . . 134

8.8 Unas palabras sobre el alcance . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 135

8.9 Sobrecarga de operadores y genéricos . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

9 registros extendidos 142


9.1 Definición . . . . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 142
9.2 Enumeradores de registros extendidos . . . ... . . . . . . . . . . . . . . ..... . . . . . 144

9.3 Operadores de registro . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 146

10 ayudantes de clase, registro y tipo 149


10.1 Definición . . . . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 149

10.2 Restricciones a los ayudantes de clase . . . ... . . . . . . . . . . . . . . ..... . . . . . 150

10.3 Restricciones sobre los ayudantes de registros . . . . . . . . . . . . . . . . . . . ..... . . . . . 151

4
Machine Translated by Google

CONTENIDO

10.4 Consideraciones para ayudantes de tipo (simples) . . . . . . . . . . . . . . . . . . . . . . . . 152

10.5 Una nota sobre el alcance y la vida útil de los ayudantes de registro y tipo . . . . . ..... . . . . . 153
10.6 Herencia . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 155

10.7 Uso . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 156

11 clases de objetivo­Pascal 159


11.1 Introducción . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 159

11.2 Declaraciones de clase Objective­Pascal . . . . . . . . . . . . . . . . . ..... . . . . . 159


11.3 Declaración formal . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 161

11.4 Asignación y desasignación de instancias . . . . . . . . . . . . . . . . . . . . . . . . . 163


11.5 Definiciones de protocolo . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 164

11.6 Categorías . . . . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 165

11.7 Alcance del nombre e identificadores . . ..... . . . . . . . . . . . . . . ..... . . . . . 166


11.8 Selectores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 167

11.9 El tipo de identificación . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 167

11.10Enumeración en clases Objective­C .. . . . . . . . . . . . . . . ..... . . . . . 167

12 Expresiones 169

12.1 Sintaxis de expresiones .. . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 170


12.2 Llamadas a funciones . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 171
12.3 Constructores de conjuntos . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 173

12.4 Encasillamientos de valores . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 174

12.5 Encasillamientos de variables . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 175

12.6 Encasillamientos no alineados . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 175

12.7 El operador @ . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 176

12.8 Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 177

12.8.1 Operadores aritméticos . . . . . . . . . . . . . . . . . . . . ..... . . . . . 177

12.8.2 Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 178

12.8.3 Operadores booleanos . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 178

12.8.4 Operadores de cadena . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 179

12.8.5 Operadores de matriz dinámica . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179

12.8.6 Establecer operadores . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 180

12.8.7 Operadores relacionales . . . . . . . . . . . . . . . . . . . . ..... . . . . . 182

12.8.8 Operadores de clase . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 182

13 declaraciones 186

13.1 Declaraciones . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 186


. . .. . . . . . .
simples 13.1.1 Asignaciones . . . . . . . . . . . . . . ..... . . . . . 186
13.1.2 Declaraciones de procedimiento . . . ... . . . . . . . . . . . . . . ..... . . . . . 187
13.1.3 Ir a declaraciones . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 188
13.2 Declaraciones estructuradas . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 189

5
Machine Translated by Google

CONTENIDO

13.2.1 Declaraciones compuestas ..... . . . . . . . . . . . . . . ..... . . . . . 189


13.2.2 La declaración del caso . . . . . . . . . . . . . . . . . . . . ..... . . . . . 190
13.2.3 La declaración If...then...else . . . . . . . . . . . . ..... . . . . . 192
13.2.4 La declaración For..to/downto..do . . . . . . . . . ..... . . . . . 193
13.2.5 La declaración For..in..do . . . . . . . . . . . . . . . ..... . . . . . 195

13.2.6 La declaración Repetir... hasta . . . . . . . . . . . . . ..... . . . . . 202


13.2.7 La declaración Mientras...hacer . . . . . . . . . . . . . . . . . . . . . . . . . . 203
13.2.8 La declaración With . . . ... . . . . . . . . . . . . . . ..... . . . . . 204

13.2.9 Declaraciones de excepción . . . . . . . . . . . . . . . . . . . . ..... . . . . . 205


13.3 Declaraciones del ensamblador . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 206

14 Uso de funciones y procedimientos 207


14.1 Declaración de procedimiento . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 207
14.2 Declaración de función . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 208
14.3 Resultados de la función . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 208
14.4 Listas de parámetros . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 209

14.4.1 Parámetros de valor . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 209

14.4.2 Parámetros variables . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 210

14.4.3 Parámetros de salida . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 211

14.4.4 Parámetros constantes . . . . . . . . . . . . . . . . . . . . ..... . . . . . 212

14.4.5 Parámetros de matriz abierta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213

14.4.6 Matriz de constantes . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 214

14.4.7 Parámetros sin tipo . . . ... . . . . . . . . . . . . . . ..... . . . . . 216

14.4.8 Tipos gestionados y recuentos de referencias . . . . . . . . . . . . . . . . . . . . . . 217

14.5 Sobrecarga de funciones . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 221


14.6 Funciones declaradas hacia adelante . . ..... . . . . . . . . . . . . . . ..... . . . . . 222
14.7 Funciones externas . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 223
14.8 Funciones del ensamblador . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 224
14.9 Modificadores . . . . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 224
14.9.1 alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 225
14.9.2 decl. . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 226

14.9.3 cppdecl . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 226

14.9.4 exportar . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 226


14.9.5 flotación dura . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 226
14.9.6 en línea . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 226

14.9.7 interrupción . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 227


14.9.8 iocheck . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 227
14.9.9 locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 227
14.9.10 MS_ABI_Default . . . ..... . . . . . . . . . . . . . . ..... . . . . . 227
14.9.11 MS_ABI_CDecl . . . ..... . . . . . . . . . . . . . . ..... . . . . . 228

6
Machine Translated by Google

CONTENIDO

14.9.12 MWPascal . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 228


14.9.13 sin retorno . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 228
14.9.14 marco sin pila . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 228
14.9.15 sobrecarga . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 228

14.9.16 pascales . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 230

14.9.17 público . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 230

14.9.18 registrarse . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 231


14.9.19 llamada segura . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 231

14.9.20 guardar registros . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 231


14.9.21 flotación suave . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 231
14.9.22 llamada estándar . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 231
14.9.23 SYSV_ABI_Default . . . ... . . . . . . . . . . . . . . ..... . . . . . 231
14.9.24 SYSV_ABI_CDecl . . ..... . . . . . . . . . . . . . . ..... . . . . . 231
14.9.25 Llamada vectorial . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 231

14.9.26 varargs . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 232

14.9.27 invierno . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 232

14.10Modificadores Turbo Pascal no admitidos . . . . . . . . . . . . . . . . . . . . . . . . . . 232

15 Sobrecarga del operador 233


15.1 Introducción . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 233

15.2 Declaraciones de . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 233


. . . .. . . . . .
operadores 15.3 Operadores de asignación . . . . . . . . . . . . . . ..... . . . . . 235

15.4 Operadores aritméticos . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 238

15.5 Operador de comparación . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 240

15.6 En operador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 242

15.7 Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 242

15,8 Operadores Inc/Dic . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 244

15.9 El operador enumerador . . . ..... . . . . . . . . . . . . . . ..... . . . . . 244

16 Programas, unidades, bloques. 246

16.1 Programas . . . . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 246


16.2 Unidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 247

16.3 Espacios de nombres: unidades punteadas . . ..... . . . . . . . . . . . . . . ..... . . . . . 249

16.4 Dependencias de unidades . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 252


16,5 bloques . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 252

16.6 Alcance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 254

16.6.1 Alcance del bloque . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 254

16.6.2 Alcance del registro . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 254

16.6.3 Alcance de la clase . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 254

16.6.4 Alcance de la unidad . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 255


16.7 Bibliotecas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 255

7
Machine Translated by Google

CONTENIDO

17 excepciones 257
17.1 La declaración de aumento . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 257

17.2 La declaración try...except . . . ..... . . . . . . . . . . . . . . ..... . . . . . 259

17.3 La declaración try...finally 17.4 . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 260

Anidamiento de manejo de excepciones .. . . . . . . . . . . . . . . . . . . . ..... . . . . . 261

17.5 Clases de excepción . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 261

18 Usando ensamblador 263


18.1 Declaraciones del ensamblador . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 263

18.2 Procedimientos y funciones del ensamblador . . . . . . . . . . . . . . . . . . . . . . . . . . 263

8
Machine Translated by Google

Lista de tablas

3.1 Tipos de enteros predefinidos . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 26

3.2 Tipos de enteros predefinidos . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 27

3.3 Mapeo de tipo constante entero . . . . . . . . . . . . . . . . . . . ..... . . . . . 27

3.4 Tipos booleanos . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 28

3.5 Tipos reales admitidos . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 31

3.6 Aritmética de punteros PChar 3.7 . . . ..... . . . . . . . . . . . . . . ..... . . . . . 40

Tamaños de memoria de cadenas . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 41

9.1 Nombres de operadores . . . . . . . . ..... . . . . . . . . . . . . . . ..... . . . . . 147

12.1 Precedencia de operadores . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 169

12.2 Operadores aritméticos binarios . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 177

12.3 Operadores aritméticos unarios . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 177

12.4 Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 178

12.5 Operadores booleanos . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 178

12.6 Establecer operadores . . . . . . . . . . . ... . . . . . . . . . . . . . . ..... . . . . . 180

12.7 Operadores relacionales . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 182

12.8 Operadores de clase . . . . . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 182

13.1 Construcciones C permitidas en Free Pascal .. . . . . . . . . . . . . . . ..... . . . . . 187

14.1 Modificadores no admitidos . . . . . . . . . . . . . . . . . . . . . . . ..... . . . . . 232

9
Machine Translated by Google

LISTA DE TABLAS

Acerca de esta guía

Este documento sirve como referencia para el lenguaje Pascal implementado por el compilador Free
Pascal. Describe todas las construcciones de Pascal admitidas por Free Pascal y enumera todos los tipos
de datos admitidos. Sin embargo, no da una explicación detallada del lenguaje Pascal: no es un tutorial.
El objetivo es enumerar qué construcciones de Pascal son compatibles y mostrar en qué se diferencia la
implementación de Free Pascal de las implementaciones de Turbo Pascal o Delphi.
Los compiladores Turbo Pascal y Delphi Pascal introdujeron varias características en el lenguaje Pascal.
El compilador Free Pascal emula estos compiladores en el modo apropiado del compilador: ciertas
características están disponibles solo si el compilador se cambia al modo apropiado. Cuando sea necesario
para una determinada característica, el uso del modificador de línea de comandos ­M o la directiva {$MODE}
se indicará en el texto. Puede encontrar más información sobre los distintos modos en el manual del usuario
y en el manual del programador.
Las versiones anteriores de este documento también contenían la documentación de referencia de la unidad del
sistema y la unidad objpas. Esto se ha trasladado a la guía de referencia de RTL.

Notaciones

A lo largo de este documento nos referiremos a funciones, tipos y variables con fuente de máquina de escribir.
Los archivos se denominan con una fuente sans: nombre de archivo.

Diagramas de sintaxis
Todos los elementos del lenguaje Pascal se explican en diagramas de sintaxis. Los diagramas de sintaxis
son como diagramas de flujo. Leer un diagrama de sintaxis significa ir del lado izquierdo al derecho, siguiendo
las flechas. Cuando se llega al lado derecho de un diagrama de sintaxis y termina con una sola flecha, esto
significa que el diagrama de sintaxis continúa en la siguiente línea. Si la línea termina en dos flechas que
apuntan entre sí, entonces el diagrama finaliza.
Los elementos sintácticos se escriben así.

­­ los elementos sintácticos son así ­

Palabras clave que deben escribirse exactamente como en el diagrama:

­­ las palabras clave son así ­

Cuando algo se puede repetir, hay una flecha alrededor:


­­ 6 esto se puede repetir ­

Cuando existen diferentes posibilidades, se enumeran en filas:


­­ Primera posibilidad ­
Segunda posibilidad

Tenga en cuenta que una de las posibilidades puede estar vacía:

­­ ­
Primera posibilidad
Segunda posibilidad

Esto significa que tanto la primera como la segunda posibilidad son opcionales. Por supuesto, todos estos elementos se
pueden combinar y anidar.

10
Machine Translated by Google

LISTA DE TABLAS

Sobre el idioma Pascal


El lenguaje Pascal fue diseñado originalmente por Niklaus Wirth alrededor de 1970. Ha evolucionado significativamente desde ese día,
con muchas contribuciones de varios constructores de compiladores (en particular: Borland). Los elementos básicos se han mantenido a
lo largo de los años:

• Sintaxis sencilla, bastante detallada, pero fácil de leer. Ideal para la enseñanza.

• Fuertemente tipificado.

• Procesal.

• No distingue entre mayúsculas y minúsculas.

• Permite procedimientos anidados.

• Rutinas de entrada/salida sencillas integradas.

Los compiladores Turbo Pascal y Delphi Pascal introdujeron varias características en el lenguaje Pascal, en particular un manejo de
cadenas más sencillo y orientación a objetos. El compilador Free Pascal inicialmente emuló la mayor parte de Turbo Pascal y más tarde
Delphi. Emula estos compiladores en el modo apropiado del compilador: ciertas características están disponibles solo si el compilador se
cambia al modo apropiado. Cuando sea necesario para una determinada característica, el uso del modificador de línea de comandos ­M o
la directiva {$MODE} se indicará en el texto. Puede encontrar más información sobre los distintos modos en el manual del usuario y en el
manual del programador.

11
Machine Translated by Google

Capítulo 1

Fichas de Pascal

Los tokens son los componentes léxicos básicos del código fuente: son las "palabras" del lenguaje:
los caracteres se combinan en tokens de acuerdo con las reglas del lenguaje de programación. Hay
cinco clases de tokens:

palabras reservadas Son palabras que tienen un significado fijo en el idioma. Ellos no pueden ser
cambiado o redefinido.

Identificadores Estos son nombres de símbolos que el programador define. Se pueden cambiar y
reutilizar. Están sujetos a las reglas de alcance del idioma.

operadores Generalmente son símbolos para operaciones matemáticas o de otro tipo: +, ­, *, etc.

separadores Suele ser un espacio en blanco.

constantes Las constantes numéricas o de caracteres se utilizan para indicar valores reales en el código fuente, como
1 (constante entera) o 2,3 (constante flotante) o “constante de cadena” (una cadena: un fragmento de texto).

En este capítulo describimos todas las palabras reservadas de Pascal, así como las diversas formas de denotar
cadenas, números, identificadores, etc.

1.1 Símbolos
Free Pascal permite todos los caracteres, dígitos y algunos símbolos de caracteres especiales en un archivo fuente de Pascal.

Símbolos reconocidos

­­ carta ARIZONA
­
Arizona

­­ dígitos 0...9
­

­­ dígito hexadecimal 0...9 ­


A...F
a...f

Los siguientes caracteres tienen un significado especial:

12
Machine Translated by Google

CAPÍTULO 1. FICHAS DE PASCAL

' . :
+­*/=<>[ ] , ( ) ^@{ ps

y los siguientes pares de personajes también:

<< >> ** <> >< <= >= := += ­= *= /= (* *) (. .) //

Cuando se utiliza en un especificador de rango, el par de caracteres (. es equivalente al corchete izquierdo [.


Asimismo, el par de caracteres .) equivale al corchete derecho ]. Cuando se usa para comentar
delimitadores, el par de caracteres (* es equivalente a la llave izquierda { y el par de caracteres *) es equivalente
a la llave derecha }. Estos pares de caracteres conservan su significado normal en expresiones de cadena.

1.2 Comentarios

Los comentarios son partes del código fuente que el compilador descarta por completo. Ellos existen
sólo para beneficio del programador, para que pueda explicar ciertos fragmentos de código. Para el compilador,
Es como si los comentarios no estuvieran presentes.

El siguiente fragmento de código demuestra un comentario:

(*Mi hermosa función devuelve un resultado interesante*)


Función Hermosa: Entero;

El uso de (* y *) como delimitadores de comentarios data de los primeros días del lenguaje Pascal. Él
se ha reemplazado principalmente por el uso de { y } como delimitadores de comentarios, como en el siguiente ejemplo:

{ Mi hermosa función devuelve un resultado interesante }


Función Hermosa: Entero;

El comentario también puede abarcar varias líneas:

{
Mi hermosa función devuelve un resultado interesante,
pero sólo si el argumento A es menor que B.
}
Función Hermosa (A,B: Entero): Entero;

También se pueden hacer comentarios de una sola línea con el delimitador //:

// Mi hermosa función devuelve un resultado interesante


Función Hermosa: Entero;

El comentario se extiende desde el carácter // hasta el final de la línea. Este tipo de comentario fue
introducido por Borland en el compilador Delphi Pascal.
Free Pascal admite el uso de comentarios anidados. Las siguientes construcciones son comentarios válidos:

(*Este es un comentario de estilo antiguo*)


{Este es un comentario de Turbo Pascal}
// Este es un comentario de Delphi. Todo se ignora hasta el final de la línea.

13
Machine Translated by Google

CAPÍTULO 1. FICHAS DE PASCAL

Las siguientes son formas válidas de anidar comentarios:

{ Comentario 1 (* comentario 2 *) }
(* Comentario 1 { comentario 2 } *) { comentario
1 // Comentario 2 } (* comentario 1 //
Comentario 2 *) // comentario 1 (* comentario 2
*) // comentario 1 { comentario 2 }

Los dos últimos comentarios deben estar en una línea. Los dos siguientes darán errores:

// Comentario válido { ¡¡Ya no es válido el comentario!!


}

// Comentario válido (* ¡¡Comentario ya no válido!!


*)

El compilador reaccionará con un error de "carácter no válido" cuando encuentre tales construcciones, independientemente
del modificador ­Mtp.

Observación En modo TP y Delphi, no se permiten comentarios anidados, para máxima compatibilidad con el
código existente para esos compiladores.

1.3 palabras reservadas

Las palabras reservadas son parte del lenguaje Pascal y, como tales, el programador no puede redefinirlas.
A lo largo de los diagramas de sintaxis se indicarán utilizando un tipo de letra en negrita . Pascal no distingue entre mayúsculas
y minúsculas, por lo que el compilador aceptará cualquier combinación de letras mayúsculas o minúsculas para palabras
reservadas.

Hacemos una distinción entre palabras reservadas de Turbo Pascal y Delphi. En modo TP, sólo se reconocen
las palabras reservadas de Turbo Pascal, pero las de Delphi se pueden redefinir. Por defecto, Free Pascal
reconoce las palabras reservadas de Delphi.

1.3.1 Palabras reservadas de Turbo Pascal


Las siguientes palabras clave existen en el modo Turbo Pascal

absoluto archivo objeto de cadena


y para entonces
formación función operador a
ENSAMBLE
ir si o tipo
comenzar unidad
caso implementación en procedimiento empaquetado hasta
constante registro del usos
constructor heredado programa era
incinerador de basuras en línea reintroducir mientras
div interfaz repetirse con
hacer etiqueta gratis

Abajo a contra colocar

demás nulo shl


fin no shr

14
Machine Translated by Google

CAPÍTULO 1. FICHAS DE PASCAL

1.3.2 Palabras reservadas de objeto Pascal


Las palabras reservadas de Object Pascal (usadas en modo Delphi u Objfpc) son las mismas que las de Turbo
Pascal, con las siguientes palabras clave adicionales:

como finalización finalmente biblioteca aumentar


interfaz inicialización en threadvar de cadena de
de clase excepto en línea afuera recursos
propiedad intentar

exportaciones es empaquetada

1.3.3 Modificadores
La siguiente es una lista de todos los modificadores. No son exactamente palabras reservadas en el sentido de que
puedan usarse como identificadores, pero en lugares específicos tienen un significado especial para el compilador,
es decir, el compilador las considera parte del lenguaje Pascal.

absoluto externo marco de pila saveregisters softfloat


descanso far oldfpccall de lo se especializa
bitpacked far16 contrario se en estática
del ensamblador forward sobrecarga
de alias abstracto generic anular llamada estándar

helper plataforma almacenado


cdecl implementa el pascal privado estricto
continuar índice protegido no alineado no
cppdecl interrumpir público publicado implementado
cvar iocheck leer varargs
por defecto local virtuales

enumerador mensaje escribir


dinámico nombre registrarse winapi
obsoleto cerca reintroducir
exportación ningún valor predeterminado resultado
experimental sin retorno llamada segura

Observación Los tipos predefinidos como Byte, Boolean y constantes como maxint no son palabras reservadas.
Son identificadores, declarados en la unidad del sistema. Esto significa que estos tipos se pueden redefinir en otras
unidades. Sin embargo, no se anima al programador a hacer esto, ya que causará mucha confusión.

Observación A partir de la versión 2.5.1, es posible utilizar palabras reservadas como identificadores escapándolas con un signo &.
Esto significa que es posible lo siguiente

era
&var: entero;

comenzar
&var:=1;
Escribir(&var);
fin.

sin embargo, no se recomienda utilizar esta función en código nuevo, ya que hace que el código sea menos legible. Su
objetivo principal es corregir el código antiguo cuando la lista de palabras reservadas cambia y abarca una palabra que
aún no estaba reservada (consulte también la sección 1.4, página 16).

15
Machine Translated by Google

CAPÍTULO 1. FICHAS DE PASCAL

1.4 Identificadores

Los identificadores denotan nombres definidos por el programador para constantes, tipos, variables, procedimientos y
funciones, unidades y programas específicos. Todos los nombres definidos por el programador en el código fuente
(excluidas las palabras reservadas) se designan como identificadores.

Los identificadores constan de entre 1 y 127 caracteres significativos (letras, dígitos y el carácter de subrayado), de los
cuales el primero debe ser una letra (a–z o A–Z) o un guión bajo (_). El siguiente diagrama proporciona la sintaxis básica
de los identificadores.

Identificadores

­­ identi cador carta 6 ­


_ dígito de

la letra

Al igual que las palabras reservadas de Pascal, los identificadores no distinguen entre mayúsculas y minúsculas, es decir, ambos

mi procedimiento;

Mi Procedimiento;

consulte el mismo procedimiento.

Observación A partir de la versión 2.5.1 es posible especificar una palabra reservada como identificador anteponiéndola con un
signo comercial (&). Esto significa que es posible lo siguiente:

prueba del programa;

procedimiento &do;

empezar
fin;

comenzar

&hacer; fin.

La palabra reservada do se utiliza como identificador para la declaración así como para la invocación del procedimiento
do.

1.5 Directivas de sugerencias

La mayoría de los identificadores (constantes, variables, funciones o métodos, propiedades) pueden tener una directiva de
sugerencia adjunta a su definición:

directivas de sugerencias

dieciséis
Machine Translated by Google

CAPÍTULO 1. FICHAS DE PASCAL

­ directiva de sugerencia ­
Obsoleto
constante de cadena
Experimental
Plataforma
No implementado

Siempre que el compilador encuentre un identificador marcado con una directiva de sugerencia, se mostrará una
advertencia correspondiente a la sugerencia especificada.

obsoleto El uso de este identificador está obsoleto; utilice una alternativa en su lugar. La palabra clave obsoleta puede
ir seguida de una cadena constante con un mensaje. El compilador mostrará este mensaje cada vez que
encuentre el identificador.

experimental El uso de este identificador es experimental: se puede utilizar para marcar nuevas características que
debe usarse con precaución.

plataforma Este es un identificador que depende de la plataforma: puede que no esté definido en todas las plataformas.

no implementado Esto debe usarse solo en funciones y procedimientos. Debe usarse para señalar
que una característica particular aún no se ha implementado.

Los siguientes son ejemplos:

constante

AConst = 12 en desuso;

era
p: plataforma entera;

Función Algo: Entero; experimental;

comenzar
Algo:=P+AConst; fin;

comenzar
algo; fin.

Esto daría como resultado el siguiente resultado:

[Link](11,15) Advertencia: el símbolo "p" no es portátil [Link](11,22) Advertencia: el


símbolo "AConst" está en desuso [Link](15,3) Advertencia: el símbolo "Algo" es experimental

Las directivas de sugerencias pueden seguir todo tipo de identificadores: unidades, constantes, tipos, variables, funciones,
procedimientos y métodos.

1.6 Números
Los números se indican por defecto en notación decimal. Los números reales (o decimales) se escriben utilizando
notación científica o de ingeniería (por ejemplo, 0,314E1).

Para constantes de tipo entero, Free Pascal admite cuatro formatos:

17
Machine Translated by Google

CAPÍTULO 1. FICHAS DE PASCAL

1. Formato decimal normal (base 10). Este es el formato estándar.

2. Formato hexadecimal (base 16), del mismo modo que lo hace Turbo Pascal. Para especificar un valor constante en
formato hexadecimal, antepóngale un signo de dólar ($). Por lo tanto, el $FF hexadecimal equivale a 255 decimales.
Tenga en cuenta que las mayúsculas y minúsculas son insignificantes cuando se utilizan constantes hexadecimales.

3. A partir de la versión 1.0.7, también se admite el formato octal (base 8). Para especificar una constante
en formato octal, antepóngale un signo comercial (&). Por ejemplo, 15 se especifica en notación octal
como &17.

4. Notación binaria (base 2). Un número binario se puede especificar precediéndolo con un signo de porcentaje.
(%). Por tanto, 255 se puede especificar en notación binaria como %11111111.

Los siguientes diagramas muestran la sintaxis de los números.

Números

­­ secuencia de dígitos hexadecimales 6 dígitos hexadecimales


­

­­ secuencia de dígitos octales 6 dígitos octales ­

­­ secuencia de dígitos bin 6 ­


10

­­ secuencia de dígitos 6 dígitos ­

­­ entero sin signo secuencia de dígitos ­


$ secuencia de dígitos
hexadecimales y secuencia de
dígitos octales % secuencia de dígitos bin

­­signo + _ ­
­

­­ secuencia de dígitos reales sin signo ­


. secuencia de dígitos factor de escala

­­ factor de escala E secuencia de dígitos ­


Es firmar

­­ número sin firmar entero real sin ­


signo

­­ número firmado número sin firmar ­


firmar

Observación La notación octal y binaria no se admite en el modo de compatibilidad TP o Delphi.

18
Machine Translated by Google

CAPÍTULO 1. FICHAS DE PASCAL

1.7 Etiquetas

Una etiqueta es el nombre de una ubicación en el código fuente a la que se puede saltar desde otra ubicación con
una instrucción goto. Una etiqueta es un identificador estándar o una secuencia de dígitos.

Etiqueta

­­ etiqueta identificador de
­
secuencia de dígitos

Observación Los modificadores ­Sg o ­Mtp deben especificarse antes de poder utilizar etiquetas. De forma predeterminada, Free
Pascal no admite declaraciones de etiqueta ni de ir a. La directiva {$GOTO ON} también se puede utilizar para
permitir el uso de etiquetas y la instrucción goto.

Los siguientes son ejemplos de etiquetas válidas:

Etiqueta
123,
abc;

1.8 Cadenas de caracteres

Una cadena de caracteres (o cadena para abreviar) es una secuencia de cero o más caracteres (tamaño byte), entre
comillas simples y en una sola línea del código fuente del programa: no pueden aparecer caracteres de retorno de
carro literal ni salto de línea en la cadena. .

Un conjunto de caracteres sin nada entre comillas ('') es una cadena vacía.

Cadenas de caracteres

­­ cadena de caracteres 6 cadena entre comillas ­


cadena de control

' ­
­­cadena entre comillas ' carácter de 6 cadenas

' ­
­­ carácter de cadena Cualquier personaje excepto o CR
"

­­ cadena de control 6 # entero sin signo ­

La cadena consta de caracteres ASCII estándar de 8 bits o caracteres Unicode (normalmente codificados en
UTF­8). La cadena de control se puede utilizar para especificar caracteres que no se pueden escribir en un
teclado, como el número 27 para el carácter de escape.
El carácter de comilla simple se puede incrustar en la cadena escribiéndolo dos veces. La construcción C de
caracteres de escape en la cadena (usando una barra invertida) no es compatible con Pascal.

Las siguientes son constantes de cadena válidas:

19
Machine Translated by Google

CAPÍTULO 1. FICHAS DE PASCAL

'Esta es una cadena de pascal'


''
'a'
'Un carácter tabulador: '#9' es fácil de incrustar'

La siguiente es una cadena no válida:

'la cadena comienza aquí y continúa


aquí'

La cadena anterior debe escribirse como:

'la cadena comienza aquí'#13#10' y continúa aquí'

'la cadena comienza aquí'#10' y continúa aquí'

en Unices (incluido Mac OS X) y como

'la cadena comienza aquí'#13' y continúa aquí'

en un sistema operativo clásico tipo Mac.


Es posible utilizar otros juegos de caracteres en cadenas: en ese caso la página de códigos del archivo fuente
debe especificarse con la directiva {$CODEPAGE XXX} o con la opción de línea de comando ­Fc para el
compilador. En ese caso, los caracteres de una cadena se interpretarán como caracteres de la página de
códigos especificada.

20
Machine Translated by Google

Capitulo 2

Constantes

Al igual que Turbo Pascal, Free Pascal admite constantes tanto ordinarias como escritas. Se declaran en un
bloque de declaración constante en una declaración de unidad, programa o clase, función o procedimiento
(sección 16.5, página 252).

2.1 Constantes ordinarias

Las declaraciones de constantes ordinarias se construyen utilizando un nombre de identificador seguido de un


token "=" y seguido de una expresión opcional que consta de combinaciones legales de números, caracteres,
valores booleanos o valores enumerados, según corresponda. El siguiente diagrama de sintaxis muestra cómo
construir una declaración legal de una constante ordinaria.

Declaración constante

­­ declaración constante 6 identi er = expresión directivas de sugerencias; ­

El compilador debe poder evaluar la expresión en una declaración constante en el momento de la compilación. Esto
significa que la mayoría de las funciones de la biblioteca Run­Time no se pueden utilizar en una declaración constante.
Sin embargo, se pueden utilizar operadores como +, ­, *, /, not, and, or, div, mod, ord, chr, sizeof, pi,
int, trunc, round, frac, odd. Para obtener más información sobre expresiones, consulte el capítulo
12, página 169.
Cuando se utiliza en el código una constante ordinaria previamente declarada, el compilador insertará el valor
real de la constante en lugar del nombre de la constante. Es decir, los dos fragmentos de código siguientes son
totalmente equivalentes:

constante
Uno = 1;

comenzar
Escribir(Uno);
fin.

Lo anterior producirá el mismo código que si se hubiera escrito:

21
Machine Translated by Google

CAPÍTULO 2. CONSTANTES

comenzar Writeln(1);
fin.

Sólo se pueden declarar constantes de los siguientes tipos:

• Tipos ordinales

• Tipos de conjuntos

• Tipos de puntero (pero el único valor permitido es Nil).

• Tipos reales

• carbón,

• Cadena

Las siguientes son todas declaraciones constantes válidas:

constante

mi = 2,7182818; { Constante de tipo real. }a = 2; c = '4'; s =


'Esta es { Constante de tipo ordinal (entero). }
una cadena { Tipo de carácter constante. }
constante'; {Constante de tipo de cadena.} sc = chr(32) ls = SizeOf(Longint); P = nulo; Ss
= [1,2];

No se permite asignar un valor a una constante ordinaria. Por lo tanto, dada la declaración anterior, lo
siguiente resultará en un error de compilación:

s := 'alguna otra cadena';

Para las constantes de cadena, el tipo de cadena depende de algunos modificadores del compilador. Si se desea un
tipo específico, se debe utilizar una constante escrita, como se explica en la siguiente sección.

Antes de la versión 1.9, Free Pascal no admitía correctamente constantes de 64 bits. A partir de la versión 1.9, se pueden
especificar constantes de 64 bits.

2.2 Constantes escritas

A veces es necesario especificar el tipo de constante, por ejemplo para constantes de estructuras complejas
(definidas más adelante en el manual). Su definición es bastante simple.

Declaración constante escrita

­­declaración constante escrita 6 identi er: tipo = directiva de sugerencia constante escrita; ­
­ ­

22
Machine Translated by Google

CAPÍTULO 2. CONSTANTES

­­ constante escrita dirección ­


constante matriz
constante
registro constante
constante de procedimiento

A diferencia de las constantes ordinarias, se les puede asignar un valor en tiempo de ejecución. Este es un concepto
antiguo de Turbo Pascal, que ha sido reemplazado con soporte para variables inicializadas: para obtener una descripción
detallada, consulte la sección 4.4, página 68.

La compatibilidad con la asignación de valores a constantes escritas está controlada por la directiva {$J}: se puede desactivar,
pero está activada de forma predeterminada (para compatibilidad con Turbo Pascal). Siempre se permiten variables inicializadas.

Observación Cabe destacar que las constantes escritas se inicializan automáticamente al inicio del programa. Esto también es
válido para las constantes escritas localmente y las variables inicializadas. Las constantes escritas localmente también
se inicializan al inicio del programa. Si su valor se cambió durante invocaciones anteriores de la función, conservarán
su valor modificado, es decir, no se inicializarán cada vez que se invoque la función.

2.3 Cadenas de recursos

Un tipo especial de bloque de declaración constante es el bloque Resourcestring. Las declaraciones de cadenas de
recursos son muy parecidas a las declaraciones de cadenas constantes: las cadenas de recursos actúan como cadenas
constantes, pero pueden localizarse mediante un conjunto de rutinas especiales en la unidad objpas. Un bloque de
declaración de cadena de recursos sólo se permite en los modos Delphi u Objfpc.

El siguiente es un ejemplo de una definición de cadena de recursos:

Cadena de recursos

MenúArchivo = '&Archivo...'; EditMenu


= '&Editar...';

Todas las constantes de cadena definidas en la sección de cadena de recursos se almacenan en tablas especiales. Las cadenas
de estas tablas se pueden manipular en tiempo de ejecución con algunos mecanismos especiales en la unidad objpas.

Semánticamente, las cadenas actúan como constantes ordinarias; No está permitido asignarles valores (excepto a
través de mecanismos especiales en la unidad objpas). Sin embargo, se pueden utilizar en asignaciones o expresiones
como constantes de cadena ordinarias. El uso principal de la sección de cadenas de recursos es proporcionar un
medio sencillo de internacionalización.

Puede encontrar más información sobre el tema de las cadenas de recursos en la Guía del programador y en la referencia
de la unidad objpas.

Observación Tenga en cuenta que una cadena de recurso que se proporciona como expresión no cambiará si se cambian las
partes de la expresión:

cadena de recursos
Parte1 = 'Primera parte de una cadena larga.'; Part2 = 'Segunda
parte de una cadena larga.'; Oración = Parte1+' '+Parte2;

Si las rutinas de localización traducen la Parte 1 y la Parte 2, la constante Oración no se traducirá automáticamente:
tiene una entrada separada en las tablas de cadenas de recursos y, por lo tanto, debe traducirse por separado. La
construcción anterior simplemente dice que el valor inicial de Sentencia es igual a Parte1+' '+Parte2.

23
Machine Translated by Google

CAPÍTULO 2. CONSTANTES

Observación Del mismo modo, cuando se utilizan cadenas de recursos en una matriz constante, solo se usarán los valores
iniciales de las cadenas de recursos en la matriz: cuando se traducen las constantes individuales, los elementos
de la matriz conservarán su valor original.

cadena de recursos Sí =
'Sí.'; No = 'No';

Era
SíNo: Matriz[Booleano] de cadena = (No,Sí);
B: booleano;

comenzar
Writeln(SíNo[B]); fin.

Esto imprimirá "Sí". o no." dependiendo del valor de B, incluso si las constantes Sí y No han sido localizadas mediante
algún mecanismo de localización.

24
Machine Translated by Google

Capítulo 3

Tipos

Todas las variables tienen un tipo. Free Pascal admite los mismos tipos básicos que Turbo Pascal, con
algunos tipos adicionales de Delphi y algunos propios.
El programador puede declarar sus propios tipos, lo que en esencia consiste en definir un identificador que puede
usarse para indicar este tipo personalizado al declarar variables más adelante en el código fuente. La declaración
de un tipo ocurre en un bloque Tipo (sección 16.5, página 252), que es una colección de declaraciones de tipo,
separadas por punto y coma:

Declaración de tipo

­­ identificador de declaración de tipo = tipo ; ­


directivas de pistas

Hay ocho tipos principales de tipos:

Tipos

­­ tipo tipo simple ­


tipo de
cadena tipo
estructurado
tipo de puntero
tipo
procedimental tipo
genérico tipo especializado alias de tipo

Cada uno de estos casos se examinará por separado.

3.1 Tipos de bases

Los tipos básicos o simples de Free Pascal son los tipos Delphi. Discutiremos cada tipo por separado.

25
Machine Translated by Google

CAPÍTULO 3. TIPOS

tipos simples

­­ tipo simple tipo ordinal ­


tipo real

­­ tipo real identificador de tipo real ­

3.1.1 Tipos ordinales

Con la excepción de los tipos de valores de coma flotante, todos los tipos base son tipos ordinales. Los tipos ordinales
tienen las siguientes características:

1. Los tipos ordinales son contables y ordenados, es decir, en principio, es posible empezar a contarlos uno por uno,
en un orden específico. Esta propiedad permite definir el funcionamiento de funciones como Inc, Ord, Dec en
tipos ordinales.

2. Los valores ordinales tienen el valor más pequeño posible. Intentar aplicar la función Pred en el valor más pequeño
posible generará un error de verificación de rango si la verificación de rango está habilitada.

3. Los valores ordinales tienen el mayor valor posible. Intentando aplicar la función Succ en el más grande
El valor posible generará un error de verificación de rango si la verificación de rango está habilitada.

Observación Int64 y QWord se consideran tipos ordinales en CPU de 64 bits. En los tipos de 32 bits tienen algunas de las
características de los ordinales, pero no se pueden utilizar, por ejemplo, en bucles for.

Enteros

En la tabla (3.1) se presenta una lista de tipos de enteros predefinidos.

Tabla 3.1: Tipos de enteros predefinidos

Nombre

Entero
punto corto
PequeñoInt

punto largo
palabra larga
Int64
Byte
Palabra
Cardenal
Palabra Q
ByteBool
PalabraBool
bool largo
QPalabraBool

Los tipos de enteros, y sus rangos y tamaños, que están predefinidos en Free Pascal se enumeran en la tabla (3.2).
Tenga en cuenta que los tipos qword e int64 no son ordinales verdaderos, por lo que algunas construcciones de Pascal
no funcionarán con estos dos tipos de enteros.

26
Machine Translated by Google

CAPÍTULO 3. TIPOS

Tabla 3.2: Tipos de enteros predefinidos

Tipo Rango Tamaño en bytes


Byte 0 .. 255 1
punto corto ­128 .. 127 1
Pequeño ­32768 .. 32767 0 .. 2
Palabra 65535 ya 2

Entero sea en letra pequeña o en letra talla 2 o 4


Cardenal larga 4

punto largo ­2147483648 .. 2147483647 0 .. 4

palabra larga 4294967295 4


Int64 ­9223372036854775808 .. 92 23372036854775807 0 .. 8

Palabra Q 18446744073709551615 8

El tipo entero es un alias del tipo Smallint en el modo Free Pascal predeterminado. Es un alias para
el tipo entero largo en modo Delphi u ObjFPC. El tipo cardinal actualmente siempre está mapeado
al tipo de palabra larga.

Observación El compilador decide el tipo de una constante entera basándose en el valor: Una constante entera obtiene
el tipo con signo más pequeño posible. Se utiliza la primera coincidencia de la tabla (3.3).

Tabla 3.3: Mapeo de tipos de constantes enteras

Tipo de rango
­128..127 Punto corto

128..255 bytes
­32768..32767 Pequeño
32768..65535 Palabra

­2147483648..2147483647 entero largo


2147483648..4294967295 Cardenal (palabra larga)
­9223372036854775808 .. 9223372036854775807 Int64

Eso significa que las constantes en el rango ­128..127 se asignan a shortint, las constantes en el rango 128..255 se asignan
asignadas a bytes, etc. Las constantes en el rango 2147483647..high(cardinal) se analizan como cardinales, y
todas las constantes decimales que no se ajustan a ninguno de los rangos anteriores se analizan como constantes enteras de 64 bits.

Observación En las versiones más nuevas de Delphi, el tipo de entero largo depende de la plataforma y de la CPU. Esto no es así en FPC,
donde longint es de 32 bits en todas las plataformas.

Como compilador de Pascal, Free Pascal realiza conversión y actualización automática de tipos en expresiones.
donde se utilizan diferentes tipos de tipos de números enteros:

1. Cada plataforma tiene un tamaño entero “nativo”, dependiendo de si la plataforma es de 8 bits, 16 bits o
32 bits o 64 bits. [Link]. en AVR esto es de 8 bits.

2. Cada número entero menor que el tamaño "nativo" se promueve a una versión con signo del "nativo"
tamaño. Los números enteros iguales al tamaño "nativo" mantienen su signo.

3. El resultado de los operadores aritméticos binarios (+, ­, *, etc.) se determina de la siguiente manera:

(a) Si al menos uno de los operandos es mayor que el tamaño entero nativo, se elige el resultado
ser el tipo más pequeño que abarca los rangos de los tipos de ambos operandos. Este
significa que mezclar un sin firmar con uno más pequeño o de igual tamaño firmado producirá un
tipo firmado que es más grande que ambos.

27
Machine Translated by Google

CAPÍTULO 3. TIPOS

(b) Si ambos operandos tienen el mismo signo, el resultado es del mismo tipo que ellos. El único
la excepción es restar (­): en el caso de sin firmar, la resta sin signo produce un
resultado firmado en FPC (como en Delphi, pero no en TP7).

(c) La combinación de operandos con y sin signo del tamaño int “nativo” produce un operador con signo más grande
resultado. Esto significa que mezclar longint y longword en plataformas de 32 bits producirá
un int64. De manera similar, mezclar byte y shortint en plataformas de 8 bits (AVR) producirá un
pequeño.

tipos booleanos

Free Pascal admite el tipo booleano, con sus dos valores posibles predefinidos, Verdadero y Falso.
Estos son los dos únicos valores que se pueden asignar a un tipo booleano. Por supuesto, cualquier expresión
que se resuelve en un valor booleano, también se puede asignar a un tipo booleano.

Tabla 3.4: Tipos booleanos

Nombre Orden de tamaño (verdadero)

Booleano 11

Booleano16 2 1
Booleano32 4 1
Booleano64 8 1
ByteBool 1 Cualquier valor distinto de cero
WordBool 2 Cualquier valor distinto de cero

bool largo 4 Cualquier valor distinto de cero

QPalabraBool 8 Cualquier valor distinto de cero

Además del tipo booleano simple, los adicionales Boolean16, Boolean32 y Boolean64
existen tipos. De hecho, estos son tipos de números enteros, cuya asignación es compatible con el método booleano simple.
tipo. Como número entero, los valores de Verdadero y Falso son 1 y 0. Esto se puede utilizar para interactuar con
Código C que define un booleano de este tamaño con valores 0 y 1.

Para facilitar aún más la interfaz con C, Free Pascal también soporta ByteBool, WordBool,
Tipos LongBool y QWordBool. Estos son de tipo Byte, Word, Longint o Int64, pero son
nuevamente una asignación compatible con un booleano. La única diferencia con el booleano 16/32/64
tipos es en qué valores se consideran verdaderos o falsos: El valor Falso equivale a 0 (cero) y
cualquier valor distinto de cero se considera verdadero al convertirlo a un valor booleano. Un valor booleano de
True se convierte a Not(0) en caso de que se asigne a una variable de tipo ByteBool, WordBool,
LongBool o QWordBool.

Suponiendo que B sea de tipo booleano, las siguientes son asignaciones válidas:

B := Verdadero;
B := Falso;
B := 1<>2; {Resultados en B:= Verdadero}

Las expresiones booleanas también se utilizan en las condiciones.

Observación En Free Pascal, las expresiones booleanas por defecto siempre se evalúan de tal manera que cuando
Se conoce el resultado, el resto de la expresión ya no se evaluará: esto se llama atajo booleano
evaluación.

En el siguiente ejemplo, nunca se llamará a la función Func, lo que puede tener efectos secundarios extraños.

28
Machine Translated by Google

CAPÍTULO 3. TIPOS

...
B := Falso;
A := B y Func;

Aquí Func es una función que devuelve un tipo booleano.

Este comportamiento es controlable mediante la directiva del compilador {$B }.

Tipos de enumeración

Los tipos de enumeración son compatibles con Free Pascal. Además de la implementación de Turbo Pascal,
Free Pascal también permite una extensión estilo C del tipo de enumeración, donde se asigna un valor a un
elemento particular de la lista de enumeración.

Tipos enumerados

­­ tipo enumerado ( 6 lista de identificadores ) ­


lista de enumeración asignada

­­ lista de identi cadores 6 identificador ­


,

­­ lista de enumeración asignada 6 identi er := expresión identi er ­


= expresión
,

(consulte el capítulo 12, página 169 para saber cómo utilizar expresiones) Cuando se utilizan tipos enumerados
asignados, los elementos asignados deben estar en orden numérico ascendente en la lista, o el compilador se quejará.
Las expresiones utilizadas en los elementos enumerados asignados deben conocerse en el momento de la compilación.
Entonces la siguiente es una declaración de tipo enumerada correcta:

Tipo
Dirección = (Norte, Este, Sur, Oeste);

Un tipo de enumeración estilo C tiene el siguiente aspecto:

Tipo
EnumType = (uno, dos, tres, cuarenta := 40,cuarenta y uno);

o puedes usar

Tipo
EnumType = (uno, dos, tres, cuarenta = 40,cuarenta y uno);

Esta última notación es obligatoria en modo DELPHI.

Como resultado, el número ordinal de cuarenta es 40, y no 3, como sería si ':= 40' no estuviera presente. El valor ordinal de
cuarenta y uno es entonces 41, y no 4, como sería si la asignación no estuviera presente. Después de una asignación en
una definición enumerada, el compilador agrega 1 al valor asignado para asignarlo al siguiente valor enumerado.

Al especificar dicho tipo de enumeración, es importante tener en cuenta que los elementos enumerados deben mantenerse
en orden ascendente. Lo siguiente producirá un error de compilación:

29
Machine Translated by Google

CAPÍTULO 3. TIPOS

Tipo
EnumType = (uno, dos, tres, cuarenta := 40, treinta := 30);

Es necesario mantener cuarenta y treinta en el orden correcto. Al utilizar tipos de enumeración, es importante tener en
cuenta los siguientes puntos:

1. Las funciones Pred y Succ no se pueden utilizar en este tipo de tipos de enumeración. Intentar hacer esto
de todos modos resultará en un error del compilador.

2. Los tipos de enumeración se almacenan de forma predeterminada, independientemente del número real
de valores: el compilador no intenta optimizar el espacio. Este comportamiento se puede cambiar con la
directiva del compilador {$PACKENUM n}, que le indica al compilador el número mínimo de bytes que
se utilizarán para los tipos de enumeración. Por ejemplo

Tipo
{$PAQUETE 4}
LargeEnum = (BigOne, BigTwo, BigThree);
{$PAQUETE 1}
SmallEnum = (uno, dos, tres); Var S: SmallEnum; L:
Enumeración grande; comenzar
WriteLn ('Enumeración
pequeña:
',SizeOf(S)); WriteLn ('Enumeración grande: ',SizeOf(L)); fin.

Cuando se ejecute, imprimirá lo siguiente:

Enumeración pequeña: 1

Enumeración grande: 4

Puede encontrar más información en la Guía del programador, en la sección de directivas del compilador.

Tipos de subrango

Un tipo de subrango es un rango de valores de un tipo ordinal (el tipo de host). Para definir un tipo de subrango, se deben
especificar sus valores límite: el valor más alto y más bajo del tipo.

Tipos de subrango

­­ tipo de subrango constante .. constante ­

Algunos de los tipos de enteros predefinidos se definen como tipos de subrango:

Escriba
Entero largo = $80000000..$7fffffff; Entero =
­32768..32767; punto corto = ­128..127; byte
= 0..255; Palabra = 0..65535;

30
Machine Translated by Google

CAPÍTULO 3. TIPOS

También se pueden definir tipos de subrango de tipos de enumeración:

Tipo
Días = (lunes, martes, miércoles, jueves, viernes,
sabado domingo);
Días Laborales = lunes .. viernes;
Fin de Semana = Sábado .. Domingo;

Tipos de personajes

Un tipo de carácter también es un tipo ordinal: los caracteres están ordenados y se pueden contar. Hay 2 personajes
tipos:

AnsiChar Este es un carácter de 1 byte. La interpretación del carácter depende de la página de códigos.

WideChar Este es un carácter de 2 bytes. La interpretación del carácter depende de la página de códigos.

Los caracteres se pueden usar en un bucle, se pueden usar prev y succ, así como ord.

Para obtener más información sobre los personajes, consulte la sección 3.2, página 32.

3.1.2 Tipos reales


Free Pascal utiliza el coprocesador matemático (o emulación) para todos sus cálculos de punto flotante. El Real
El tipo nativo depende del procesador, pero puede ser simple o doble. Sólo el IEEE flotando
Se admiten tipos de puntos, que dependen del procesador de destino y de las opciones de emulación. La verdad
Los tipos compatibles con Turbo Pascal se enumeran en la tabla (3.5).

Tabla 3.5: Tipos reales admitidos

Tipo Rango Depende de la plataforma real Sencillo Dígitos significantes ??? Tamaño

1.5E­45 .. 3.4E38 Doble 5.0E­324 .. 1.7E308 Extendido 7–8 4u8

1.9E­4932 .. 1.1E4932 Comp ­2E64+1 .. 2E63­1 Moneda 15– 4


­922337203685477.5808 .. 922337203 685477.5807 16 19– 8
20 19– 10
20 19– 8
20 8

El tipo Comp es, de hecho, un entero de 64 bits y no está disponible en todas las plataformas de destino. Para conseguir más
Para obtener información sobre los tipos admitidos para cada plataforma, consulte la Guía del programador.

El tipo de moneda es un tipo de datos real de punto fijo que se utiliza internamente como un tipo entero de 64 bits.
(escalado automáticamente con un factor 10000), esto minimiza los errores de redondeo. Este tipo debe usarse
con cuidado: cuando se usa en expresiones que usan, por ejemplo, multiplicación, la evaluación de la expresión puede
sale mal (pierde precisión) si los resultados intermedios quedan fuera del rango de divisas.

Tenga en cuenta que no todos los tipos de flotadores están disponibles en todas las plataformas. El tipo de flotador único es el único
Se garantiza que estará disponible en todas las plataformas que tengan soporte de punto flotante (por ejemplo, AVR no
tenerlo). El tipo doble está disponible en todas las plataformas con coprocesador, y el tipo extendido
está disponible en todos los procesadores Intel x86, excepto en la plataforma Windows de 64 bits. Más detalles sobre
La disponibilidad se puede encontrar en la Guía del programador.

31
Machine Translated by Google

CAPÍTULO 3. TIPOS

3.2 Tipos de personajes

3.2.1 Carbón o AnsiChar

Free Pascal admite el tipo Char. Un Char tiene exactamente un tamaño de 1 byte y contiene un carácter ASCII.

Una constante de caracteres se puede especificar encerrándola entre comillas simples, de la siguiente manera: 'a' o
'A' son ambas constantes de caracteres.

Un carácter también se puede especificar por su valor de carácter (comúnmente un código ASCII), precediendo el
valor ordinal con el símbolo numérico (#). Por ejemplo, especificar #65 sería lo mismo que 'A'.

Además, el carácter de intercalación (^) se puede usar en combinación con una letra para especificar un carácter con un
valor ASCII menor que 27. Por lo tanto, ^G es igual a #7 − G es la séptima letra del alfabeto. El compilador es bastante
descuidado con respecto a los caracteres que permite después del signo de intercalación, pero en general se deben asumir
sólo letras.

Cuando se debe representar el carácter de comilla simple, se debe escribir dos veces seguidas, de esta manera
''''
representa el carácter de comilla simple.

Para distinguir Char de WideChar, la unidad del sistema también define el tipo AnsiChar, que es el mismo que el tipo
char. En versiones futuras de FPC, el tipo Char puede convertirse en un alias para WideChar o AnsiChar.

3.2.2 Carácter ancho

Free Pascal admite el tipo WideChar. Un WideChar tiene exactamente 2 bytes de tamaño y contiene un carácter
UNICODE en codificación UTF­16.

Un carácter Unicode se puede especificar por su valor de carácter (un código UTF­16), precediendo el valor ordinal
con el símbolo numérico (#).

También se puede utilizar un literal de carácter ansi (1 byte) normal para un carácter ancho; el compilador lo
convertirá automáticamente a un carácter UTF­16 de 2 bytes.

A continuación se definen algunos caracteres griegos (phi, omega):

constante
C3: carácter ancho = #$03A8;
C4: carácter ancho = #$03A9;

Lo mismo se puede lograr encasillando una palabra en carácter ancho:

constante
C3: carácter ancho = carácter ancho ($03A8);
C4: carácter ancho = carácter ancho ($03A9);

3.2.3 Otros tipos de caracteres Free Pascal

define algunos otros tipos de caracteres en la unidad del sistema, como UCS2Char, UCS4Char, UniCodeChar. Sin
embargo, no existe ningún soporte especial para estos tipos de caracteres; se han definido únicamente para
compatibilidad con Delphi.

3.2.4 Tipos de cadenas de un solo byte Free Pascal

admite el tipo Cadena tal como se define en Turbo Pascal: una secuencia de caracteres de un solo byte con una
especificación de tamaño opcional. También admite ansistrings (con longitud ilimitada) y

32
Machine Translated by Google

CAPÍTULO 3. TIPOS

información de página de códigos1 como en Delphi.

Para declarar una variable como una cadena, utilice la siguiente especificación de tipo:

Tipo de cadena

­­ tipo de cadena cadena ­


[ entero sin signo ]
tipo cadena ( entero sin signo ) ansistring

Si hay un especificador de tamaño (usando corchetes), entonces su valor máximo, que indica el tamaño máximo de la
cadena, es 255. Si hay un especificador de página de códigos (usando corchetes), indica una ansistring con información
de página de códigos asociada.

El significado de una declaración de cadena sin indicación de página de código y tamaño se interpreta de manera
diferente según el modificador {$H}:

era
Una cuerda;

Si no hay ninguna indicación de tamaño y página de códigos, la declaración anterior puede declarar una cadena
hermana o una cadena corta.

Cualquiera que sea el tipo real, las cadenas de un solo byte se pueden utilizar indistintamente. El compilador siempre
se encarga de las conversiones de tipos necesarias. Sin embargo, tenga en cuenta que el resultado de una expresión
que contiene ansistrings y cadenas cortas siempre será una ansistring.

cuerdas cortas

Una declaración de cadena declara una cadena corta en los siguientes casos:

1. Si el interruptor $H está desactivado: {$H­}, la declaración de cadena siempre será una declaración de cadena corta.

2. Si el interruptor está en {$H+} y hay un especificador de longitud máxima (el tamaño), la declaración es una
declaración de cadena corta.

Siempre se supone que las cadenas cortas utilizan la página de códigos del sistema. El tipo predefinido ShortString se
define como una cadena de tamaño 255:

Cadena corta = Cadena[255];

Si no se especifica el tamaño de la cadena, se toma 255 como valor predeterminado. La longitud real de la cadena se
puede obtener con la rutina de ejecución estándar Longitud. Por ejemplo en

{$H­}

Tipo
NombreCadena = Cadena[10];
StreetString = Cadena;

1A partir de la versión 3.0 de Free Pascal

33
Machine Translated by Google

CAPÍTULO 3. TIPOS

NameString puede contener un máximo de 10 caracteres. Mientras que StreetString puede contener hasta 255 caracteres.

Observación Las cadenas cortas tienen una longitud máxima de 255 caracteres: al especificar una longitud máxima, la longitud máxima no
puede exceder los 255. Si se intenta una longitud mayor que 255, el compilador mostrará un mensaje de error:

Error: la longitud de la cadena debe ser un valor de 1 a 255

Para cadenas cortas, la longitud se almacena en el carácter en el índice 0. El código antiguo de Turbo Pascal se basa en esto y
se implementa de manera similar en Free Pascal.

A pesar de esto, para escribir código portátil, es mejor establecer la longitud de una cadena corta con la llamada SetLength y
recuperarla con la llamada Longitud. Estas funciones siempre funcionarán, cualquiera que sea la representación interna de las
cadenas cortas u otras cadenas en uso: esto permite cambiar fácilmente entre los distintos tipos de cadenas.

Ansistrings

Las ansistrings son cadenas que no tienen límite de longitud y tienen una página de códigos asociada a ellas2. Se cuentan por
referencia y se garantiza que tendrán terminación nula.

Internamente, una ansistring se trata como un puntero: el contenido real de la cadena se almacena en el montón y se asigna
tanta memoria como sea necesaria para almacenar el contenido de la cadena.

Si no se proporciona ninguna página de códigos en la declaración, se asume la página de códigos del sistema. La página de
códigos que se trata está determinada por la constante DefaultSystemCodePage en la unidad del sistema.

Todo esto se maneja de forma transparente, es decir, se pueden manipular como una cadena corta normal. Las ansistrings se
pueden definir usando el tipo AnsiString predefinido o usando la palabra clave string en el modo {$H+}.

Observación La terminación nula no significa que no se puedan usar caracteres nulos (char(0) o #0): la terminación nula no se usa
internamente, pero está ahí por conveniencia cuando se trata de rutinas externas que esperan una cadena terminada en nulo
( como lo hacen la mayoría de las rutinas de C).

Si el interruptor {$H} está activado, entonces una definición de cadena que utilice la palabra clave String normal que no contenga
un especificador de longitud también se considerará una ansistring. Si hay un especificador de longitud presente, se utilizará una
cadena corta, independientemente de la configuración {$H}.

Si la cadena está vacía (''), entonces la representación del puntero interno del puntero de cadena es Nil. Si la cadena no está
vacía, entonces el puntero apunta a una estructura en la memoria del montón.

La representación interna como un puntero y la terminación nula automática hacen posible escribir una ansistring a un pchar.
Si la cadena está vacía (por lo que el puntero es Nil), entonces el compilador se asegura de que el pchar encasillado apunte a
un byte nulo.

Asignar una ansistring a otra no implica mover la cadena real. Una declaración

S2:=S1;

da como resultado que el recuento de referencia de S2 se reduzca en 1, el recuento de referencia de S1 se incrementa en 1 y,


finalmente, S1 (como puntero) se copia a S2. Esta es una aceleración significativa en el código.

Si el recuento de referencias de una cadena llega a cero, la memoria ocupada por la cadena se desasigna automáticamente y el
puntero se establece en Nil, por lo que no surgen pérdidas de memoria.

Cuando se declara una ansistring, el compilador de Free Pascal inicialmente asigna solo memoria para un puntero, no más. Se
garantiza que este puntero será Nil, lo que significa que la cadena está inicialmente vacía. Esto es cierto para ansistrings locales
y globales o ansistrings que forman parte de una estructura (matrices, registros u objetos).

2codepage se introdujo en la versión 3.0 de Free Pascal

34
Machine Translated by Google

CAPÍTULO 3. TIPOS

Observación Tenga en cuenta que el resultado de una función a este respecto se considera equivalente a un parámetro var y,
por lo tanto, no se inicializará en Nil. Como consecuencia, puede apuntar a una ansistring legítima distinta de Nil
cuando comienza la función.

Esto introduce una sobrecarga. Por ejemplo, declarar

Era
A: Matriz[1..100000] de cadena;

Copiará el valor Nil 100.000 veces en A. Cuando A sale del alcance, el recuento de referencia de las 100.000
cadenas se reducirá en 1 para cada una de estas cadenas. Todo esto sucede de manera invisible para el
programador, pero cuando se consideran cuestiones de rendimiento, esto es importante.
La memoria para el contenido de la cadena se asignará solo cuando a la cadena se le asigne un valor. Si la cadena sale
del alcance, su recuento de referencias se reduce automáticamente en 1. Si el recuento de referencias llega a cero, se
libera la memoria reservada para la cadena.

Si se asigna un valor a un carácter de una cadena que tiene un recuento de referencia mayor que 1, como en las
siguientes declaraciones:

S:=T; {el recuento de referencias para S y T ahora es 2}


S[I]:='@';

luego se crea una copia de la cadena antes de la asignación. Esto se conoce como semántica de copia en escritura.
Es posible forzar que una cadena tenga un recuento de referencias igual a 1 con la llamada UniqueString:

S:=T;
R:=T; // El recuento de referencias de T es al menos 3
Cadena única(T);
// El recuento de referencia de T está garantizado 1

Se recomienda hacer esto, por ejemplo, al encasillar una ansistring en una var PChar y pasarla a una rutina C que
modifica la cadena.

La función Longitud debe usarse para obtener la longitud de una ansistring: la longitud no se almacena en el carácter 0
de la ansistring. El constructo

L:=ord(S[0]);

que era válido para cadenas cortas de Turbo Pascal, ya no es correcto para Ansistrings. El compilador avisará si se
encuentra dicha construcción.

Para establecer la longitud de una ansistring, se debe utilizar la función SetLength. Las ansistrings constantes tienen
un recuento de referencia de ­1 y se tratan de manera especial. Se debe dar la misma observación que para Longitud:
La construcción

L:=12;
S[0]:=Carbón(L);

que era válido para cadenas cortas de Turbo Pascal, ya no es correcto para Ansistrings. El compilador avisará si se
encuentra dicha construcción.

El compilador convierte las ansistrings en cadenas cortas si es necesario, esto significa que el uso de sistrings y cadenas
cortas se puede mezclar sin problemas.

Las ansistrings se pueden encasillar en tipos PChar o Pointer:

Var P: Puntero;
PC: PCar;

35
Machine Translated by Google

CAPÍTULO 3. TIPOS

S: Cadena Ansi;

comenzar
S :='Esto es una ansistring';
PC:=Pchar(S);
P :=Puntero(S);

Hay una diferencia entre los dos encasillamientos. Cuando una ansistring vacía se encasilla en un puntero, el puntero será Nil. Si
una ansistring vacía se encasilla en un PChar, entonces el resultado será un puntero a un byte cero (una cadena vacía).

El resultado de tal encasillamiento debe utilizarse con cuidado. En general, es mejor considerar el resultado de este tipo de
encasillamiento como de sólo lectura, es decir, sólo apto para pasar a un procedimiento que necesita un argumento pchar
constante.

Por lo tanto, no es aconsejable encasillar uno de los siguientes:

1. Expresiones.

2. Cadenas que tienen un recuento de referencias mayor que 1. En este caso, debe llamar a Uniquestring para asegurarse de
que la cadena tenga un recuento de referencias de 1.

Conversiones de páginas de códigos

Dado que las cadenas tienen información de página de códigos asociada, es importante saber qué página de códigos utiliza una
cadena:

• Las cadenas cortas siempre utilizan la página de códigos del sistema.

• Los ansistrings simples utilizan la página de códigos del sistema.

• Las cadenas de un solo byte con una página de códigos declarada utilizan esa página de códigos.

• El tipo RawBytestring no tiene información de página de códigos asociada.

• Las cadenas constantes tienen la página de códigos del archivo fuente. Si no se especifica ninguno, la página de códigos del sistema
se utiliza (CP_ACP).
Consulte la Guía del programador, directiva {$CODEPAGE }.

Esta página de códigos se denomina página de códigos declarada.

El compilador convertirá la página de códigos de cadenas según sea necesario: al asignar una cadena, la página de códigos real
de la cadena de origen se convertirá a la página de códigos declarada de la cadena de destino si las páginas de códigos de origen
y de destino declaradas son diferentes.

Si una cadena con una página declarada SOURCE_CP asignada a una cadena con la página de códigos declarada DEST_CP,
en un archivo con la página de códigos CODE_CP, a continuación se describe el mecanismo:

• si (SOURCE_CP=CP_NONE) o (DEST_CP = CP_NONE), consulte RawByteString.

• si (CODE_CP <> CP_ACP), entonces si (DEST_CP = CP_ACP) y (SOURCE_CP = CODE_CP) o viceversa, no se producirá
ninguna conversión, incluso si en tiempo de ejecución DefaultSystemCodePage tiene un valor diferente de SOURCE_CP.

El motivo de esta condición “(CODE_CP <>CP_ACP)” es la compatibilidad con versiones anteriores de FPC: si bien no
admitían AnsiStrings con páginas de códigos arbitrarios, siempre reinterpretaban AnsiStrings de acuerdo con el valor
actual de la página de códigos del sistema.
De lo contrario,

36
Machine Translated by Google

CAPÍTULO 3. TIPOS

• Si (SOURCE_CP <> DEST_CP), los datos de cadena se convertirán de la página de códigos SOURCE_CP a la
página de códigos DEST_CP antes de la asignación, por lo que CP_ACP se interpretará como el valor actual de
DefaultSystemCodePage. De lo contrario,

• si (SOURCE_CP = DEST_CP), no se realizará ninguna conversión de página de códigos.

Estas reglas significan que es perfectamente posible que una variable AnsiString obtenga una página de códigos que
difiera de su página de códigos declarada. [Link]. en el tercer caso SOURCE_CP podría ser CP_ACP, mientras que
después de la asignación puede tener una página de códigos dinámicos igual a DefaultSystemCodePage.

Nota: como se mencionó anteriormente, si se produce o no una posible conversión de página de códigos solo depende
de las páginas de códigos declaradas de las cadenas involucradas. Esto significa que si asigna un AnsiString(X) a otro
AnsiString(X) y el código dinámico del primero era diferente de X, la asignación no convertirá los datos de la cadena a la
página de códigos X.

Todo esto significa que en el siguiente código:

{$h+}
usa sysutils;

Tipo
TString1 = Escriba cadena (1252);
TString2 = Escriba cadena (1251);

Era
A: TString1;
B: TScadena2;

comenzar
A:='123'+'345'+intToStr(123); B:=A; Writeln('B:
"',B,'" :
',StringRefCount(B),' ­> ',StringCodePage(B)); Writeln('A: "',A,'" : ',StringRefCount(A),' ­> ',StringCodePage(A)); fin.

Esto imprimirá:

B: "123345123": 1 ­> 1251


Respuesta: "123345123": 1 ­> 1252

Durante la asignación de A a B, el contenido de la cadena A se convierte a la página de códigos de la cadena B.


Tenga en cuenta que si se realiza una conversión de página de códigos, no se utiliza el mecanismo de recuento de referencias:
se asignará una nueva cadena.

Esta conversión automatizada de páginas de códigos puede ralentizar seriamente el código, por lo que se debe tener cuidado
para asegurarse de que las conversiones de páginas de códigos se limiten al mínimo.

La página de códigos de una cadena se puede configurar explícitamente utilizando la rutina SetCodePage de la unidad del sistema.
Llamar a esta rutina convertirá el valor de una cadena a la página de códigos solicitada.

Observación Las conversiones de páginas de códigos pueden provocar la pérdida de datos: si un determinado carácter no se puede representar
en la página de códigos de destino, la salida para ese carácter no está definida.

Observación Cuando una cadena cuya página de códigos estáticos es igual a la página de códigos del archivo fuente, a cualquier cosa con la
página de códigos CP_ACP (es decir, una simple ansistring, shortstring o pchar), tampoco se realizará ninguna conversión. No se
realiza ninguna conversión de página de códigos cuando s puede provocar una pérdida de datos: si un determinado carácter no
se puede representar en la página de códigos de destino, la salida para ese carácter no está definida.

Observación La compatibilidad con la página de códigos requiere bastantes rutinas de ayuda, que se implementan en el
administrador de cadenas unicodestring. En Windows se utilizan para ello las rutinas del sistema. En Unices, la unidad cwstring

37
Machine Translated by Google

CAPÍTULO 3. TIPOS

se puede utilizar para vincular a la biblioteca C y utilizar el soporte de conversión de la biblioteca C. Alternativamente, la unidad
fpwidestring contiene un administrador Unicodestring implementado de forma nativa en Object Pascal.

Cadena de bytes sin procesar

El tipo RawByteString predefinido es un tipo de cadena ansistring sin información de página de códigos
(CP_NINGUNO):

Tipo
RawByteString = escriba ansistring(CP_NONE);

Se trata de manera especial en el sentido de que si las rutinas de conversión encuentran CP_NONE en una cadena de origen o de
destino, no se realiza ninguna conversión de página de códigos y se conserva la página de códigos de la cadena de origen.

Por este motivo, la mayoría de las rutinas de cadenas de un solo byte en las unidades system y sysutils utilizan el tipo RawByteString.

Cadena UTF8

Las cadenas de página de códigos de un solo byte solo pueden almacenar los caracteres disponibles en esa página de códigos. Los
caracteres que no están presentes en la página de códigos no pueden representarse mediante esa cadena. La codificación Unicode
UTF­8 es una codificación que se puede utilizar con cadenas de un solo byte: los caracteres ASCII (valor ordinal <128) en esta
codificación se asignan exactamente a la codificación CP_ACP. Este hecho se utiliza para definir un tipo de cadena de un solo byte
que puede contener todos los caracteres:

Tipo
UTF8String = escriba AnsiString(CP_UTF8);

El tipo de cadena UTF8string se puede utilizar para representar todos los caracteres Unicode. Sin embargo, este poder tiene un
precio. Dado que un carácter Unicode puede requerir varios bytes para representarse en la codificación UTF­8, hay dos puntos a
tener en cuenta al utilizar UTF8String:

1. El índice de caracteres, que recupera un carácter del tamaño de un byte en una posición determinada, debe usarse con
cuidado: la expresión S[i] no será necesariamente un carácter válido para una cadena S de tipo UTF8String.

2. La longitud en bytes de la cadena no es igual al número de caracteres de la cadena. La longitud de la función estándar no se
puede utilizar para obtener la longitud del carácter, siempre devolverá la longitud del byte.

Para todas las demás páginas de códigos, el número de caracteres en una cadena de página de códigos de un solo byte es igual a
la longitud en bytes de la cadena.

3.2.5 Tipos de cadenas multibyte


Para tipos de cadenas de varios bytes, el carácter básico tiene un tamaño de al menos 2. Esto significa que se puede utilizar para
almacenar un carácter Unicode en codificación UTF16 o UCS2.

Cadenas Unicode

Las cadenas Unicode (utilizadas para representar cadenas de caracteres Unicode) se implementan de manera muy similar a las
ansistrings: matrices terminadas en nulo y contadas por referencia, solo que se implementan como matrices de WideChars en lugar
de Chars normales. Un WideChar es un carácter de dos bytes (un elemento de un

38
Machine Translated by Google

CAPÍTULO 3. TIPOS

DBCS: Juego de caracteres de doble byte). Básicamente, se aplican las mismas reglas para UnicodeStrings que para
AnsiStrings. El compilador convierte de forma transparente UnicodeStrings a AnsiStrings y viceversa.

De manera similar a la conversión de un Ansistring a una matriz de caracteres terminada en nulo PChar, una cadena
de código Uni se puede convertir en una matriz de caracteres terminada en nulo PUnicodeChar. Tenga en cuenta que
la matriz PUnicodeChar termina con 2 bytes nulos en lugar de 1, por lo que una conversión encasillada a pchar no es
automática.

El compilador en sí no proporciona soporte para ninguna conversión de Unicode a ansistrings o viceversa.


La unidad del sistema tiene un registro de administrador Unicodestring, que se puede inicializar con algunas rutinas de manejo Unicode
específicas del sistema operativo. Para obtener más información, consulte la referencia de la unidad del sistema.

Un literal de cadena Unicode se puede construir de manera similar a un carácter ancho:

constante

ws2: unicodestring = 'phi omega : '#$03A8' '#$03A9;

cuerdas anchas

El tipo Widestring (utilizado para representar cadenas de caracteres Unicode en aplicaciones COM) se implementa de
forma muy parecida a Unicodestring en Windows, y en otras plataformas son simplemente del mismo tipo. Si no se
requiere interacción con COM, se debe utilizar el tipo UnicodeString.

En Windows, a diferencia de UnicodeString, el tipo WideString no cuenta con referencias y se les asigna una función
especial de Windows que permite su uso para la automatización OLE. Esto significa que se implementan como matrices
terminadas en nulo de WideChars en lugar de Chars normales.
WideString obedece las mismas reglas que UnicodeStrings. De manera similar a las cadenas Unicode, el compilador
convierte de forma transparente WideStrings en AnsiStrings y viceversa.

Para el encasillamiento y la conversión, se aplican las mismas reglas que para el tipo UnicodeString.

Tenga en cuenta que en Windows, debido a que una cadena WideString se asigna mediante una función especial de
Windows, el diseño de la memoria difiere de UnicodeString. La longitud, por ejemplo, se almacena en bytes en lugar de
caracteres.

3.2.6 Cadenas constantes


Para especificar una cadena constante, debe estar entre comillas simples, al igual que el tipo Char, solo que ahora se
permite más de un carácter. Dado que S es de tipo String, las siguientes son asignaciones válidas:

S := 'Esto es una cadena.'; S := 'Uno'+',


Dos'+', Tres'; S := '¡Esto no es difícil!'; S := 'Este es
un personaje extraño: '#145'!';

Como puede verse, el carácter de comilla simple está representado por 2 caracteres de comillas simples uno al lado
del otro. Los caracteres extraños se pueden especificar por su valor de carácter (normalmente un código ASCII). El
ejemplo muestra también que se pueden agregar dos cadenas. La cadena resultante es simplemente la concatenación
de la primera con la segunda cadena, sin espacios entre ellas. Sin embargo, las cadenas no se pueden restar.

Si la cadena constante se almacena como una ansistring o una cadena corta depende de la configuración del
modificador {$H}.

39
Machine Translated by Google

CAPÍTULO 3. TIPOS

3.2.7 PChar – Cadenas terminadas en nulo

Free Pascal admite la implementación Delphi del tipo PChar. PChar se define como un puntero a un tipo
Char, pero permite operaciones adicionales. El tipo PChar puede entenderse mejor como el equivalente en
Pascal de una cadena terminada en nulo estilo C, es decir, una variable de tipo PChar es un puntero que
apunta a una matriz de tipo Char, que finaliza con un carácter nulo (#0 ). Free Pascal admite la inicialización
de constantes escritas en PChar o una asignación directa. Por ejemplo, los siguientes fragmentos de código
son equivalentes:

programa uno;
var P: PChar; comenzar
P :=
'Esta es una cadena terminada en nulo.'; EscribirLn (P); fin.

Resultados iguales que

programa dos; const


P : PChar = 'Esta es una cadena terminada en nulo.'; comenzar WriteLn (P); fin.

Estos ejemplos también muestran que es posible escribir el contenido de la cadena en un archivo de tipo
Texto. La unidad de cadenas contiene procedimientos y funciones que manipulan el tipo PChar como en
la biblioteca C estándar. Dado que equivale a un puntero a una variable de tipo Char, también es posible
hacer lo siguiente:

Programa tres; Var S:


Cadena [30]; P: PCar; comenzar
S := 'Esta es una
cadena
terminada en nulo.'#0; P := @S[1]; EscribirLn (P); fin.

Esto tendrá el mismo resultado que los dos ejemplos anteriores. Las cadenas terminadas en nulo no se pueden
agregar como cadenas Pascal normales. Si se deben concatenar dos cadenas PChar; Se deben utilizar las
funciones de las cadenas unitarias.

Sin embargo, es posible hacer algo de aritmética de punteros. Los operadores + y ­ se pueden utilizar para
realizar operaciones en punteros PChar. En la tabla (3.6), P y Q son de tipo PChar e I es de tipo Entero largo.

Tabla 3.6: Aritmética del puntero PChar

Operación
P+I Resultado Agrega I a la dirección señalada por P.
yo + p Agrega I a la dirección señalada por P.
PI Resta I de la dirección señalada por P.
P­Q Devuelve, como un número entero, la distancia entre dos
direcciones (o el número de caracteres entre P y Q)

40
Machine Translated by Google

CAPÍTULO 3. TIPOS

3.2.8 Tamaños de cadena

La memoria ocupada por una cadena depende del tipo de cadena. Algunos tipos de cadenas asignan la cadena
datos en la memoria en el montón, otros tienen los datos de cadena en la pila. La tabla (3.7) resume
el uso de memoria de los distintos tipos de cadenas para los distintos tipos de cadenas. En la tabla, lo siguiente
Se utilizan constantes simbólicas:

1. L es la longitud real de la cuerda.

2. HS depende de la versión de Free Pascal, pero tiene 16 bytes a partir de Free Pascal 2.7.1.

3. El tamaño UHS es de 8 bytes para todas las versiones de Free Pascal.

4. En Windows, el tamaño de WHS es de 4 bytes para todas las versiones de Free Pascal. En todas las demás plataformas, WHS
es igual a UHS porque el tipo WideString es igual al tipo UnicodeString.

Tabla 3.7: Tamaños de memoria de cadenas

Tipo de cadena Tamaño de pila Tamaño de montón

cuerda corta Longitud declarada + 1 0


ansistring Tamaño del puntero L+1+SA

cuerda ancha Tamaño del puntero 2*(L + 1) + WHS


Tamaño del puntero UnicodeString 2*(L + 1) + UHS
Pchar Tamaño del puntero L+1

3.3 Tipos estructurados

Un tipo estructurado es un tipo que puede contener múltiples valores en una variable. Los tipos estructurados pueden ser
anidado en niveles ilimitados.

Tipos estructurados

­­ tipo estructurado tipo de matriz ­

tipo de registro
tipo de objeto
tipo de clase
tipo de referencia de clase
Tipo de interfaz
tipo de conjunto

tipo de archivo

A diferencia de Delphi, Free Pascal no admite la palabra clave Packed para todos los tipos estructurados. En el
En las siguientes secciones se analiza cada uno de los posibles tipos estructurados. Se mencionará cuando un
type admite la palabra clave empaquetada.

Tipos estructurados empaquetados

Cuando se declara un tipo estructurado, no se deben hacer suposiciones sobre la posición interna del
elementos del tipo. El compilador dispondrá los elementos de la estructura en la memoria como piensa.

41
Machine Translated by Google

CAPÍTULO 3. TIPOS

será el más adecuado. Es decir, se mantendrá el orden de los elementos, pero no se garantiza la ubicación de los elementos, y se rige
parcialmente por la directiva $PACKRECORDS (esta directiva se explica en la Guía del Programador).

Sin embargo, Free Pascal permite controlar el diseño con las palabras clave Packed y Bitpacked.
El significado de estas palabras depende del contexto:

Bitpacked En este caso, el compilador intentará alinear los tipos ordinales en límites de bits, como por ejemplo
se explica a continuación.

Empaquetado El significado de la palabra clave Empaquetado depende de la situación:

1. En modo MACPAS, equivale a la palabra clave Bitpacked.

2. En otros modos, con la directiva $BITPACKING configurada en ON, también es equivalente a la


Palabra clave empaquetada en bits.

3. En otros modos, con la directiva $BITPACKING configurada en OFF, significa empaquetado normal en límites de bytes.

Empaquetar límites de bytes significa que cada nuevo elemento de un tipo estructurado comienza en un límite de bytes.

El mecanismo de empaquetado de bytes es simple: el compilador alinea cada elemento de la estructura en el primer límite de bytes
disponible, incluso si el tamaño del elemento anterior (tipos enumerados pequeños, tipos de subrango) es menor que un byte.

Cuando se utiliza el mecanismo de empaquetado de bits, el compilador calcula para cada tipo ordinal cuántos bits se necesitan para
almacenarlo. El siguiente tipo ordinal se almacena en el siguiente bit libre. Los tipos no ordinales, que incluyen, entre otros, conjuntos,
flotantes, cadenas, registros (en formato bit), matrices (en formato bit), punteros, clases, objetos y variables de procedimiento, se almacenan
en el primer límite de bytes disponible.

Tenga en cuenta que las partes internas del paquete de bits son opacas: pueden cambiar en cualquier momento en el futuro. Es más: el
empaquetado interno depende del endianidad de la plataforma para la que se realiza la compilación y no es posible ninguna conversión
entre plataformas. Esto hace que las estructuras empaquetadas en bits no sean adecuadas para almacenar en disco o transportar a través
de redes. Sin embargo, el formato es el mismo que el utilizado por el compilador GNU Pascal, y el equipo de Free Pascal pretende mantener
esta compatibilidad en el futuro.

Existen algunas restricciones más para los elementos de estructuras empaquetadas en bits:

• La dirección no se puede recuperar, a menos que el tamaño de bits sea múltiplo de 8 y el elemento ocurra
que se almacenará en un límite de bytes.

• Un elemento de una estructura empaquetada en bits no se puede utilizar como parámetro var, a menos que el tamaño del bit sea
múltiplo de 8 y el elemento esté almacenado en un límite de bytes.

Para determinar el tamaño de un elemento en una estructura empaquetada de bits, existe la función BitSizeOf. Devuelve el tamaño (en bits)
del elemento. Para otros tipos o elementos de estructuras que no están empaquetados en bits, esto simplemente devolverá el tamaño en
bytes multiplicado por 8, es decir, el valor de retorno es entonces el mismo que 8*SizeOf.

El tamaño de los registros y matrices empaquetados en bits es limitado:

• En sistemas de 32 bits, el tamaño máximo es 229 bytes (512 MB).

• En sistemas de 64 bits, el tamaño máximo es 261 bytes.

La razón es que el desplazamiento de un elemento debe calcularse con el tamaño entero máximo del sistema.

42
Machine Translated by Google

CAPÍTULO 3. TIPOS

3.3.1 Matrices

Free Pascal admite matrices como en Turbo Pascal. También se admiten matrices multidimensionales y matrices
empaquetadas (en bits), así como las matrices dinámicas de Delphi:

Tipos de matriz

­­ tipo de matriz formación de tipo ­


[ 6 tipo ordinal ]
empaquetado bitpacked ,

matrices estáticas

Cuando el rango de la matriz se incluye en la definición de la matriz, se denomina matriz estática. Intentar acceder a
un elemento con un índice que está fuera del rango declarado generará un error de tiempo de ejecución (si la
verificación de rango está activada). El siguiente es un ejemplo de una declaración de matriz válida:

Tipo
RealArray = Matriz [1..100] de Real;

Los índices válidos para acceder a un elemento del array están entre 1 y 100, donde se incluyen los bordes 1 y 100.
Como en Turbo Pascal, si el tipo de componente de la matriz es en sí mismo una matriz, es posible combinar las dos
matrices en una matriz multidimensional. La siguiente declaración:

Escriba
APoints = matriz[1..100] de Matriz[1..3] de Real;

es equivalente a la declaración:

Escriba
APoints = matriz[1..100,1..3] de Real;

Las funciones Alto y Bajo devuelven los límites alto y bajo del tipo de índice más a la izquierda de la matriz.
En el caso anterior, esto sería 100 y 1. Debe usarlos siempre que sea posible, ya que mejora la mantenibilidad de su
código. El uso de ambas funciones es tan eficiente como el uso de constantes, porque se evalúan en tiempo de
compilación.

Cuando se asignan variables estáticas de tipo matriz entre sí, se copia el contenido de toda la matriz.
Esto también es válido para matrices multidimensionales:

programa testarray1;

Tipo
TA = Matriz[0..9,0..9] de número entero;

era
A,B:TA;
I,J: Entero; comenzar

Para I:=0 a 9 hacer


Para J:=0 a 9 hacer
A[I,J]:=I*J;

43
Machine Translated by Google

CAPÍTULO 3. TIPOS

Para I:=0 a 9 hacer


comenzar
Para J:=0 a 9 hacer
Escribir(A[I,J]:2,'');
Escribir; fin;

B:=A;
Escribir;
Para I:=0 a 9 hacer
Para J:=0 a 9 hacer
A[9­I,9­J]:=I*J;
Para I:=0 a 9 hacer
comenzar
Para J:=0 a 9 hacer
Escribir(B[I,J]:2,'');
Escribir; fin;

fin.

El resultado de este programa serán dos matrices idénticas.

matrices dinámicas

A partir de la versión 1.1, Free Pascal también conoce matrices dinámicas: en ese caso se omite el rango de la matriz,
como en el siguiente ejemplo:

Tipo
TByteArray = Matriz de bytes;

Al declarar una variable de tipo matriz dinámica, la longitud inicial de la matriz es cero. La longitud real de la matriz
debe establecerse con la función estándar SetLength, que asignará la memoria necesaria para contener los
elementos de la matriz en el montón.

El siguiente ejemplo establecerá la longitud en 1000:

Era
R: TByteArray;

comenzar
EstablecerLongitud(A,1000);

Después de una llamada a SetLength, los índices de matriz válidos son del 0 al 999: el índice de la matriz siempre tiene base cero.

SetLength también se puede utilizar para matrices multidimensionales. El siguiente ejemplo creará una matriz
"rectangular":

Era
A: Matriz de TByteArray;

comenzar
EstablecerLongitud(A,10,100);

Después de una llamada a SetLength, los índices de matriz válidos son del 0 al 9 para la primera dimensión y del 0 al 99 para
la segunda dimensión.

A diferencia de los arreglos multidimensionales estáticos, los arreglos dinámicos no necesitan ser "rectangulares", es
decir, los distintos elementos pueden tener diferentes longitudes:

44
Machine Translated by Google

CAPÍTULO 3. TIPOS

era
a: matriz de matriz de matriz de LongInt; i, j, k: Int largo; comenzar

EstablecerLongitud(a, 10, 5);


EstablecerLongitud(a[5], 3);

para i := Bajo(a) a Alto(a) hacer


para j: = Bajo (a [i]) a Alto (a [i]) comience
EstablecerLongitud(a[i, j], i * 10 + j); para k := Bajo(a[i,
j]) a Alto(a[i, j]) haga a[i, j, k] := i * 10000 + j * 100 + k; fin;

para i: = Bajo (a) a Alto (a) comienza


para j: = Bajo (a [i]) a Alto (a [i]) comience
para k := Bajo(a[i, j]) a Alto(a[i, j]) hacer
Writeln(a[i, j, k]);
Writeln('­­­­­­­'); fin;

Writeln('======='); fin;

fin.

Tenga en cuenta que la longitud de la matriz se establece en elementos, no en bytes de memoria


asignada (aunque pueden ser iguales). La cantidad de memoria asignada es el tamaño de la matriz
multiplicado por el tamaño de 1 elemento de la matriz. La memoria será eliminada al salir del
procedimiento o función actual.

También es posible cambiar el tamaño de la matriz: en ese caso, se conservarán tantos elementos de la matriz como
quepan en el nuevo tamaño. Se puede cambiar el tamaño de la matriz a cero, lo que efectivamente restablece la variable.

En todo momento, intentar acceder a un elemento del array con un índice que no está en la longitud actual del
array generará un error de tiempo de ejecución.

Las matrices dinámicas se cuentan por referencia: la asignación de una variable de tipo matriz dinámica a
otra permitirá que ambas variables apunten a la misma matriz. Al contrario de las ansistrings, una asignación
a un elemento de una matriz se reflejará en la otra: no hay copia en escritura. Considere el siguiente ejemplo:

Era
A,B: TByteArray;

comenzar
EstablecerLongitud(A,10);
A[0]:=33;
B:=A;
A[0]:=31;

Después de la segunda asignación, el primer elemento de B también contendrá 31.

También se puede ver en el resultado del siguiente ejemplo:

programa testarray1;

Tipo
TA = Matriz de matriz de Entero;

45
Machine Translated by Google

CAPÍTULO 3. TIPOS

era
A,B:TA;
I,J: Entero; comenzar

Establecer longitud (A,10,10);


Para I:=0 a 9 hacer
Para J:=0 a 9 hacer
A[I,J]:=I*J;
Para I:=0 a 9 hacer
comenzar
Para J:=0 a 9 hacer
Escribir(A[I,J]:2,'');
Escribir; fin;

B:=A;
Escribir;
Para I:=0 a 9 hacer
Para J:=0 a 9 hacer
A[9­I,9­J]:=I*J;
Para I:=0 a 9 hacer
comenzar
Para J:=0 a 9
escriba(B[I,J]:2,''); Escribir; fin; fin.

El resultado de este programa será una matriz de números y luego la misma matriz reflejada.
Como se comentó anteriormente, las matrices dinámicas se cuentan por referencia: si en uno de los ejemplos
anteriores A sale del alcance y B no, entonces la matriz aún no se ha eliminado: el recuento de referencias de
A (y B) se reduce en 1. Tan pronto como el recuento de referencias llega a cero, se elimina la memoria asignada
para el contenido de la matriz.

La llamada a SetLength asegurará que el recuento de referencias de la matriz devuelta sea 1, es decir, si
dos variables de matriz dinámica apuntaban a la misma memoria, ya no lo harán después de la llamada a
setlength:

programa y texto;

Tipo
TA = matriz de números enteros;

era
A,B:TA;
I: Entero;

comenzar
Establecer longitud (A, 10);
Para I:=0 a 9 hacer
A[Yo]:=Yo;
B:=A;
EstablecerLongitud(B,6);
A[0]:=123;
Para I:=0 a 5 hacer

46
Machine Translated by Google

CAPÍTULO 3. TIPOS

Escribir(B[I]); fin.

También es posible copiar y/o cambiar el tamaño de la matriz con la función Copiar estándar, que actúa como
función de copia para cadenas:

programa testerray3;

Tipo
TA = matriz de números enteros;

era
A,B:TA;
I: Entero;

comenzar
Establecer longitud (A, 10);
Para I:=0 a 9 hacer
A[Yo]:=Yo;
B:=Copiar(A,3,6);
Para I:=0 a 5 hacer
Escribir(B[I]);
fin.

La función Copiar copiará seis elementos de la matriz a una nueva matriz. Comenzando en el elemento en el
índice 3 (es decir, el cuarto elemento) de la matriz.
La función Longitud devolverá el número de elementos de la matriz. La función Baja en una matriz dinámica siempre
devolverá 0 y la función Alta devolverá el valor Longitud­1, es decir, el valor del índice de matriz más alto permitido.

Compatibilidad de tipos de matriz dinámica

Object Pascal es un lenguaje estrictamente tipificado. En ocasiones, dos tipos técnicamente distintos se
consideran compatibles con la asignación (es decir, un valor de un tipo puede asignarse a una variable de otro
tipo) en determinadas circunstancias. Las matrices dinámicas se consideran compatibles con la asignación
cuando utilizan el mismo tipo de elemento. Eso significa que se compilará lo siguiente:

{$modo objfpc}

Tipo
TA = Matriz de números enteros;
TB = Matriz de números enteros;

Era
R: TA;
B: tuberculosis;

comenzar
SetLength(A,1); A[0]:=1;
B:=A; fin.

Pero lo siguiente no lo hará, aunque los tipos de enteros y palabras sean compatibles con la asignación:

47
Machine Translated by Google

CAPÍTULO 3. TIPOS

{$modo objfpc}

Tipo
TA = Matriz de palabras;
TB = Matriz de números enteros;

Era
R: TA;
B: tuberculosis;

comenzar
EstablecerLongitud(A,1);
A[0]:=1;
B:=A;
fin.

constructor de matriz dinámica

A partir de la versión 3.0 de Free Pascal, los tipos de matrices dinámicas tienen un constructor. Esto es intrínseco,
el compilador lo proporciona. Hasta la versión 2.6.4, la única forma de inicializar una matriz dinámica era la siguiente:

Tipo
TIntegerArray = Matriz de números enteros;

era
A: TIntegerArray;

comenzar
SetLength(A,3); A[0]:=1;
A[1]:=2;
A[3]:=3;

Writeln(Longitud(A)); fin.

A partir de la versión 3.0 de Free Pascal, una matriz dinámica se puede inicializar utilizando una sintaxis similar a la de un constructor.
El constructor se llama Crear y acepta como parámetros un número variable de parámetros del tipo de
elemento del tipo de matriz. Esto significa que la inicialización anterior ahora se puede realizar como:

Tipo
TIntegerArray = Matriz de números enteros;

era
A: TIntegerArray;

comenzar
A:=[Link](1,2,3); Writeln(Longitud(A));

fin.

Tenga en cuenta que esto no funcionará para matrices dinámicas para las que no se creó ningún tipo. Es decir, lo
siguiente no funcionará:

era

48
Machine Translated by Google

CAPÍTULO 3. TIPOS

A: Matriz de números enteros;

comenzar
A:=Matriz de [Link](1,2,3); Writeln(Longitud(A)); fin.

Este enfoque también funciona de forma recursiva para matrices multidimensionales:

Tipo
TIntegerArray = Matriz de números enteros;
TIntegerArrayArray = Matriz de TIntegerArray;

era
A: TIntegerArrayArray;

comenzar
A:=[Link]([Link](1,2,3), [Link](4,5,6),
[Link](7,8,9)); Writeln('Longitud
',longitud(A)); fin.

Sin embargo, dado que es un constructor (el código se ejecuta en tiempo de ejecución), no es posible usarlo en una
sintaxis de variable inicializada. Es decir, lo siguiente no funcionará:

Tipo
TIntegerArray = Matriz de números enteros;

era
A: TIntegerArray = [Link](1,2,3);

comenzar
Writeln('Longitud ',longitud(A)); fin.

Expresiones constantes de matriz dinámica

A partir de la versión 3.2 del compilador, se puede construir una matriz utilizando una expresión de matriz en una
asignación explícita o en una variable inicializada. Sin embargo, la expresión es diferente. En una declaración de
asignación, se parece a una expresión establecida; en una variable inicializada, se debe usar la misma sintaxis
que para una matriz constante de longitud fija:

Tipo
TIntegerArray = Matriz de números enteros;

era
A: TIntegerArray = (1,2,3);
B: Matriz de enteros;

comenzar
B:=[3,4,5];
fin.

49
Machine Translated by Google

CAPÍTULO 3. TIPOS

Empacar y desempacar una matriz

Las matrices se pueden empaquetar y empaquetar en bits. Dos tipos de matriz que tienen el mismo tipo de índice y tipo de elemento,
pero que están empaquetados de manera diferente, no son compatibles con la asignación.

Sin embargo, es posible convertir una matriz normal en una matriz empaquetada en bits con la rutina pack. La operación
inversa también es posible; una matriz empaquetada en bits se puede convertir en una matriz empaquetada normalmente
usando la rutina de desempaquetar, como en el siguiente ejemplo:

Era

foo: matriz ['a'..'f'] de booleano


= (falso, falso, verdadero, falso, falso, falso);
bar: matriz empaquetada en bits [42..47] de booleano; baz: matriz ['0'..'5'] de booleano;

comenzar
paquete(foo,'a',bar);
desempaquetar(barra,baz,'0'); fin.

Puede encontrar más información sobre las rutinas de empaque y desempaquetado en la referencia de la unidad del sistema.

3.3.2 Tipos de registros


Free Pascal admite registros fijos y registros con partes variantes. El diagrama de sintaxis para un tipo de
registro es

Tipos de registros

­­ tipo de registro registro fin ­


lista de campos

empaquetado bitpacked

­­ lista de campos campos jos ­


parte variante ;
campos jos ;

­­ campos jos 6 lista de identi cadores : tipo ; ­

­­ caso de pieza variante tipo ordinal de 6 variante ­


identificador : ;

­­ variante 6 constante :( ) ­
, lista de campos

Entonces las siguientes son declaraciones de tipo de registro válidas:

Tipo
Punto = Registro
X,Y,Z : Reales; fin;

RPoint = Registro

50
Machine Translated by Google

CAPÍTULO 3. TIPOS

Caso booleano de
Falso : (X,Y,Z : Real);
Verdadero: (R,theta,phi: Real); fin;

BetterRPoint = Grabar
Caso de usoPolar: booleano de
Falso : (X,Y,Z : Real);
Verdadero: (R,theta,phi: Real); fin;

La parte variante debe ser la última en el registro. El identificador opcional en la declaración del caso sirve para
acceder al valor del campo de etiqueta, que de otro modo sería invisible para el programador. Se puede utilizar para
ver qué variante está activa en un momento determinado3. En efecto, introduce un nuevo campo en el registro.

Observación Es posible anidar piezas variantes, como en:

Tipo
MiRec = Grabar
X: Entero largo;
Byte de caso de
2 : (Y : Entero largo; byte
caso de
3: (Z: Entero largo); );

fin;

Diseño y tamaño del registro

El diseño y el tamaño de un registro están influenciados por cinco aspectos:

• El tamaño de sus campos.

• Los requisitos de alineación de los tipos de campos, que dependen de la plataforma. Tenga en cuenta que
los requisitos de alineación de un tipo dentro de un registro pueden ser diferentes de los de una variable
separada de ese tipo. Además, la ubicación de un campo dentro de un registro también puede influir en
los requisitos de alineación de su tipo.

• La configuración actualmente activa {$ALIGN N} o {$PACKRECORDS N} (estas configuraciones se anulan


entre sí, por lo que la última especificada es la activa; tenga en cuenta que estas directivas no aceptan
exactamente los mismos argumentos, consulte el manual del programador para más información).

• La configuración {$CODEALIGN RECORDMIN=X} actualmente activa.

• La configuración {$CODEALIGN RECORDMAX=X} actualmente activa.

El diseño y el tamaño de las partes variantes en los registros se determina reemplazándolos con un campo cuyo tipo
es un registro con como primer elemento un campo del tipo de campo etiqueta si se declaró un identi cador para este
campo etiqueta, seguido de los elementos de la variante más grande.

El desplazamiento del campo F2 en un registro es igual a la suma del desplazamiento del campo F1 anterior y el tamaño de F1,
redondeado a un múltiplo de la alineación requerida de F2. Esta alineación requerida se calcula de la siguiente manera:

• La alineación requerida se establece en la alineación predeterminada del tipo de campo, posiblemente


ajustada en función del hecho de que este tipo ocurre en un registro y de la ubicación del campo en el registro.

3Sin embargo, es responsabilidad del programador mantener este campo.

51
Machine Translated by Google

CAPÍTULO 3. TIPOS

• Si la alineación requerida es menor que la {$CODEALIGN RECORDMIN=X} actualmente activa


configuración, se cambia a este valor X.

• Si la configuración actualmente activa {$ALIGN N} o {$PACKRECORDS N} es

– un valor numérico: si la alineación requerida es mayor que N, se cambia a NI e., si N


es 1, todos los campos se colocarán uno después del otro.

– RESET o DEFAULT: la alineación requerida resultante depende del objetivo.

– C: la alineación requerida se ajusta de acuerdo con las reglas especificadas en el ABI oficial
para la plataforma actual.

– POWER/POWERPC, MAC68K: el ajuste del valor de alineación se determina siguiendo las reglas oficiales
ABI para resp. las plataformas (clásicas) Macintosh PowerPC o Macintosh 680x0.

El tamaño de un registro es igual a la suma del desplazamiento del último campo del registro y el tamaño de este campo,
redondeado a un múltiplo de la alineación requerida del registro. La alineación requerida del registro se calcula de la
siguiente manera:

• La alineación requerida se establece en la alineación del campo del registro con la alineación más grande, según
se determinó al diseñar el registro.

• Si la configuración actual {$ALIGN N} o {$PACKRECORDS N} es diferente de C y la alineación requerida es mayor


que la {$CODEALIGN RECORDMAX=X} actualmente activa, la alineación requerida se cambia a X.

• Si la configuración actual de {$ALIGN N} o {$PACKRECORDS N} es igual a C, la alineación requerida se determina


siguiendo las reglas oficiales de ABI.

Observaciones y ejemplos

Free Pascal también admite un "registro empaquetado", que es un registro donde todos los elementos están alineados
en bytes. Como resultado, las dos declaraciones siguientes son equivalentes:

{$PackRegistros 1}
Trec2 = Registro

A: Byte;
B: Palabra; fin;

{$PackRecords predeterminado}

Trec2 = Registro empaquetado

A: Byte;
B: Palabra; fin;

¡Tenga en cuenta el {$PackRecords Default} después de la primera declaración para restaurar la configuración predeterminada!

Dada la naturaleza dependiente de la plataforma de cómo se distribuyen los registros en la memoria, la única manera
de garantizar un diseño compatible entre plataformas (suponiendo que todos los campos se declaren usando un tipo
con el mismo significado en estas mismas plataformas) es usando {$PACKRECORDS 1}.

En particular, si se debe leer un archivo escrito con registros, producido por un programa Turbo Pascal, es
probable que el intento de leer ese archivo correctamente falle. La razón es que el valor predeterminado de Free Pascal

52
Machine Translated by Google

CAPÍTULO 3. TIPOS

La configuración {$PACKRECORDS N} no es necesariamente compatible con la de Turbo Pascal. Se puede cambiar a


{$PACKRECORDS 1} o {$PACKRECORDS 2} dependiendo de la configuración utilizada en el programa Turbo Pascal
que crea el archivo (aunque aún puede fallar con {$PACKRECORDS 2} debido a diferentes requisitos de alineación de
tipos entre 16 bit MSDOS y su plataforma actual).

La misma observación se aplica a Delphi: solo se garantiza que el intercambio de datos sea posible si tanto el productor
como el consumidor usan un registro empaquetado, o si están en la misma plataforma y usan la misma configuración
{$PACKRECORDS X}.

3.3.3 Tipos de conjuntos

Free Pascal admite los tipos de conjuntos como en Turbo Pascal. El prototipo de una declaración de conjunto es:

Tipos de conjuntos

­­ conjunto de tipos de tipo ordinal ­

Cada uno de los elementos de SetType debe ser de tipo TargetType. TargetType puede ser cualquier tipo ordinal
con un rango entre 0 y 255. Un conjunto puede contener como máximo 256 elementos. Las siguientes son
declaraciones de conjunto válidas:

Tipo
Basura = Conjunto de Char;
Días = (lunes, martes, miércoles, jueves, viernes, sábado, domingo);

Era
WorkDays: Conjunto de días;

Dadas estas declaraciones, es legal la siguiente cesión:

Días laborables: = [lunes, martes, miércoles, jueves, viernes];

Se pueden realizar varias operaciones sobre conjuntos: tomar uniones o diferencias, agregar o quitar elementos,
comparaciones. Estos están documentados en la sección 12.8.6, página 180.

La forma en que el compilador almacena los conjuntos depende del modo y se puede controlar con una directiva. Para
obtener más información, consulte la guía del programador.

3.3.4 Tipos de archivos

Los tipos de archivos son tipos que almacenan una secuencia de algún tipo base, que puede ser cualquier tipo excepto otro
tipo de archivo. Puede contener (en principio) un número infinito de elementos. Los tipos de archivos se utilizan comúnmente
para almacenar datos en el disco. Sin embargo, nada impide que el programador escriba un controlador de archivo que
almacene sus datos, por ejemplo, en la memoria.

Aquí está la declaración de tipo para un tipo de archivo:

Tipos de archivo

­­ archivo tipo archivo ­


de tipo identi cador

53
Machine Translated by Google

CAPÍTULO 3. TIPOS

Si no se proporciona ningún identificador de tipo, entonces el archivo es un archivo sin tipo; puede considerarse equivalente a
un archivo de bytes. Los archivos sin tipo requieren comandos especiales para actuar sobre ellos (consulte Blockread, Blockwrite).
La siguiente declaración declara un fichero de registros:

Tipo
Punto = Registro
X,Y,Z: reales; fin;

PointFile = Archivo de Punto;

Internamente, los archivos están representados por el registro FileRec, que se declara en las unidades Dos o
SysUtils.

Un tipo de archivo especial es el tipo de archivo de texto, representado por el registro TextRec. Un archivo de tipo Texto
utiliza rutinas especiales de entrada y salida. Los tipos de archivos predeterminados de Entrada, Salida y StdErr se definen
en la unidad del sistema: todos son de tipo Texto y se abren mediante el código de inicialización de la unidad del sistema.

3.4 Consejos

Free Pascal admite el uso de punteros. Una variable del tipo puntero contiene una dirección en la memoria, donde
se pueden almacenar los datos de otra variable. Un tipo de puntero se puede definir de la siguiente manera:

Tipos de puntero

ˆ
­­ tipo de puntero tipo de identificador ­

Como puede verse en este diagrama, los punteros se escriben, lo que significa que apuntan a un tipo particular de
datos. El tipo de estos datos debe conocerse en el momento de la compilación.

Desreferenciar el puntero (indicado agregando ˆ después del nombre de la variable) se comporta entonces como una variable.
Esta variable tiene el tipo declarado en la declaración del puntero y la variable se almacena en la dirección a la que
apunta la variable del puntero. Considere el siguiente ejemplo:

Consejos de programa; tipo

Búfer = Cadena[255];
BufPtr = ^Búfer;
Donde B: Tampón;
PA: BufPtr;
PP: Puntero;
etc..

En este ejemplo, BP es un puntero a un tipo de búfer; mientras que B es una variable de tipo Buffer. B ocupa
256 bytes de memoria y BP sólo ocupa 4 (u 8) bytes de memoria: suficiente memoria para almacenar una dirección.

La expresion

PA^

Se conoce como desreferenciación de BP. El resultado es de tipo Buffer, por lo que

54
Machine Translated by Google

CAPÍTULO 3. TIPOS

PA^[23]

Indica el carácter 23 de la cadena a la que apunta BP.

Observación Free Pascal trata los punteros de la misma manera que C. Esto significa que un puntero a algún tipo puede tratarse
como una matriz de este tipo.

Desde este punto de vista, el puntero apunta al elemento cero de esta matriz. Así, la siguiente declaración de puntero

Var p : ^Entero largo;

puede considerarse equivalente a la siguiente declaración de matriz:

Var p: matriz[0..Infinity] de Longint;

La diferencia es que la primera declaración asigna memoria sólo para el puntero (no para la matriz), y la segunda
declaración asigna memoria para toda la matriz. Si se utiliza el primero, la memoria debe asignarse manualmente,
utilizando la función Getmem. La referencia Pˆ es entonces la misma que p[0].
El siguiente programa ilustra esto quizás más claro:

programa PointerArray; var i: Entero


largo; p : ^Entero largo; pp:
matriz [0..100] de
Longint; comenzar para i := 0 a 100 hacer pp[i] := i;
{ Llenar
matriz } p := @pp[0]; para i := 0 a 100 hacer
{Dejemos que p apunte a pp}

si p[i]<>pp[i] entonces
WriteLn ('¡Oh, problema!')
fin.

Free Pascal admite la aritmética de punteros como lo hace C. Esto significa que, si P es un puntero escrito, las
instrucciones

Inc(P);
diciembre(P);

aumentará, respectivamente disminuirá la dirección a la que apunta el puntero con el tamaño del tipo P al
que apunta. Por ejemplo

Var P: ^Entero largo;


...
Inc (p);

aumentará P en 4, porque 4 es el tamaño de un entero largo. Si el puntero no tiene tipo, se supone un tamaño
de 1 byte (es decir, como si el puntero fuera un puntero a un byte: ˆbyte).

También se pueden utilizar operadores aritméticos normales en punteros, es decir, las siguientes son operaciones
aritméticas de punteros válidas:

var p1,p2 : ^Entero largo;


L: Entero largo; comenzar
comenzar
P1 := @P2;

55
Machine Translated by Google

CAPÍTULO 3. TIPOS

P2 := @L; L :=
P1­P2; P1 := P1­4;
P2 := P2+4; fin.

Aquí, el valor que se suma o resta se multiplica por el tamaño del tipo al que apunta el puntero.
En el ejemplo anterior, P1 se reducirá en 16 bytes y P2 se incrementará en 16.

3.5 Declaraciones de tipo directo

Los programas a menudo necesitan mantener una lista vinculada de registros. Luego, cada registro contiene un
puntero al siguiente registro (y posiblemente también al registro anterior). Para seguridad de tipos, es mejor definir
este puntero como un puntero escrito, de modo que el siguiente registro se pueda asignar en el montón mediante
la llamada Nueva. Para hacerlo, el registro debe definirse de esta manera:

Tipo
TListItem = Registro

Datos: entero;
Siguiente : ^TListItem; fin;

Al intentar compilar esto, el compilador se quejará de que el tipo TListItem aún no está definido cuando encuentra la
declaración Next: Esto es correcto, ya que la definición aún se está analizando.

Para poder tener el elemento Siguiente como puntero escrito, se debe introducir una “declaración de tipo hacia adelante”:

Tipo
PListItem = ^TListItem;
TListItem = Registro

Datos: entero;
Siguiente: PTListItem; fin;

Cuando el compilador encuentra una declaración de puntero escrita donde aún no se conoce el tipo al que se hace
referencia, pospone la resolución de la referencia para más adelante. La definición del puntero es una "declaración de tipo directo".

El tipo al que se hace referencia debe introducirse más adelante en el mismo bloque Tipo. Ningún otro bloque puede
interponerse entre la definición del tipo de puntero y el tipo referenciado. De hecho, es posible que ni siquiera la palabra
Tipo en sí vuelva a aparecer: de hecho, iniciaría un nuevo bloque de tipo, lo que provocaría que el compilador resolviera
todas las declaraciones pendientes en el bloque actual.

En la mayoría de los casos, la definición del tipo referenciado seguirá inmediatamente después de la definición del
tipo de puntero, como se muestra en el listado anterior. El tipo definido hacia adelante se puede utilizar en cualquier
definición de tipo después de su declaración.

Tenga en cuenta que una declaración de tipo directa sólo es posible con tipos y clases de puntero, no con otros
tipos.

3.6 Tipos procesales

Free Pascal tiene soporte para tipos procedimentales, aunque difiere un poco de la implementación de los
mismos Turbo Pascal o Del phi. La declaración de tipo sigue siendo la misma, como se puede ver en el siguiente
diagrama de sintaxis:

56
Machine Translated by Google

CAPÍTULO 3. TIPOS

Tipos procesales

­­ tipo procesal encabezado de función ­


encabezado de procedimiento del objeto está ; modificadores de llamadas
anidado

­­ encabezado de función lista de parámetros formales de función : tipo de resultado ­

­­ encabezado del procedimiento lista de parámetros formales del procedimiento ­

­­ modificadores de llamadas registran cdecl ­


pascal

stdcall
safecall
inline

Para obtener una descripción de las listas de parámetros formales, consulte el capítulo 14, página 207. Los dos ejemplos siguientes
son declaraciones de tipo válidas:

Escriba TOneArg = Procedimiento (Var X: entero);


TNoArg = Función: Real;
varproc: TOneArg; función:
TNoArg;

Se pueden asignar los siguientes valores a una variable de tipo procesal:

1. Nil, tanto para punteros de procedimiento normal como para punteros de método.

2. Una referencia de variable de tipo procedimental, es decir, otra variable del mismo tipo.

3. Un procedimiento global o dirección de función, con encabezado y llamada de función o procedimiento coincidentes.
convención.

4. Una dirección de método.

Dadas estas declaraciones, son válidas las siguientes asignaciones:

Procedimiento printit (Var X: Entero); comenzar

EscribirLn (x); fin;

...
Proceso := @printit;
Función := @Pi;

A partir de este ejemplo, la diferencia con Turbo Pascal es clara: en Turbo Pascal no es necesario utilizar el
operador de dirección (@) al asignar una variable de tipo procedimental, mientras que en Free Pascal sí es
necesario. En caso de que se utilicen los modificadores ­MDelphi o ­MTP, se puede eliminar el operador de dirección.

Observación Los modificadores relacionados con las convenciones de llamada deben ser los mismos que los de la declaración;
es decir, el siguiente código daría un error:

57
Machine Translated by Google

CAPÍTULO 3. TIPOS

Escriba TOneArgCcall = Procedimiento (Var X: entero);cdecl; var proc: TOneArgCcall;


Procedimiento printit (Var X: Entero);
comenzar WriteLn (x); fin; comenzar Proc := @printit; fin.

Porque el tipo TOneArgCcall es un procedimiento que utiliza la convención de llamada cdecl.

En caso de que se agregue la modificación anidada, la variable de procedimiento se puede utilizar con
procedimientos anidados. Esto requiere que las fuentes estén compiladas en modo macpas o ISO, o que el interruptor
de modo nestedprocvars esté activado:

{$modeswitch nestedprocvars} programa


tmaclocalprocparam3;

escriba
tnestedprocvar = el procedimiento está anidado;

era
temperatura: tnestedprocvar;

procedimiento p1(pp: tnestedprocvar); comenzar tempp:=pp;


final
temporal;

procedimiento p2(pp: tnestedprocvar);


era
localpp: tneplaceprocvar; comenzar
localpp :=
pp ; p1 (localpp) fin;

procedimiento n;
comenzar
writeln('llamar a través de n') finalizar;

procedimiento q;

var qi: entero largo;

procedimiento r;
comience
si qi = 1 y luego escriba
('éxito para r') en caso contrario

comenzar
a escribir ('fallar');

58
Machine Translated by Google

CAPÍTULO 3. TIPOS

detener(1) fin
fin;

comenzar
qi:= 1;
p1(@r);
p2(@r);
p1(@n);
p2(@n); fin;

comenzar

fin.

En caso de que se desee asignar métodos de una clase a una variable de tipo procedimental, se debe
declarar el tipo procedimental con el modificador of object.
Los dos ejemplos siguientes son declaraciones de tipo válidas para variables de procedimiento de método (también conocidas
como controladores de eventos debido a su uso en el diseño de GUI):

Escriba TOneArg = Procedimiento (Var X: entero) del objeto;


TNoArg = Función: Real del objeto;
era
oproc: TOneArg; ofunc:
TNoArg;

A estas funciones se les puede asignar un método de firma correcta. Cuando se llama, Self apuntará a la instancia del
objeto que se utilizó para asignar el procedimiento del método.

Se pueden asignar los siguientes métodos de objeto a oproc y ofunc:

Tipo
TMiObjeto = Clase(TOobjeto)
Procedimiento DoX (Var X: número entero);
Función DoY: Real; fin;

Era
M: TMiObjeto;

comenzar
oproc:=@[Link];
ofunc:=@[Link]; fin;

Al llamar a oproc y ofunc, Self será igual a M.

Este mecanismo a veces se denomina delegación.

Observación Al comparar dos variables de tipo método, solo se compara la dirección del método, no el puntero de instancia. Eso
significa que el siguiente programa imprimirá Verdadero:

Tipo
TSomeMethod = Procedimiento del objeto;

59
Machine Translated by Google

CAPÍTULO 3. TIPOS

TMiObjeto = Clase(TOobjeto)
Procedimiento Hacer algo; fin;

Procedimiento [Link];

comenzar
Writeln('En HacerAlgo'); fin;

era
X,Y : TMiObjeto;
P1,P2: TAlgúnMétodo;

comenzar
X:=[Link];
Y:=[Link];
P1:=@[Link] algo;
P2:=@[Link]; Writeln('Mismo
método: ',P1=P2);
fin.

Si se deben comparar ambos punteros, se debe realizar un encasillamiento en TMethod y se deben comparar ambos
punteros. TMethod se define en la unidad del sistema de la siguiente manera:

TMétodo = registro

Código: puntero de código;


Datos: puntero; fin;

Por lo tanto, el siguiente programa imprimirá False:

Tipo
TSomeMethod = Procedimiento del objeto;

TMiObjeto = Clase(TOobjeto)
Procedimiento Hacer algo; fin;

Procedimiento [Link];

comenzar
Writeln('En HacerAlgo'); fin;

era
X,Y : TMiObjeto;
P1,P2: Método T;

comenzar
X:=[Link];
Y:=[Link];
P1:=TMétodo(@[Link]);
P2:=TMétodo(@[Link]);

60
Machine Translated by Google

CAPÍTULO 3. TIPOS

Writeln('Mismo método: ',([Link]=[Link]) y ([Link]=[Link])); fin.

3.7 Tipos de variantes

3.7.1 Definición

A partir de la versión 1.1, FPC admite variantes. Para obtener el máximo soporte de variantes, se recomienda
agregar la unidad de variantes a la cláusula de usos de cada unidad que use variantes de alguna manera: la
unidad de variantes contiene soporte para examinar y transformar variantes distintas al soporte predeterminado
ofrecido por las unidades System u ObjPas.

El tipo de valor almacenado en una variante solo se determina en tiempo de ejecución: depende de lo que se
haya asignado a la variante. Casi cualquier tipo simple se puede asignar a variantes: tipos ordinales, tipos de
cadena, tipos int64.
Los tipos estructurados como conjuntos, registros, matrices, archivos, objetos y clases no son compatibles con
la asignación de una variante, ni con los punteros. A una variante se pueden asignar interfaces y objetos COM o
CORBA (básicamente porque son simplemente un puntero).

Esto significa que las siguientes asignaciones son válidas:

Tipo
TMyEnum = (Uno, Dos, Tres);

Era
V: Variante;
I: Entero;
B: Byte;
W: Palabra;
P: Int64;
E: Extendido;
D: Doble;
Pero: TMyEnum;
COMO: AnsiString;
WS: cadena ancha;

comenzar
V:=yo;
V:=B;
V:=W;
V:=Q;
V:=E;
P:=Y;
V:=D;
V:=COMO;
V:=WS;
fin;

Y, por supuesto, también al revés.

Una variante puede contener una matriz de valores: todos los elementos de la matriz tienen el mismo tipo (pero pueden
ser del tipo “variante”). Para una variante que contiene una matriz, la variante se puede indexar:

Prueba del programa;

61
Machine Translated by Google

CAPÍTULO 3. TIPOS

utiliza variantes;

Era
Una variante;
yo: número entero;

comenzar
A:=VarArrayCreate([1,10],varInteger);
Para I:=1 a 10 hacer
A[Yo]:=Yo; fin.

Para obtener una explicación de VarArrayCreate, consulte Referencia de unidad.

Tenga en cuenta que cuando la matriz contiene una cadena, no se considera una "matriz de caracteres" y, por lo tanto, la
variante no se puede indexar para recuperar un carácter en una determinada posición de la cadena.

3.7.2 Variantes en asignaciones y expresiones

Como puede verse en la definición anterior, la mayoría de los tipos simples se pueden asignar a una variante.
Asimismo, se puede asignar una variante a un tipo simple: Si es posible, el valor de la variante se convertirá al
tipo que se está asignando. Esto puede fallar: la asignación de una variante que contiene una cadena a un
número entero fallará a menos que la cadena represente un número entero válido. En el siguiente ejemplo, la
primera tarea funcionará, la segunda fallará:

prueba del programav3;

utiliza variantes;

Era
V: Variante;
I: Entero;

comenzar
V:='100';
Yo:=V;
Writeln('Yo: ',Yo);
V:='Algo más';
Yo:=V;
Writeln('Yo: ',Yo);
fin.

La primera asignación funcionará, pero la segunda no, ya que algo más no se puede convertir en un valor entero válido.
El resultado será una excepción EConvertError.

El resultado de una expresión que involucra una variante volverá a ser del tipo variante, pero se puede asignar
a una variable de un tipo diferente, si el resultado se puede convertir a una variable de este tipo.
Tenga en cuenta que las expresiones que incluyen variantes tardan más en evaluarse y, por lo tanto, deben
usarse con precaución. Si es necesario realizar muchos cálculos, es mejor evitar el uso de variantes.

Al considerar conversiones de tipos implícitas (por ejemplo, byte a entero, entero a doble, carácter a cadena), el
compilador ignorará las variantes a menos que una variante aparezca explícitamente en la expresión.

62
Machine Translated by Google

CAPÍTULO 3. TIPOS

3.7.3 Variantes e interfaces

Observación La compatibilidad con la interfaz de envío para variantes actualmente no funciona en el compilador.

Las variantes pueden contener una referencia a una interfaz: una interfaz normal (que desciende de IInterface) o
una interfaz de despacho (que desciende de IDispatch). Se pueden usar variantes que contienen una referencia a
una interfaz de despacho para controlar el objeto detrás de él: el compilador usará un enlace tardío para realizar
la llamada a la interfaz de despacho: no habrá verificación en tiempo de ejecución de los nombres de las funciones
y los parámetros o argumentos dados. a las funciones. El tipo de resultado tampoco se verifica. El compilador
simplemente insertará código para realizar la llamada de envío y recuperar el resultado.

Básicamente, esto significa que puedes hacer lo siguiente en Windows:

Era
W: Variante;
V: Cadena;

comenzar
W:=CreateOleObject('[Link]'); V:=[Link];
Writeln('La versión instalada de MS Word
es: ',V); fin;

La línea

V:=[Link];

se ejecuta insertando el código necesario para consultar la interfaz de despacho almacenada en la variante W, y
ejecuta la llamada si se encuentra la información de despacho necesaria.

3.8 Alias de tipos

Los alias de tipo son una forma de darle otro nombre a un tipo, pero también se pueden utilizar para crear tipos realmente nuevos.
Cuál de los dos depende de la forma en que se define el alias de tipo:

Escriba alias

­­ escriba alias identificador ­


tipo

El primer caso es sólo una forma de darle otro nombre a un tipo:

Tipo
MiEntero = Entero;

Esto crea un nuevo nombre para hacer referencia al tipo Integer, pero no crea un nuevo tipo real. Es
decir, dos variables:

Era
A: MiEntero;
B: Entero;

63
Machine Translated by Google

CAPÍTULO 3. TIPOS

En realidad, tendrá el mismo tipo desde el punto de vista del compilador (es decir, Integer).

Lo anterior presenta una manera de hacer que los tipos sean independientes de la plataforma, utilizando solo los tipos
de alias y luego definiendo estos tipos para cada plataforma individualmente. Cualquier programador que luego utilice
estos tipos personalizados no tiene que preocuparse por el tamaño del tipo subyacente: le resulta opaco. También
permite utilizar nombres de acceso directo para nombres de tipos completos. por ejemplo, defina [Link] como
Olongint y luego redefina longint.

Con frecuencia se ve que el alias vuelve a exponer un tipo:

Unidad A;

Interfaz

Utiliza B;

Escriba
MiTipo = [Link];

Esta construcción se ve a menudo después de alguna refactorización, al mover algunas declaraciones de la unidad A a la
unidad B, para preservar la compatibilidad con versiones anteriores de la interfaz de la unidad A.

El segundo caso es un poco más sutil:

Tipo
MiInteger = Escriba un número entero;

Esto no solo crea un nuevo nombre para hacer referencia al tipo Integer, sino que también crea un nuevo tipo. Es
decir, dos variables:

Era
A: MiEntero;
B: Entero;

No tendrá el mismo tipo desde el punto de vista del compilador. Sin embargo, estos dos tipos serán compatibles con la
asignación. Eso significa que una tarea

A:=B;

trabajará.

La diferencia se puede ver al examinar la información de tipo:

Si TypeInfo(MyInteger)<>TypeInfo(Integer) entonces
Writeln('MyInteger e Integer son tipos diferentes');

La función del compilador TypeInfo devuelve un puntero a la información de tipo en el binario. Dado que los dos tipos
MyInteger e Integer son diferentes, generarán bloques de información de diferentes tipos y los punteros serán diferentes.

Hay tres consecuencias de tener diferentes tipos:

1. Que tienen diferente tipo de información, por lo tanto, diferente RTTI (Información de tipo de tiempo de ejecución).

2. Se pueden utilizar en sobrecargas de funciones, es decir

Procedimiento MyProc(A: MyInteger); sobrecarga;


Procedimiento MyProc(A: Entero); sobrecarga;

64
Machine Translated by Google

CAPÍTULO 3. TIPOS

trabajará. Esto no funcionará con un alias de tipo simple.

3. Se pueden utilizar en sobrecargas del operador, es decir

Operador +(A,B: MiEntero): MiEntero;

funcionará también.

3.9 tipos gestionados

De forma predeterminada, los tipos pascal no están administrados. Eso significa que las variables deben inicializarse,
finalizarse, asignarse memoria, etc. Sin embargo, en Object Pascal se gestionan varios tipos, lo que significa que el
compilador inicializa y finaliza variables de este tipo: esto es necesario, por ejemplo, para tipos de datos contados por
referencia.

Se gestionan los siguientes tipos:

AnsiString Se inicializan a Nil.

UnicodeString Se inicializan a Nil.

WideString Se inicializan a Nil.

Interfaz Se inicializan a Nil.

Los arreglos dinámicos se inicializan a Nil.

Y cualquier registro o matriz cuyos elementos contengan tipos administrados.

Las instancias de clase que contienen tipos administrados también se inicializan, pero el puntero de la instancia de clase en
sí no.

Las variables de tipos gestionados también se finalizarán: esto significa, en general, que su recuento de referencias
disminuirá a más tardar al final del alcance actual.

Observación Tenga en cuenta que no se debe hacer ninguna suposición sobre el momento exacto de esta finalización. todo eso es guaran
Asegúrese de que estén finalizados cuando salgan del alcance.

sesenta y cinco
Machine Translated by Google

Capítulo 4

variables

4.1 De nición

Las variables son ubicaciones de memoria nombradas explícitamente con un tipo determinado. Al asignar valores a
las variables, el compilador de Free Pascal genera código de máquina para mover el valor a la ubicación de memoria
reservada para esta variable. El lugar donde se almacena esta variable depende de dónde se declara:

• Las variables globales son variables declaradas en una unidad o programa, pero no dentro de un
procedimiento o función. Se almacenan en ubicaciones fijas de memoria y están disponibles durante todo el
tiempo de ejecución del programa.

• Las variables locales se declaran dentro de un procedimiento o función. Su valor se almacena en el pro
pila de gramos, es decir, no en ubicaciones fijas.

El compilador de Free Pascal gestiona la asignación de estas ubicaciones de memoria de forma transparente, aunque
esta ubicación puede verse influenciada en la declaración.

El compilador de Free Pascal también maneja la lectura o escritura de valores en las variables de forma
transparente. Pero incluso esto puede ser manejado explícitamente por el programador cuando usa propiedades.

Las variables deben declararse explícitamente cuando sean necesarias. No se asigna memoria a menos que se
declare una variable. El uso de un identificador de variable (por ejemplo, una variable de bucle) que no se declara
primero es un error que será informado por el compilador.

4.2 Declaración

Las variables deben declararse en un bloque de declaración de variables de una unidad o de un procedimiento o función
(sección 16.5, página 252). Tiene el siguiente aspecto:

declaración de variables

­­ identificador de declaración de variable: tipo ­


= expresión
­ directiva de sugerencia; ­
modi cadores de variables

66
Machine Translated by Google

CAPÍTULO 4. VARIABLES

­­modificadores de variables 6 identificador de expresión entero ­


absoluto;
exportar ;
cvar
; externo
constante de cadena constante de cadena de nombre
directiva de pista

­ ­

Esto significa que las siguientes son declaraciones de variables válidas:

Era
curterm1: número entero;

curterm2: número entero; cvar; curterm3:


número entero; cvar; externo;

curterm4: número entero; nombre externo 'curterm3'; curterm5: número


entero; nombre 'libc' externo 'curterm9';

curterm6 : entero absoluto curterm1;

curterm7: número entero; cvar; exportar; curterm8:


número entero; cvar; público; curterm9: número entero;
nombre de exportación 'yo'; curterm10: número entero;
nombre público 'ma';

curterm11: entero = 1;

La diferencia entre estas declaraciones es la siguiente:

1. La primera forma (curterm1) define una variable regular. El compilador gestiona todo mediante
sí mismo.

2. La segunda forma (curterm2) declara también una variable normal, pero especifica que el nombre del ensamblador
para esta variable es igual al nombre de la variable tal como está escrito en el código fuente.

3. La tercera forma (curterm3) declara una variable que está ubicada externamente: el compilador asumirá que la
memoria está ubicada en otro lugar y que el nombre del ensamblador de esta ubicación está especificado por
el nombre de la variable, tal como está escrito en la fuente. Es posible que no se especifique el nombre.

4. La cuarta forma es completamente equivalente a la tercera, declara una variable que se almacena
externamente y proporciona explícitamente el nombre del ensamblador de la ubicación. Si no se utiliza
cvar, se debe especificar el nombre.

5. La quinta forma es una variante de la cuarta forma, solo el nombre de la biblioteca en la que se encuentra la memoria.
está reservado también se especifica.

6. La sexta forma declara una variable (curterm6) y le dice al compilador que está almacenada en el
misma ubicación que otra variable (curterm1).

7. La séptima forma declara una variable (curterm7) y le dice al compilador que la etiqueta del ensamblador de esta
variable debe ser el nombre de la variable (distingue entre mayúsculas y minúsculas) y debe hacerse pública.
es decir, se puede hacer referencia a él desde otros archivos objeto.

67
Machine Translated by Google

CAPÍTULO 4. VARIABLES

8. La octava forma (curterm8) es equivalente a la séptima: “público” es un alias de “exportar”.

9. Las formas novena y décima son equivalentes: especifican el nombre del ensamblador de la variable.

10. la undécima forma declara una variable (curterm11) y la inicializa con un valor (1 en el
caso anterior).

Tenga en cuenta que los nombres de los ensambladores deben ser únicos. No es posible declarar o exportar dos
variables con el mismo nombre de ensamblador. En particular, no intente exportar variables con un nombre público
que comience con FPC_; el compilador utiliza algunas rutinas internas del sistema con este nombre.

4.3 Alcance

Las variables, como cualquier identificador, obedecen a las reglas generales de alcance. Además, las variables inicializadas
se inicializan cuando entran en el alcance:

• Las variables globalmente inicializadas se inicializan una vez, cuando se inicia el programa.

• Las variables locales inicializadas se inicializan cada vez que se ingresa al procedimiento.

Tenga en cuenta que el comportamiento de las variables inicializadas locales es diferente del de una constante escrita local.
Una constante escrita local se comporta como una variable inicializada global.

4.4 Variables inicializadas

Por defecto, las variables simples en Pascal no se inicializan después de su declaración. Cualquier suposición de que
contengan 0 o cualquier otro valor predeterminado es errónea: pueden contener basura. Para remediar esto, existe
el concepto de variables inicializadas. La diferencia con las variables normales es que su declaración incluye un valor
inicial, como se puede ver en el diagrama del apartado anterior.

Observación Existen 2 excepciones a esta regla:

1. Los tipos administrados son una excepción a esta regla: los tipos administrados siempre se inicializan con un
valor predeterminado: en general, esto significa establecer el recuento de referencia en cero o establecer el
valor del puntero del tipo en Nil. Ver sección 3.9, página 65

2. Las variables globales se inicializan con el equivalente a cero.

Tenga en cuenta que el comportamiento de poner a cero ciertas variables puede generar contenido no válido para las variables:

Tipo
TWeekDays =
(lunes,martes,miércoles,jueves,viernes,sábado,domingo);
TWeekend = sábado..domingo;

era
W: TFin de semana;

comenzar
Escribir(W);
fin.

Lo anterior dará como resultado, cuando se ejecute, un error:

68
Machine Translated by Google

CAPÍTULO 4. VARIABLES

Error de tiempo de ejecución 107 en $000000000040024A


$000000000040024A
$000000000042BF70
$00000000004001D2

Por lo tanto, es muy recomendable inicializar siempre las variables antes de usarlas.

Esto se puede hacer fácilmente en la declaración de las variables. Dada la declaración:

Era
S: Cadena = 'Esta es una cadena inicializada';

El valor de la siguiente variable se inicializará con el valor proporcionado. La siguiente es una forma aún mejor de hacer
esto:

constante

SDefault = 'Esta es una cadena inicializada';

Era
S: Cadena = SDpredeterminado;

La inicialización se utiliza a menudo para inicializar matrices y registros. Para las matrices, los elementos inicializados
deben especificarse, encerrados entre corchetes y separados por comas. El número de elementos inicializados debe ser
exactamente igual al número de elementos en la declaración del tipo. Como ejemplo:

Era
tt: matriz[1..3] de cadena[20] = ('no','gij','hij'); ti : matriz[1..3] de Entero largo = (1,2,3);

Para registros constantes, cada elemento del registro que desea inicializar debe especificarse en el formulario Campo:
Valor, separado por punto y coma y entre corchetes. Puede omitir los campos que no desea inicializar, de hecho puedes
omitir todos los campos. Si omite campos, el compilador emitirá una advertencia.

Como ejemplo:

Tipo
Punto = registro
X,Y : Final real;

Era
Origen: Punto = (X:0.0; Y:0.0); Parcial: Punto = (X:0.0);
Vacío: Punto = ();

Las declaraciones anteriores darán lugar a las siguientes advertencias:

[Link](7,27) Advertencia: algunos campos que vienen después de "X" no se inicializaron [Link](8,20) Advertencia: algunos campos
que vienen después de "" no fueron inicializados

El orden de los campos en un registro constante debe ser el mismo que en la declaración de tipo; de lo contrario, se
producirá un error en tiempo de compilación.

Observación Cabe destacar que las variables inicializadas se inicializan cuando entran en el alcance, a diferencia de las
constantes escritas, que se inicializan al inicio del programa. Esto también es válido para las inicializaciones locales.

69
Machine Translated by Google

CAPÍTULO 4. VARIABLES

variables. Las variables localizadas inicializadas se inicializan cada vez que se llama a la rutina. Cualquier
cambio que haya ocurrido en la invocación anterior de la rutina se deshará porque se inicializarán nuevamente.
Observación Se debe tener cuidado al utilizar tipos de punteros inicializados como PChars. En los siguientes ejemplos,
S es un puntero que apunta a un bloque de datos de programa constantes (de sólo lectura). Por lo tanto, no
funcionará asignar un carácter en la cadena. Por supuesto, la asignación de S en sí misma funcionará. La
primera rutina dará error, la segunda no:

procedimiento fo1;
era
s:PChar = 'PChar'; comenzar
s[0] :=
'a'; fin;

procedimiento foo2;
era
s: PCar;
comenzar
s := 'PChar'; s[0] :=
'un'; fin;

4.5 Inicializando variables usando el valor predeterminado

Algunas variables deben inicializarse porque contienen tipos administrados. Para las variables que se
declaran en la sección var de una función o en el programa principal, esto sucede automáticamente. Para
las variables que se asignan en el montón, este no es necesariamente el caso.
Para ello, el compilador contiene el intrínseco predeterminado. Esta función acepta un identificador de tipo
como argumento y devolverá una variable correctamente inicializada de ese tipo. En esencia, pondrá a
cero toda la variable.

A continuación se muestra un ejemplo de su uso:

tipo
TRegistro = registro

i: Int Largo; s:
AnsiString; fin;

era
i: Int Largo; o:
TObjeto; r:
TRegistro;
comenzar
i := Predeterminado(LongInt); // 0 o :=
Predeterminado(TObject); // Nil r :=
Predeterminado(TRecord); // (yo: 0; s: '')
fin.

El caso en el que se asigna una variable en el montón es más interesante:

tipo
TRegistro = registro

70
Machine Translated by Google

CAPÍTULO 4. VARIABLES

i: Int Largo; s:
AnsiString; fin;

era
yo: ^LongInt; o:
^TObjeto; r:
^TRegistro; comenzar

i:=GetMem(SizeOf(Longint)); i^ :=
Predeterminado(LongInt); // 0
o:=GetMem(SizeOf(TObject)); o^ :=
Predeterminado(TObject); // Nil
r:=GetMem(SizeOf(TRecord)); r^ :=
Predeterminado(TRecord); // (yo: 0; s: '')
fin.

Funciona para todos los tipos, excepto para los distintos tipos de archivos (o tipos complejos que contienen un tipo de archivo).

Observación

• Para los genéricos, el uso de Default es especialmente útil, ya que es posible que no se conozca el tipo de una
variable durante la declaración de un genérico. Para más información sección 8.7, página 134.

• Los resultados de las funciones están disponibles como un identificador de resultados y, como tales, se parecen a
las variables. No son variables, pero se tratan como parámetros pasados por referencia. Por tanto, no se inicializan.

4.6 Variables del hilo

Para un programa que utiliza subprocesos, las variables pueden ser realmente globales, es decir, las mismas para todos los
subprocesos, o locales: esto significa que cada subproceso obtiene una copia de la variable. Las variables locales (definidas
dentro de un procedimiento) son siempre locales de subproceso. Las variables globales normalmente son las mismas para
todos los subprocesos. Una variable global se puede declarar local de subproceso reemplazando la palabra clave var al inicio
del bloque de declaración de variable con Threadvar:

Varilla de subprocesos

IOResultado: Entero;

Si no se utilizan subprocesos, la variable se comporta como una variable ordinaria. Si se utilizan subprocesos, se realiza
una copia para cada subproceso (incluido el subproceso principal). Tenga en cuenta que la copia se realiza con el valor
original de la variable, no con el valor de la variable en el momento en que se inicia el hilo.

Threadvars debe usarse con moderación: existe una sobrecarga para recuperar o establecer el valor de la variable. Si
es posible, considere utilizar variables locales; siempre son más rápidos que las variables de hilo.

Los hilos no están habilitados de forma predeterminada. Para obtener más información sobre la programación de subprocesos, consulte el
capítulo sobre subprocesos en la Guía del programador.

4.7 Propiedades
Un bloque global puede declarar propiedades, tal como se podrían definir en una clase. La diferencia es que la propiedad
global no necesita una instancia de clase: solo hay una instancia de esta propiedad. Otro

71
Machine Translated by Google

CAPÍTULO 4. VARIABLES

Más allá de eso, una propiedad global se comporta como una propiedad de clase. Los especificadores de lectura/escritura para la
propiedad global también deben ser procedimientos regulares, no métodos.

El concepto de propiedad global es específico de Free Pascal y no existe en Delphi. Se requiere el


modo ObjFPC para trabajar con propiedades.
El concepto de propiedad global se puede utilizar para "ocultar" la ubicación del valor, calcular el
valor sobre la marcha o comprobar los valores escritos en la propiedad.
La declaración es la siguiente:

Propiedades

­­ identificador de definición de propiedad especificadores de propiedad ­


interfaz de propiedad

­­ interfaz de propiedad : tipo identi cador ­


lista de parámetros de propiedad
­ ­
índice entero constante

­­ lista de parámetros de propiedad [ Declaración de 6 parámetros ] ­


;

­­ especi caciones de propiedad ­


leer especificación escribir especi cación especificador predeterminado

­­ leer un campo o función de lectura especí ca ­

­­ escribir un campo o procedimiento de escritura específico ­

­­ especificador predeterminado predeterminado ­


valor

predeterminado de nodo constante

­­ campo o procedimiento Identificador de ­


campo identificador de procedimiento

­­ campo o función Identificador de ­


campo identificador de función

Lo siguiente es un ejemplo:

{$modo objfpc} unidad


de pruebaprop;

Interfaz

Función GetMyInt: Entero;


Procedimiento SetMyInt(Valor: Entero);

Propiedad
MyProp: Entero Leer GetMyInt Escribir SetMyInt;

Implementación

72
Machine Translated by Google

CAPÍTULO 4. VARIABLES

Utiliza sysutils;

Era
FMyInt: Entero;

Función GetMyInt: Entero;

comenzar
Resultado:=FMyInt; fin;

Procedimiento SetMyInt(Valor: Entero);

comenzar
Si ((Valor mod 2)=1) entonces
Raise [Link]('MyProp solo puede contener valores pares');
FMyInt:=Valor; fin;

fin.

Los especificadores de lectura/escritura se pueden ocultar declarándolos en otra unidad que debe estar en la cláusula de
usos de la unidad. Esto se puede utilizar para ocultar los especificadores de acceso de lectura/escritura para los
programadores, como si estuvieran en una sección privada de una clase (que se analiza a continuación). Para el ejemplo
anterior, esto podría verse así:

{$modo objfpc} unidad


de prueba;

Interfaz

Función GetMyInt: Entero;


Procedimiento SetMyInt(Valor: Entero);

Implementación

Utiliza sysutils;

Era
FMyInt: Entero;

Función GetMyInt: Entero;

comenzar
Resultado:=FMyInt; fin;

Procedimiento SetMyInt(Valor: Entero);

comenzar
Si ((Valor mod 2)=1) entonces
Raise [Link]('Sólo se permiten valores pares');
FMyInt:=Valor;

73
Machine Translated by Google

CAPÍTULO 4. VARIABLES

fin;

fin.

La propiedad de prueba unitaria entonces se vería así:

{$modo objfpc} unidad


de pruebaprop;

Interfaz

utiliza prueba;

Propiedad
MyProp: Entero Leer GetMyInt Escribir SetMyInt;

Implementación

fin.

Puede encontrar más información sobre las propiedades en el capítulo 6, página 86.

74
Machine Translated by Google

Capítulo 5

Objetos

5.1 Declaración
Free Pascal admite la programación orientada a objetos. De hecho, la mayor parte del compilador está escrito
utilizando objetos. Aquí presentamos algunas preguntas técnicas sobre la programación orientada a objetos en
Free Pascal.

Los objetos deben tratarse como un tipo especial de registro. El registro contiene todos los
campos declarados en la definición de objetos y punteros a los métodos asociados al tipo de objetos.
Un objeto se declara del mismo modo que se declararía un registro; excepto que ahora, los procedimientos y
funciones pueden declararse como si fueran parte del registro. Los objetos pueden "heredar" campos y métodos de
objetos "principales". Esto significa que estos campos y métodos se pueden utilizar como si estuvieran incluidos en
los objetos declarados como objetos "secundarios".

Además, se introduce un concepto de visibilidad: campos, procedimientos y funciones pueden declararse


como públicos, protegidos o privados. De forma predeterminada, los campos y métodos son públicos y se
exportan fuera de la unidad actual.

Los campos o métodos declarados privados solo son accesibles en la unidad actual: su alcance
se limita a la implementación de la unidad actual.
La declaración prototipo de un objeto es la siguiente:

tipos de objetos

­­ objeto Fin de la lista de 6 componentes


­
lleno resumen herencia
sellado

­­ patrimonio ( identificador de tipo de objeto )


­

­­ lista de componentes ­
especificador de visibilidad de objetos 6 definición constante

­ ­
6 definición de campo 6 definición del método

­­ definición de campo lista de identi cadores : tipo ;


­
era estático;
clase

75
Machine Translated by Google

CAPÍTULO 5. OBJETOS

­­ definición constante identificador constante = expresión constante ; ­

­­ especi cador de visibilidad de objetos privado ­


protegido
público

Como puede verse, cualquier bloque de visibilidad se puede especificar varias veces, tantas veces como sea necesario.

La siguiente es una definición válida de un objeto:

Tipo
TObj = objeto
Privado
Título: Cadena corta;
Público
Inicio del constructor;
Destructor hecho;
Procedimiento SetCaption (AValue: String);
Función GetCaption: Cadena; fin;

Contiene un par constructor/destructor y un método para obtener y establecer un título. El campo Título es
privado para el objeto: no se puede acceder a él fuera de la unidad en la que se declara TObj.
Observación En el modo MacPas, la palabra clave Object se reemplaza por la palabra clave class para compatibilidad
con otros compiladores Pascal disponibles en Mac. Eso significa que los objetos no se pueden utilizar en modo
MacPas.

Observación Free Pascal también soporta el objeto empaquetado. Esto es lo mismo que un objeto, solo que los elementos (campos)
del objeto están alineados en bytes, tal como en el registro empaquetado. La declaración de un objeto empaquetado es
similar a la declaración de un registro empaquetado:

Tipo
TObj = objeto empaquetado
Inicio del constructor;
...
fin;
Pobj = ^TObj;
Var PP: Pobj;

De manera similar, la directiva {$PackRecords } también actúa sobre objetos.

5.2 Objetos abstractos y sellados

Un objeto puede declararse sellado. En ese caso, no es posible declarar un objeto descendiente.
El compilador devolverá un error si encuentra una declaración de un descendiente:

Tipo
TMyClass = objeto sellado
x : número
entero; fin;

TMiClase2 = objeto(TMiClase)

76
Machine Translated by Google

CAPÍTULO 5. OBJETOS

Y: Entero; fin;

empezar
fin.

Esto dará como resultado el siguiente error:

Error: No se puede crear un descendiente de la clase sellada "TMyClass"

Una clase abstracta es una clase que no se puede utilizar directamente. En su lugar, siempre se debe utilizar una clase
descendiente. Sin embargo, por compatibilidad con Delphi, el compilador ignora esta directiva.

5.3 Campos

Los campos de objeto son como campos de registro. Se accede a ellos de la misma manera que se accedería a un
campo de registro: utilizando un identificador calificado. Dada la siguiente declaración:

Tipo TAnObject = Objeto


Campo A: Entero largo;
Procedimiento AMétodo; fin;

Var AnObject: TAnObject;

entonces la siguiente sería una asignación válida:

[Link]:= 0;

Dentro de los métodos, se puede acceder a los campos utilizando el identificador corto:

Procedimiento [Link];
comenzar
...
Campo A := 0;
...
fin;

O bien, se puede utilizar el autoidentificador. El autoidentificador se refiere a la instancia actual del objeto:

Procedimiento [Link];
comenzar
...
[Link]:= 0;
...
fin;

No se puede acceder a campos que se encuentran en secciones privadas o protegidas de un objeto desde fuera de
los métodos de los objetos. Si se intenta esto de todos modos, el compilador se quejará de un identificador desconocido.

También es posible utilizar la instrucción with con una instancia de objeto, tal como con un registro:

Con AnObject comienza

Fuera := 12;
Un método;
fin;

77
Machine Translated by Google

CAPÍTULO 5. OBJETOS

En este ejemplo, entre el principio y el final, es como si se antepusiera AnObject a los identificadores Afield y Amethod.
Más sobre esto en la sección 13.2.8, página 204.

5.4 Campos de clase o estáticos

Un objeto puede contener campos de clase o estáticos: estos campos son globales para el tipo de objeto y actúan
como variables globales, pero solo se conocen en el alcance del objeto. La diferencia entre variables estáticas y de
clase es puramente el modo en que funcionan: la palabra clave estática siempre funcionará, la palabra clave de clase
necesitará {$MODE DELPHI} o {$MODE OBJFPC}.

Se puede hacer referencia a ellos desde dentro de los métodos del objeto, pero también se puede hacer referencia a ellos
desde fuera del objeto proporcionando el nombre completo.

Por ejemplo, la salida del siguiente programa:

{$modo objfpc} tipo

cl=objeto l: entero
largo; estático; clase var v: entero;
fin;

era
cl1,cl2 : cl;

comenzar
Writeln('Estático'); cl1.l:=2;
writeln(cl2.l);
cl2.l:=3; writeln(cl1.l);
Writeln(cl.l);
Writeln('Clase'); cl1.v:=4;
escribir(cl2.v); cl2.v:=5;
writeln(cl1.v); Writeln(cl.v);

fin.

será el siguiente

Estático
2
3
3
Clase
4
5
5

Tenga en cuenta que la última línea de código hace referencia al tipo de objeto en sí (cl) y no a una instancia del
objeto (cl1 o cl2).

78
Machine Translated by Google

CAPÍTULO 5. OBJETOS

5.5 Constructores and destructores

Como se puede ver en el diagrama de sintaxis de una declaración de objeto, Free Pascal admite
constructores y destructores. El programador es responsable de llamar explícitamente al constructor y al
destructor cuando utiliza objetos.
La declaración de un constructor o destructor es la siguiente:

Constructores and destructores

­­ encabezado del constructor de declaración del constructor ; bloque de subrutina ­

­­ encabezado del destructor de declaración del destructor ; bloque de subrutina ­

­­ constructor encabezado constructor identificador ­


identificador del método calificado
­ lista de parámetros formales ­

­­ destructor de encabezado destructor identificador ­


identificador del método calificado
­ lista de parámetros formales ­

Se requiere un par constructor/destructor si el objeto usa métodos virtuales. La razón es que para
un objeto con métodos virtuales, se debe realizar alguna limpieza interna: esta limpieza la realiza
el constructor1.
En la declaración del tipo de objeto, se debe utilizar un identificador simple para el nombre del
constructor o destructor. Cuando se implementa el constructor o destructor, se debe utilizar un
identificador de método calificado, es decir, un identificador de la forma [Link].
Free Pascal también soporta la sintaxis extendida de los procedimientos New y Dispose. En caso de que
se deba asignar una variable dinámica de un tipo de objeto, el nombre del constructor se puede especificar
en la llamada a Nuevo. New se implementa como una función que devuelve un puntero al objeto instanciado.
Considere las siguientes declaraciones:

Tipo
TObj = objeto;
Inicio del constructor;
...
fin;
Pobj = ^TObj;
Var PP: Pobj;

Entonces las tres llamadas siguientes son equivalentes:

pp := nuevo ( Pobj , Sol );

nuevo(pp,calor);

y también

1Se debe configurar un puntero al VMT.

79
Machine Translated by Google

CAPÍTULO 5. OBJETOS

nuevo
(páginas); pp^.init;

En el último caso, el compilador emitirá una advertencia de que se debe utilizar la sintaxis extendida de
new y dispose para generar instancias de un objeto. Es posible ignorar esta advertencia, pero es una
mejor práctica de programación utilizar la sintaxis extendida para crear instancias de un objeto. De
manera similar, el procedimiento Dispose acepta el nombre de un destructor. Luego se llamará al
destructor, antes de eliminar el objeto del montón.
En vista de la advertencia del compilador, el siguiente capítulo presenta el enfoque Delphi para la
programación orientada a objetos, y puede considerarse una forma más natural de programación
orientada a objetos.

5.6 Métodos
Los métodos de objetos son como procedimientos o funciones ordinarias, sólo que tienen un parámetro
extra implícito: self. Self apunta al objeto con el que se invocó el método. Al implementar métodos, se
debe proporcionar el identificador completo en el encabezado de la función. Al declarar métodos, se
debe proporcionar un identificador normal.

5.6.1 Declaración
La declaración de un método es muy parecida a una declaración de función o procedimiento normal, con algunos
especificadores adicionales, como se puede ver en el siguiente diagrama, que forma parte de la declaración de objeto:

métodos

­­ clase de definición de método encabezado de función ; directivas de método ­


encabezado de procedimiento
encabezado del constructor
encabezado del destructor

­­ directivas de método ­
virtual ; modificadores de llamadas ;

abstracto ;

Desde el punto de vista de las declaraciones, las definiciones de métodos son declaraciones de funciones o
procedimientos normales. A diferencia de TP y Delphi, los campos se pueden declarar después de los métodos
en el mismo bloque, es decir, lo siguiente generará un error al compilar con Delphi o Turbo Pascal, pero no con FPC:

Tipo
MiObj = Objeto
Campo: Entero largo;
Procedimiento Debe; fin;

5.6.2 Invocación de método


Los métodos se llaman del mismo modo que se llaman los procedimientos normales, sólo que tienen un
identificador de instancia de objeto antepuesto (ver también el capítulo 13, página 186). Para determinar qué
método se llama, es necesario conocer el tipo de método. Tratamos los diferentes tipos a continuación.

80
Machine Translated by Google

CAPÍTULO 5. OBJETOS

Métodos estáticos normales

Los métodos normales (estáticos) son métodos que se han declarado sin una palabra clave abstracta o
virtual. Al llamar a un método estático, se utiliza el método declarado (es decir, en tiempo de compilación)
del objeto. Por ejemplo, considere las siguientes declaraciones:

Tipo
TParent = Objeto
...
procedimiento Debe;
...
fin;
PParente = ^TParente;
TChild = Objeto(TParent)
...
procedimiento Debe;
...
fin;
PCniño = ^TNiño;

Como puede verse, tanto el objeto padre como el hijo tienen un método llamado Doit. Consideremos ahora las
siguientes declaraciones y llamamientos:

Era
ParentA,ParentB : PParent;
Niño : Pniño;

comenzar
ParentA := Nuevo(PParent,Init); PadreB :=
Nuevo(PChild,Init); Niño := Nuevo(PChild,Init);
PadreA^.Doit; PadreB^.Doit; Niño^.Hacerlo;

De las tres invocaciones de Doit, sólo la última llamará a [Link], las otras dos llamadas llamarán
a [Link]. Esto se debe a que, para los métodos estáticos, el compilador determina en el momento
de la compilación qué método se debe llamar. Dado que ParentB es de tipo TParent, el compilador
decide que se debe llamar con [Link], aunque se creará como TChild. Puede haber ocasiones
en las que el método que realmente se llama dependa del tipo real de objeto en tiempo de ejecución.
Si es así, el método no puede ser un método estático, sino que debe ser un método virtual.

Métodos virtuales

Para remediar la situación del apartado anterior, se crean métodos virtuales. Esto simplemente se hace
agregando a la declaración del método el modificador virtual. Luego, el objeto descendiente puede anular
el método con una nueva implementación volviendo a declarar el método (con la misma lista de
parámetros) usando la palabra clave virtual.
Volviendo al ejemplo anterior, considere la siguiente declaración alternativa:

Tipo
TParent = Objeto
...

81
Machine Translated by Google

CAPÍTULO 5. OBJETOS

procedimiento Deberá;virtual;
...
fin;
PParente = ^TParente;
TChild = Objeto(TParent)
...
procedimiento Deberá;virtual;
...
fin;
PCniño = ^TNiño;

Como puede verse, tanto el objeto padre como el hijo tienen un método llamado Doit. Consideremos ahora las
siguientes declaraciones y llamamientos:

Era
ParentA,ParentB : PParent;
Niño : Pniño;

comenzar
ParentA := Nuevo(PParent,Init); PadreB :=
Nuevo(PChild,Init); Niño := Nuevo(PChild,Init);
PadreA^.Doit; PadreB^.Doit; Niño^.Hacerlo;

Ahora, se llamarán diferentes métodos, dependiendo del tipo de tiempo de ejecución real del objeto.
Para ParentA, nada cambia, ya que se crea como una instancia de TParent. Para Child, la situación
tampoco cambia: se vuelve a crear como una instancia de TChild.
Sin embargo, para ParentB la situación cambia: aunque fue declarado como TParent, se crea
como una instancia de TChild. Ahora, cuando se ejecuta el programa, antes de llamar a Doit, el
programa comprueba cuál es el tipo real de ParentB y sólo entonces decide qué método se debe llamar.
Al ver que ParentB es del tipo TChild, se llamará a [Link]. El compilador inserta el código para esta
verificación en tiempo de ejecución del tipo real de un objeto en el momento de la compilación.
Se dice que [Link] anula [Link]. Es posible acceder al
[Link] desde [Link], con la palabra clave heredada:

Procedimiento [Link];

comenzar heredado Debe;


...
fin;

En el ejemplo anterior, cuando se llama a [Link], lo primero que hace es llamar a [Link].
La palabra clave heredada no se puede utilizar en métodos estáticos, sólo en métodos virtuales.

Para poder hacer esto, el compilador mantiene – por tipo de objeto – una tabla con métodos virtuales:
la VMT (Virtual Method Table). Esto es simplemente una tabla con punteros a cada uno de los métodos
virtuales: cada método virtual tiene su ubicación fija en esta tabla (un índice). El compilador usa esta
tabla para buscar el método real que debe usarse. Cuando un objeto descendiente anula un método, la
entrada del método principal se sobrescribe en el VMT. Puede encontrar más información sobre VMT
en la Guía del programador.
Como se comentó anteriormente, los objetos que tienen un VMT deben inicializarse con un
constructor: la variable del objeto debe inicializarse con un puntero al VMT del tipo real con el que se creó.

82
Machine Translated by Google

CAPÍTULO 5. OBJETOS

Métodos abstractos

Un método abstracto es un tipo especial de método virtual. Un método declarado abstracto no


tiene una implementación para este método. Depende de los objetos heredados anular e
implementar este método.
De esto se deduce que un método no puede ser abstracto si no es virtual (esto se puede ver en el diagrama
de sintaxis). Una segunda consecuencia es que no se puede crear directamente una instancia de un objeto
que tenga un método abstracto.
La razón es obvia: ¡no existe ningún método al que el compilador pueda acceder! Un método declarado
abstracto no tiene una implementación para este método. Depende de los objetos heredados anular e
implementar este método. Continuando con nuestro ejemplo, eche un vistazo a esto:

Tipo
TParent = Objeto
...
procedimiento Debe;virtual;abstracto;
...
fin;
PParente=^TParente;
TChild = Objeto(TParent)
...
procedimiento Deberá;virtual;
...
fin;
PCniño = ^TNiño;

Como puede verse, tanto el objeto padre como el hijo tienen un método llamado Doit. Consideremos ahora las
siguientes declaraciones y llamamientos:

Era
ParentA,ParentB : PParent;
Niño : Pniño;

comenzar
ParentA := Nuevo(PParent,Init); PadreB :=
Nuevo(PChild,Init); Niño := Nuevo(PChild,Init);
PadreA^.Doit; PadreB^.Doit; Niño^.Hacerlo;

En primer lugar, la línea 3 generará un error del compilador, indicando que no se pueden generar instancias
de objetos con métodos abstractos: El compilador ha detectado que PParent apunta a un objeto que tiene
un método abstracto. Comentar la línea 3 permitiría la compilación del programa.
Observación Si se anula un método abstracto, no se puede llamar al método principal con herencia, ya que
no existe un método principal; El compilador detectará esto y se quejará de esta manera:

[Link](32,3) Error: Los métodos abstractos no se pueden llamar directamente

Si, a través de algún mecanismo, se llama a un método abstracto en tiempo de ejecución, se producirá un error en tiempo de ejecución.
(error de tiempo de ejecución 211, para ser precisos)

83
Machine Translated by Google

CAPÍTULO 5. OBJETOS

Métodos de clase o estáticos.

Los métodos de clase o métodos declarados con la directiva estática son métodos globales para el tipo de objeto.
Cuando se llama, el puntero "auto" implícito no está disponible. Esto significa que no se pueden llamar métodos
normales y no se puede acceder a ninguno de los campos de un objeto. Sin embargo, se pueden utilizar variables de
clase.

Los métodos de clase o estáticos son métodos regulares y se pueden asignar a una variable de procedimiento.

El siguiente programa demuestra todo esto. Las declaraciones comentadas no se compilarán.

{$CONSOLA TIPO APLICACIÓN}


{$IFDEF FPC}{$MODE DELPHI}{$H+}{$ENDIF} tipo

TTest = constante del


objeto Epsylon = 100; varf: número
entero; clase var cv1, cv2:
entero; procedimiento myproc; procedimiento
de clase testproc;
procedimiento de clase testproc2;static;
procedimiento testproc3; estático; fin;

procedimiento [Link]; comenzar


cv1:=0;
f:=1; fin;

procedimiento de clase [Link]; iniciar cv1:=1; //


f:=1; fin;

procedimiento de clase TTest.Testproc2; iniciar cv1:=2; //


f:=1; fin;

procedimiento TTest.Testproc3; iniciar


cv1:=3; //
f:=1; fin;

Era
P: Procedimiento;

comenzar
P:=@[Link];
P:=@[Link];

84
Machine Translated by Google

CAPÍTULO 5. OBJETOS

P:=@TTest.Testproc2;
P:=@TTest.Testproc3; fin.

Descomentar una de las declaraciones comentadas e intentar compilar el código resultante generará un error de
compilación:

[Link](32,6) Error: solo los métodos de clase, las propiedades de clase y


Se puede acceder a las variables de clase en los métodos de clase.

5.7 Visibilidad
Para los objetos, existen tres especificadores de visibilidad: privado, protegido y público. Si no se especifica un
especificador de visibilidad, se asume público. Tanto los métodos como los campos se pueden ocultar a un programador
colocándolos en una sección privada. La regla de visibilidad exacta es la siguiente:

Privado Sólo se puede acceder a todos los campos y métodos que están en un bloque privado en el módulo (es decir,
unidad o programa) que contiene la definición del objeto. Se puede acceder a ellos desde dentro de los métodos
del objeto o desde fuera de ellos, por ejemplo, desde métodos de otros objetos o funciones globales.

Protegido Es lo mismo que Privado, excepto que los miembros de una sección Protegida también son accesibles para
los tipos descendientes, incluso si están implementados en otros módulos.

Los campos y métodos públicos siempre son accesibles, desde cualquier lugar. Los campos y métodos de una sección
pública se comportan como si fueran parte de un tipo de registro normal.

85
Machine Translated by Google

Capítulo 6

Clases

En el enfoque Delphi de la Programación Orientada a Objetos, todo gira en torno al concepto de “Clases”. Una
clase puede verse como un puntero a un objeto o un puntero a un registro, con métodos asociados a él.

La diferencia entre objetos y clases es principalmente que un objeto se asigna en la pila, como lo haría un registro
normal, y que las clases siempre se asignan en el montón. En el siguiente ejemplo:

Era
A: TAlgúnObjeto; // un objeto
B: TAlgunaClase; // una clase

La principal diferencia es que la variable A ocupará tanto espacio en la pila como el tamaño del objeto
(TSomeObject). La variable B, por otro lado, siempre tomará sólo el tamaño de un puntero en la pila. Los datos de
clase reales están en el montón.

De esto se desprende una segunda diferencia: una clase siempre debe inicializarse a través de su constructor,
mientras que para un objeto esto no es necesario. Llamar al constructor asigna la memoria necesaria en el
montón para los datos de la instancia de clase.

Observación En versiones anteriores de Free Pascal era necesario, para poder usar clases, poner la unidad objpas en la
cláusula de usos de una unidad o programa. Esto ya no es necesario a partir de la versión 0.99.12. A partir de
esta versión, la unidad se cargará automáticamente cuando se especifiquen las opciones ­MObjfpc o ­MDelphi,
o se utilicen sus correspondientes directivas:

{$modo objfpc} {$modo


delphi}

De hecho, el compilador dará una advertencia si encuentra la unidad objpas en una cláusula de usos.

6.1 Definiciones de clase

La declaración prototipo de una clase es la siguiente:

tipos de clases

­­ tipo de clase definición directa de clase ­


definición de clase

86
Machine Translated by Google

CAPÍTULO 6. CLASES

­­ clase de definición directa de clase ­

­­ definición de clase clase ­


lleno resumen herencia
sellado
­ ­
Fin de la lista de 6 componentes

­­herencia ( identificador de tipo de clase ) ­


interfaces implementadas

­­ interfaces implementadas 6,
identificador de interfaz ­

­­ lista de componentes ­
especificador de visibilidad 6 definición de campo

­ ­
6 declaración constante
declaración de tipo de parte
declaración de variable de parte
declaración de variable de clase de parte
definición de método
de parte definición de propiedad

­­ parte de declaración de variable de clase parte de declaración de variable de clase ­

­ lista de identi cadores de definición de campo : tipo ; ­


estático;

­­ definición del método encabezado de función ;­


clase encabezado de
procedimiento encabezado
de constructor encabezado de desctector

­ ­
dinámica ; modificadores de llamadas ;

virtual; anulación abstracta

mensaje entero constante cadena


constante

­­ especi cador de visibilidad de clase privado ­


estricto protegido
público
publicado

Observación En el modo MacPas, la palabra clave Object se reemplaza por la palabra clave class para compatibilidad con
otros compiladores Pascal disponibles en Mac. Eso significa que en el modo MacPas, la palabra reservada
"clase" en el diagrama anterior puede ser reemplazada por la palabra reservada "objeto".

En una declaración de clase, se pueden utilizar tantos bloques de visibilidad como sea necesario: los distintos
bloques se pueden repetir y no hay un orden especial en el que deban aparecer.

Los métodos son declaraciones de funciones o procedimientos normales. Como puede verse, la declaración de
una clase es casi idéntica a la declaración de un objeto. La verdadera diferencia entre objetos y clases está en
la forma en que se crean (ver más adelante en este capítulo).

La visibilidad de las diferentes secciones es la siguiente:

87
Machine Translated by Google

CAPÍTULO 6. CLASES

Privado A todos los campos y métodos que están en un bloque privado, solo se puede acceder en el módulo (es
decir, unidad) que contiene la definición de clase. Se puede acceder a ellos desde dentro de los métodos de
las clases o desde fuera de ellos (por ejemplo, desde los métodos de otras clases).

Privado estricto Solo se puede acceder a todos los campos y métodos que se encuentran en un bloque privado estricto
desde los métodos de la propia clase. Otras clases o clases descendientes (incluso en la misma unidad) no
pueden acceder a miembros privados estrictos.

Protegido Es lo mismo que Privado, excepto que los miembros de una sección Protegida también son accesibles para
los tipos descendientes, incluso si están implementados en otros módulos.

Estricto Protegido Es lo mismo que Protegido, excepto que los miembros de una sección Protegida también son
accesibles para otras clases implementadas en la misma unidad. Los miembros estrictamente protegidos solo
son visibles para las clases descendientes, no para otras clases en la misma unidad.

Las secciones públicas siempre son accesibles.

Publicado Desde una perspectiva del lenguaje, esto es lo mismo que una sección Pública, pero el compilador también
genera información de tipo necesaria para la transmisión automática de estas clases si el compilador está en el
estado {$M+}. Los campos definidos en una sección publicada deben ser de tipo clase.
Las propiedades de la matriz no pueden estar en una sección publicada.

En el diagrama de sintaxis se puede ver que una clase puede enumerar las interfaces implementadas. Esta característica
se discutirá en el próximo capítulo.

Las clases pueden contener métodos de clase: son funciones que no requieren una instancia. El autoidentificador es
válido en dichos métodos, pero se refiere al puntero de clase (el VMT).

Observación Al igual que con las funciones y los tipos de punteros, a veces se necesita una definición directa de una clase. Una
definición directa de clase es simplemente el nombre de la clase, con la palabra clave Clase, como en el siguiente
ejemplo:

Tipo
TClassB = Clase;
TClassA = Clase
B: Clase TB; fin;

TClassB = Clase
A: Clase TA; fin;

Cuando se utiliza una definición directa de clase, la clase debe definirse en la misma unidad, en la misma sección
(interfaz/implementación). No necesariamente debe estar definido en la misma sección de tipo.

También es posible definir tipos de referencia de clase:

Tipo de referencia de clase

­­ clase de tipo de clase ­

Los tipos de referencia de clases se utilizan para crear instancias de una determinada clase, que aún no se conoce en el
momento de la compilación, pero que se especifica en el tiempo de ejecución. Básicamente, una variable de un tipo de
referencia de clase contiene un puntero a la definición de la clase especificada. Esto se puede utilizar para construir una
instancia de la clase correspondiente a la definición o para comprobar la herencia. El siguiente ejemplo muestra cómo funciona:

88
Machine Translated by Google

CAPÍTULO 6. CLASES

Tipo
TComponentClass = Clase de TComponent;

Función CreateComponent(AClass: TComponentClass;


APropietario: TComponent): TComponent;

comenzar // ...
Resultado:=[Link](AOwner); // ... fin;

A esta función se le puede pasar una referencia de clase de cualquier clase que descienda de TComponent. La
siguiente es una llamada válida:

Era
C: Componente T;

comenzar
C:=CrearComponente(TEdit,Form1); fin;

Al regresar la función CreateComponent, C contendrá una instancia de la clase TEdit. Tenga en cuenta que la siguiente
llamada no se podrá compilar:

Era
C: Componente T;

comenzar
C:=CrearComponente(TStream,Form1); fin;

porque TStream no desciende de TComponent y AClass se refiere a una clase de TComponent. El compilador puede
(y lo hará) verificar esto en el momento de la compilación y producirá un error.
Las referencias a clases también se pueden utilizar para comprobar la herencia:

TMinClass = Clase de TMyClass;


TMaxClass = Clase de TMyClassChild;

Función CheckObjectBetween (Instancia: TObject): booleano;

comenzar
Si no (la instancia es TMinClass) o ((la instancia es
TMaxClass)
y ([Link]<>TMaxClass)) luego levanta
[Link](SomeError) end;

El ejemplo anterior generará una excepción si la instancia pasada no es descendiente de TMinClass o descendiente
de TMaxClass.

Puede encontrar más información sobre la creación de instancias de una clase en la sección 6.4, página 93.

89
Machine Translated by Google

CAPÍTULO 6. CLASES

6.2 Clases abstractas y selladas

Una clase puede declararse sellada. En ese caso, no es posible declarar una clase descendiente. El compilador
devolverá un error si encuentra una declaración de un descendiente:

{$modo objfpc} {$h+}

Tipo
TMyClass = Clase sellada
x : número entero;
fin;

TMiClase2 = Clase(TMiClase)
Y: Entero; fin;

empezar
fin.

Esto dará como resultado el siguiente error:

Error: No se puede crear un descendiente de la clase sellada "TMyClass"

Una clase abstracta es una clase de la que no se puede crear una instancia directamente. En cambio, siempre se debe crear
una instancia de una clase descendiente. Sin embargo, por compatibilidad con Delphi, el compilador ignora esta directiva.

6.3 Campos normales y estáticos

. Las clases pueden tener campos. Dependiendo de cómo estén definidos, los campos contienen datos específicos de una
instancia de una clase o de la clase en su conjunto. Cualquiera que sea la forma en que fueron definidos, los campos observan
las reglas de visibilidad como cualquier otro miembro de la clase.

6.3.1 Campos/variables normales

Hay dos formas de declarar un campo normal. La primera es la forma clásica, similar a una definición en un objeto:

{$modo objfpc} tipo


cl=clase

l : entero largo; fin


fin;
era
cl1,cl2 : cl; comenzar

cl1:=[Link]; cl2:=[Link];
cl1.l:=2; writeln(cl1.l);
writeln(cl2.l);
fin.

90
Machine Translated by Google

CAPÍTULO 6. CLASES

será el siguiente

El ejemplo demuestra que los valores de los campos se inicializan con cero (o el equivalente a cero para tipos no
ordinales: cadena vacía, matriz vacía, etc.).

La segunda forma de declarar un campo (solo disponible en versiones más recientes de Free Pascal) es usando
un bloque var:

{$modo objfpc} tipo


cl=clase

era
l : entero largo; fin
fin;

Esta definición es completamente equivalente a la definición anterior.

Observación A partir de la versión 3.0 del compilador, el compilador puede reordenar los campos en la memoria si esto
conduce a una mejor alineación e instancias más pequeñas. Eso significa que en una instancia, los campos no
necesariamente aparecen en el mismo orden que en la declaración. El RTTI generado para una clase reflejará este cambio.

6.3.2 Campos/variables de clase


De manera similar a los objetos, una clase puede contener campos estáticos o variables de clase: estos campos o variables
son globales para la clase y actúan como variables globales, pero se conocen sólo como parte de la clase. Se puede hacer
referencia a ellos desde dentro de los métodos de las clases, pero también se puede hacer referencia a ellos desde fuera de
la clase proporcionando el nombre completo.

Nuevamente, hay dos formas de definir variables de clase. El primero es equivalente a la forma en que se hace en los
objetos, usando un modificador estático:

Por ejemplo, la salida del siguiente programa es la misma que la salida de la versión que usa un objeto:

{$modo objfpc} tipo


cl=clase

l : entero largo; estático; fin;

era
cl1,cl2 : cl; comenzar

cl1:=[Link]; cl2:=[Link];
cl1.l:=2; writeln(cl2.l);
cl2.l:=3;
writeln(cl1.l); Writeln(cl.l);

fin.

El resultado de esto será el siguiente:

91
Machine Translated by Google

CAPÍTULO 6. CLASES

Tenga en cuenta que la última línea de código hace referencia al tipo de clase en sí (cl) y no a una instancia de la
clase (cl1 o cl2).

Además del enfoque de campo estático, en las clases se puede utilizar una Class Var. De manera similar a la forma en que se
puede definir un campo en un bloque de variables, se puede declarar una variable de clase en un bloque de clase var:

{$modo objfpc} tipo

cl=clase
la clase era
l : entero largo; fin
fin;

Esta definición es equivalente a la anterior.

Tenga en cuenta que una variable de clase está vinculada a una clase específica. Las clases descendientes se referirán a la
misma instancia, a menos que se vuelva a declarar la variable. El siguiente programa lo demuestra:

{$modo objfpc} tipo

TA = clase // tipo base


clase var CÓDIGO: entero; fin;

TB = clase(TA);
TC = clase(TA);

comenzar
código TA:=0;
Có[Link]:=1;
Có[Link]:=2;
Writeln(Ta.Código:2,Tb.Código:2,Tc.código:2); fin.

El resultado de este programa es:

222

Debido a que está vinculado a una clase, se puede anular en modo Delphi:

$modo delphi} tipo

TA = clase // tipo base


clase var CÓDIGO: entero; fin;

TB = clase(TA)
Código var de clase: entero; fin;

TC = clase(TA)
Código var de clase: entero; fin;

92
Machine Translated by Google

CAPÍTULO 6. CLASES

comenzar
código TA:=0;
Có[Link]:=1;
Có[Link]:=2;
Writeln(Ta.Código:2,Tb.Código:2,Tc.código:2); fin.

Imprimirá lo siguiente:

012

Sin embargo, en modo OBJFPC no se compilará y dará un error de identificador duplicado.

6.4 Creación de instancias de clase

Las clases deben crearse utilizando uno de sus constructores (puede haber varios constructores). Recuerde que una
clase es un puntero a un objeto en el montón. Cuando se declara una variable de alguna clase, el compilador
simplemente asigna espacio para este puntero, no para todo el objeto. El constructor de una clase devuelve un puntero
a una instancia inicializada del objeto en el montón. Entonces, para inicializar una instancia de alguna clase, se haría lo
siguiente:

ClassVar := [Link];

La sintaxis extendida de new y dispose no se puede utilizar para crear instancias y destruir instancias de clase: esa
construcción está reservada para su uso únicamente con objetos. Llamar al constructor provocará una llamada al
método de clase virtual NewInstance, que, en su implementación predeterminada, llama a GetMem para asignar
suficiente espacio para contener los datos de la instancia de clase y luego pone a cero la memoria.

Después de eso, se ejecuta el código del constructor. El constructor tiene un puntero a sus datos, en Self.
Observación

• La directiva {$PackRecords } también afecta a las clases, es decir, a la alineación en memoria del
Los diferentes campos dependen del valor de la directiva {$PackRecords}.

• Al igual que con los objetos y registros, se puede declarar una clase empaquetada. Esto tiene el mismo efecto
que en un objeto o registro, es decir, que los elementos están alineados en límites de 1 byte, es decir, lo más
cerca posible.

• SizeOf(clase) devolverá lo mismo que SizeOf(Pointer), ya que una clase es un puntero a un objeto. Para obtener
el tamaño de los datos de la instancia de clase, utilice el método [Link].

• Si ocurre una excepción durante la ejecución de un constructor, se llamará al destructor.


automáticamente.

6.5 Destrucción de clase

Las instancias de clase deben destruirse utilizando el destructor. A diferencia del constructor, no hay elección en los
destructores: el destructor debe tener el nombre Destroy, debe anular el destructor Destroy declarado en TObject, no
puede tener argumentos y siempre se debe llamar al destructor heredado.

Destroy llamará a FreeInstance, que, en su implementación predeterminada, llama a FreeMem para liberar la memoria
ocupada por la instancia.

93
Machine Translated by Google

CAPÍTULO 6. CLASES

Para evitar llamar a un destructor en una instancia Nil, es mejor llamar al método Free de TObject.
Este método comprobará si Self no es Nil y, de ser así, llama a Destroy. Si Self es igual a Nil, simplemente saldrá.

Destruir una instancia no elimina ni anula una referencia a una instancia:

Era
A: Componente T;

comenzar
A:=[Link];
[Link]:='MiComponente'; [Link];
Writeln('A
todavía está asignado: ',Asignado(A)); fin.

Luego de la llamada a Free, la variable A no será Nil, la salida de este programa será:

A todavía está asignada: VERDADERO

Para asegurarse de que la variable A se borre después de llamar al destructor, se puede utilizar la función FreeAndNil
de la unidad SysUtils. Llamará a Free y luego escribirá Nil en el puntero del objeto (A en el ejemplo anterior):

Era
A: Componente T;

comenzar
A:=[Link];
[Link]:='MiComponente'; Libre y nulo
(A); Writeln('A todavía
está asignado: ',Asignado(A)); fin.

Después de la llamada a FreeAndNil, la variable A contendrá Nil, la salida de este programa será:

A todavía está asignada: FALSO

Tenga en cuenta que si ocurre una excepción durante la ejecución de un constructor, el destructor se llamará automático.
matemáticamente.

6.6 Métodos

6.6.1 Declaración
La declaración de métodos en clases sigue las mismas reglas que las declaraciones de métodos en objetos:

métodos

­­ definición del método encabezado de función ; directivas de método ­


encabezado de procedimiento
encabezado del constructor
encabezado del destructor

94
Machine Translated by Google

CAPÍTULO 6. CLASES

­­ directivas de método ­
dinámica modificadores de llamadas ;

virtual; resumen reintroducir ;


anular; mensaje
expresión
constante

Las únicas diferencias son las directivas de anulación, reintroducción y mensaje.

6.6.2 Invocación
La invocación de métodos para clases no es diferente a la de objetos. La siguiente es una invocación de
método válida:

Var AnObject: TAnObject; comenzar

AnObject := [Link]; [Link]étodo;

6.6.3 Métodos virtuales


Las clases tienen métodos virtuales, al igual que los objetos. Sin embargo, existe una diferencia entre los dos.
Para los objetos, basta con volver a declarar el mismo método en un objeto descendiente con la palabra clave virtual
para anularlo. Para las clases, la situación es diferente: los métodos virtuales deben anularse con la palabra clave
override. De no hacerlo, se iniciará un nuevo lote de métodos virtuales, ocultando el anterior. La palabra clave heredada
no saltará al método heredado si se utilizó Virtual.

El siguiente código es incorrecto:

Tipo
ObjParent = Clase
Procedimiento MyProc; virtual; fin;

ObjChild = Clase(ObjPArent)
Procedimiento MyProc; virtual; fin;

El compilador generará una advertencia:

Advertencia: [Link] oculta un método heredado

El compilador lo compilará, pero usar Heredado puede producir efectos extraños.


La declaración correcta es la siguiente:

Tipo
ObjParent = Clase
Procedimiento MyProc; virtual; fin;

ObjChild = Clase(ObjPArent)
Procedimiento MyProc; anular; fin;

95
Machine Translated by Google

CAPÍTULO 6. CLASES

Esto se compilará y ejecutará sin advertencias ni errores.


Si el método virtual realmente debe reemplazarse con un método con el mismo nombre, entonces se puede usar la
palabra clave reintroduce:

Tipo
ObjParent = Clase
Procedimiento MyProc; virtual; fin;

ObjChild = Clase(ObjPArent)
Procedimiento MyProc; reintroducir; fin;

Este nuevo método ya no es virtual.

Para poder hacer esto, el compilador mantiene – por tipo de clase – una tabla con métodos virtuales: la
VMT (Virtual Method Table). Esto es simplemente una tabla con punteros a cada uno de los métodos
virtuales: cada método virtual tiene su ubicación fija en esta tabla (un índice). El compilador usa esta tabla
para buscar el método real que debe usarse en tiempo de ejecución. Cuando un objeto descendiente
anula un método, la entrada del método principal se sobrescribe en el VMT. Puede encontrar más
información sobre VMT en la Guía del programador.
Observación La palabra clave "virtual" se puede reemplazar con la palabra clave "dinámica": los métodos dinámicos se
comportan igual que los métodos virtuales. A diferencia de Delphi, en FPC la implementación de métodos
dinámicos es igual a la implementación de métodos virtuales.

6.6.4 Métodos de clase


Los métodos de clase se identifican mediante la palabra clave Clase delante del procedimiento o declaración de
función, como en el siguiente ejemplo:

Función de clase Nombre de clase: Cadena;

Los métodos de clase son métodos que no tienen una instancia (es decir, Self no apunta a una instancia de clase)
pero que siguen las reglas de alcance y herencia de una clase. Se pueden utilizar para devolver información sobre
la clase actual, por ejemplo para registro o uso en una fábrica de clases. Como no hay ninguna instancia disponible,
no se puede utilizar ninguna información disponible en las instancias.

Los métodos de clase se pueden llamar desde dentro de un método normal, pero también se pueden llamar usando un
identificador de clase:

Era
Clase A: Clase T; // AClass es de tipo "tipo de clase"

comenzar
..
si CompareText([Link],'TCOMPONENT')=0 entonces
...

Pero también es posible llamarlos desde una instancia:

Era
MiClase: TObject;

comenzar

96
Machine Translated by Google

CAPÍTULO 6. CLASES

..
si [Link]('TCOMPONENT') entonces
...

Lo contrario no es posible: dentro de un método de clase, el identificador Self apunta a la tabla VMT de la
clase. No hay campos, propiedades o métodos regulares disponibles dentro de un método de clase. Acceder
a una propiedad o método normal provocará un error del compilador.
Tenga en cuenta que los métodos de clase pueden ser virtuales y pueden anularse.

Los métodos de clase se pueden utilizar como especificadores de lectura o escritura para una propiedad normal, pero, naturalmente,
esta propiedad tendrá el mismo valor para todas las instancias de la clase, ya que no hay ninguna instancia disponible en el método
de clase.

6.6.5 Class constructores and destructores


También se puede crear un constructor o destructor de clase. Sirven para crear instancias de algunas variables de
clase o propiedades de clase que deben inicializarse antes de poder utilizar una clase. Estos constructores se llaman
automáticamente al inicio del programa: se llama al constructor antes de la sección de inicialización de la unidad en
la que se declara, y al destructor se llama después de la sección de finalización de la unidad en la que se declara.

Hay algunas advertencias al utilizar destructores/constructores de clases:

• Sólo puede haber un constructor por clase. El nombre es arbitrario, pero no puede tener parámetros.
éteres.

• Sólo puede haber un destructor por clase. El nombre es arbitrario, pero no puede tener parámetro.
ters.

• Ni el constructor ni el destructor pueden ser virtuales.

• El constructor/destructor de la clase se llama independientemente del uso de la clase: incluso si una clase
nunca se usa, el constructor y el destructor se llaman de todos modos.

• No hay un orden garantizado en el que se llaman los constructores o destructores de clases. Para
las clases anidadas, el único orden garantizado es que los constructores de las clases anidadas se
llamen después de llamar al constructor de la clase circundante, y para los destructores se utiliza
el orden opuesto.

El siguiente programa:

{$modo objfpc} {$h+}

Tipo
TA = Clase (TOobjeto)
Privado
Función GetA: Entero;
Procedure SetA(AValue : integer);

público
Crear constructor de clase;
Destructor de clase destruir;
Propiedad A: Entero Leer GetA Escribir SetA; fin;

{Clase} Función [Link]: Entero;

97
Machine Translated by Google

CAPÍTULO 6. CLASES

comenzar
Resultado:=­1; fin;

{Class} Procedure [Link](AValue : integer);

comenzar //

finalizar;

Constructor de clase [Link];

comenzar
Writeln('Constructor de clase TA'); fin;

Destructor de clase [Link];

comenzar
Writeln('Destructor de clase TA');

fin;

Era
R: TA;

empezar
fin.

Cuando se ejecute, generará lo siguiente:

Constructor de clases TA
Destructor de clase TA

6.6.6 Métodos de clase estática


FPC conoce métodos de clase estáticos en clases: estos son métodos de clase que tienen la palabra clave Static
al final. Estos métodos se comportan completamente como procedimientos o funciones normales. Esto significa que:

• No tienen parámetro Self. Como resultado, no pueden acceder a propiedades o campos o


métodos regulares.

• No pueden ser virtuales.

• Pueden asignarse a variables procesales regulares.

Su uso es principalmente incluir el método en el espacio de nombres de la clase en lugar de tener el procedimiento
en el espacio de nombres de la unidad. Tenga en cuenta que tienen acceso a todas las variables de clase, tipos,
etc., lo que significa que es posible algo como esto:

{$modo objfpc} {$h+}

98
Machine Translated by Google

CAPÍTULO 6. CLASES

Tipo
TA = Clase (TOobjeto)
Clase
privada var myprivatea: entero; Función de clase
pública
GetA: Entero; estático; clase Procedimiento SetA (AValue: Entero);
estático; fin;

Función de clase [Link]: Entero;

comenzar
Resultado:=miprivadoA; fin;

Class Procedure [Link](AValue : integer);

begin
myprivateA:=AValue; end;

comenzar
[Link](123);
Writeln([Link]); fin.

Lo que generará 123, cuando se ejecute.

En la implementación de un método de clase estática, el identificador propio no está disponible. El método


se comporta como si Self estuviera codificado en la clase declarada, no en la clase real con la que se llamó.
En los métodos de clase normales, Self contiene la clase Actual para la que se llamó al método. El siguiente ejemplo
deja esto claro:

Tipo
TA = Clase
Procedimiento de clase DoIt; virtual;
Procedimiento de clase DoitStatic; estático; fin;

TB = Clase(TA)
Procedimiento de clase DoIt; anular; fin;

Procedimiento de clase [Link];

comenzar
Writeln('[Link]: ',[Link]); fin;

Procedimiento de clase [Link];

comenzar
Debe;

99
Machine Translated by Google

CAPÍTULO 6. CLASES

Writeln('[Link]: ',NombreDeClase); fin;

Procedimiento de clase [Link];

comenzar
Heredado;
Writeln('[Link]: ',[Link]); fin;

comenzar
Writeln('A través del método estático:'); [Link];
Writeln('A través del
método de clase:'); [Link];

fin.

Cuando se ejecute, este ejemplo imprimirá:

Mediante método estático: [Link]: TA

[Link]: TA
A través del método de clase:
[Link]: TB
[Link]: TB

Para el método de clase estática, aunque fue llamado usando TB, la clase (Self, si estuviera disponible) se
establece en TA, la clase en la que se definió el método estático. Para el método de clase, la clase se establece
en la clase real utilizada para llamar al método (TB).

6.6.7 Métodos de mensajes Una novedad

en las clases son los métodos de mensajes. Los punteros a los métodos de mensajes se almacenan en una tabla
especial, junto con el número entero o la constante de cadena con la que fueron declarados. Su objetivo principal es
facilitar la programación de funciones de devolución de llamada en varios kits de herramientas GUI, como Win32 o
GTK. A diferencia de Delphi, Free Pascal también acepta cadenas como identificadores de mensajes. Los métodos de
mensaje son siempre virtuales.

Como se puede ver en el diagrama de declaración de clases, los métodos de mensaje se declaran con una palabra
clave Mensaje, seguida de una expresión constante entera.

Además, solo pueden aceptar un argumento var (escrito o no):

Procedimiento [Link](Var Msg); Mensaje 1;

La implementación del método de una función de mensaje no es diferente de un método ordinario. También es
posible llamar directamente a un método de mensaje, pero esto no debe hacerse. En su lugar, se debe utilizar el
método [Link]. Los métodos de mensajes son automáticamente virtuales, es decir, pueden anularse
en clases descendientes.

El método [Link] se puede utilizar para llamar a un controlador de mensajes. Se declara en la unidad del
sistema y aceptará un parámetro var que debe tener en la primera posición un cardinal con el ID del mensaje que se
debe llamar. Por ejemplo:

Tipo

100
Machine Translated by Google

CAPÍTULO 6. CLASES

TMsg = Grabar
MSGID: Cardenal;
Datos: puntero;
Era
Mensaje: TMSg;

[Link] (mensaje);

En este ejemplo, el método Dispatch observará el objeto y todos sus antepasados (comenzando en el objeto y buscando
en el árbol de clases de herencia) para ver si se ha declarado un método de mensaje con el mensaje MSGID. Si se
encuentra dicho método, se llama y se le pasa el parámetro Msg.

Si no se encuentra dicho método, se llama a DefaultHandler. DefaultHandler es un método virtual de TObject que no
hace nada, pero que puede anularse para proporcionar cualquier procesamiento que pueda ser necesario. DefaultHandler
se declara de la siguiente manera:

procedimiento DefaultHandler (mensaje var); virtual;

Además del método de mensaje con un identificador entero, Free Pascal también admite un método de
mensaje con un identificador de cadena:

Procedimiento [Link](Var Msg); Mensaje 'Al hacer clic';

El funcionamiento del controlador de mensajes de cadena es el mismo que el del controlador de mensajes
enteros ordinario: el método [Link] se puede utilizar para llamar a un controlador de mensajes.
Se declara en la unidad del sistema y aceptará un parámetro que debe tener en la primera posición una cadena
corta con el ID del mensaje que se debe llamar. Por ejemplo:

Tipo
TMsg = Grabar
MsgStr: Cadena[10]; // Longitud arbitraria hasta 255 caracteres.
Datos: puntero;
Era
Mensaje: TMSg;

[Link] (mensaje);

En este ejemplo, el método DispatchStr observará el objeto y todos sus antepasados (comenzando en el objeto y
buscando en el árbol de clases de herencia) para ver si se ha declarado un método de mensaje con el mensaje MsgStr.
Si se encuentra dicho método, se llama y se le pasa el parámetro Msg.

Si no se encuentra dicho método, se llama a DefaultHandlerStr. DefaultHandlerStr es un método virtual de TObject que
no hace nada, pero que puede anularse para proporcionar cualquier procesamiento que pueda ser necesario.
DefaultHandlerStr se declara de la siguiente manera:

procedimiento DefaultHandlerStr(mensaje var);virtual;

6.6.8 Uso de heredado En un método

virtual anulado, a menudo es necesario llamar a la implementación del método virtual de la clase principal. Esto se
puede hacer con la palabra clave heredada. Asimismo, la palabra clave heredada se puede utilizar para llamar a
cualquier método de la clase principal.

El primer caso es el más sencillo:

101
Machine Translated by Google

CAPÍTULO 6. CLASES

Tipo
TMiClase = Clase(TComponente)
Constructor Crear(AOwner: TComponent); anular; fin;

Constructor [Link](AOwner: TComponent);

comenzar
Heredado;
// Hacer que terminen más
cosas;

En el ejemplo anterior, la declaración Inherited llamará a Create de TComponent y le pasará AOwner como parámetro:
los mismos parámetros que se pasaron al método actual se pasarán al método principal. No se deben volver a
especificar: si no se especifica ninguno, el compilador pasará los mismos argumentos que los recibidos.

Si no existe ningún método heredado con el mismo nombre, el Heredado no tendrá ningún efecto en este caso.
Por lo tanto, la presencia de Heredado en este formulario puede interpretarse como "llamar al método anulado
si existe".

El segundo caso es un poco más complicado:

Tipo
TMiClase = Clase(TComponente)
Constructor Crear(AOwner: TComponent); anular;
Constructor CreateNew(AOwner: TComponent; DoExtra: Booleano); fin;

Constructor [Link](AOwner: TComponent); comenzar Heredado; fin;

Constructor [Link](AOwner: TComponent; DoExtra: Booleano); comenzar creación heredada (AOwner); //


Hacer
cosas

fin;

El método CreateNew primero llamará a [Link] y le pasará AOwner como parámetro. No llamará a
[Link].

Si no existe ningún método con el nombre de pila en las clases principales, el compilador dará un error.

Aunque los ejemplos se dieron usando constructores, el uso de heredado no se limita a los constructores, también se
puede usar para cualquier procedimiento, función o destructor.

6.7 Propiedades

6.7.1 Definición

Las clases pueden contener propiedades como parte de su lista de campos. Una propiedad actúa como un
campo normal, es decir, su valor se puede recuperar o establecer, pero permite redirigir el acceso al campo a
través de funciones y procedimientos. Proporcionan un medio para asociar una acción con una asignación o
una lectura de un "campo" de clase. Esto permite, por ejemplo, comprobar que un valor es válido al asignarlo o, al leerlo,

102
Machine Translated by Google

CAPÍTULO 6. CLASES

permite construir el valor sobre la marcha. Además, las propiedades pueden ser de sólo lectura o de sólo escritura.
La declaración prototipo de una propiedad es la siguiente:

Propiedades

­­ clase de definición de propiedad identi cador de propiedad ­


interfaz de propiedad
­ directiva de sugerencia de especificadores de propiedad
­

­­ interfaz de propiedad : tipo identi cador ­


lista de parámetros de propiedad
­ ­
índice entero constante

­­ lista de parámetros de propiedad [ Declaración de 6 parámetros ] ­


;

­­ especi caciones de propiedad ­


leer especificación escribir especí er
implementos especí er
­ ­
especificador predeterminado especificación almacenada especificación de propiedad de matriz predeterminada

­­ leer un campo o método de lectura específico ­

­­ escribir un campo o método de escritura específico ­

­­ implementos específicos implementos 6 identi r ­


,

­­ especificador predeterminado predeterminado ­


constante
ningún valor predeterminado

­­ especificador almacenado identificador constante ­


almacenado

­­ campo o método identificador de ­


campo identificador de método

­­ defaultarraypropertyspeci er; por defecto ­

Un especificador de lectura es el nombre de un campo que contiene la propiedad o el nombre de una función de
método que tiene el mismo tipo de retorno que el tipo de propiedad. En el caso de un tipo simple, esta función
no debe aceptar ningún argumento. En el caso de una propiedad de matriz, la función debe aceptar un único
argumento del mismo tipo que el índice. En el caso de una propiedad indexada, debe aceptar un número entero
como argumento.

Un especificador de lectura es opcional, lo que hace que la propiedad sea de solo escritura. Tenga en cuenta que los métodos de clase no se pueden

utilizar como especificadores de lectura.

Un especificador de escritura es opcional: si no hay un especificador de escritura, la propiedad es de solo lectura.


Un especificador de escritura es el nombre de un campo o el nombre de un procedimiento de método que acepta
como único argumento una variable del mismo tipo que la propiedad. En el caso de una propiedad de matriz, el
procedimiento debe aceptar dos argumentos: el primer argumento debe tener el mismo tipo que el índice, el segundo

103
Machine Translated by Google

CAPÍTULO 6. CLASES

El argumento debe ser del mismo tipo que la propiedad. De manera similar, en el caso de una propiedad indexada, el primer
parámetro debe ser un número entero.

La sección (privada, publicada) en la que reside la función o procedimiento especificado es irrelevante. Sin embargo,
normalmente este será un método protegido o privado.

Por ejemplo, dada la siguiente declaración:

Tipo
MiClase = Clase
Privado
Campo1: Entero largo;
Campo2: Entero largo;
Campo3: Entero largo;
Procedimiento Sety (valor: Entero largo);
Función Gety: Entero largo;
Función Getz: Entero largo;
Público
Propiedad X: Entero largo Leer campo1 escribir campo2;
Propiedad Y: Entero largo Leer GetY Escribir Sety;
Propiedad Z: Entero largo Leer GetZ; fin;

Era
MiClase: TMiClase;

Las siguientes son declaraciones válidas:

WriteLn ('X: ',MiClase.X); WriteLn ('Y :


',MiClase.Y); WriteLn ('Z : ',MiClase.Z);
MiClase.X := 0; MiClase.Y := 0;

Pero lo siguiente generaría un error:

MiClase.Z := 0;

porque Z es una propiedad de sólo lectura.

Lo que sucede en las declaraciones anteriores es que cuando es necesario leer un valor, el compilador inserta
una llamada a los distintos métodos getNNN del objeto y se utiliza el resultado de esta llamada. Cuando se
realiza una asignación, el compilador pasa el valor que debe asignarse como parámetro a los distintos
métodos setNNN.

Debido a este mecanismo, las propiedades no se pueden pasar como argumentos var a una función o
procedimiento, ya que no se conoce la dirección de la propiedad (al menos no siempre).

6.7.2 Propiedades indexadas Si la definición

de propiedad contiene un índice, entonces los especificadores de lectura y escritura deben ser una función y un
procedimiento. Además, estas funciones requieren un parámetro adicional: un parámetro entero. Esto permite leer o
escribir varias propiedades con la misma función. Para ello las propiedades deben ser del mismo tipo. El siguiente es
un ejemplo de una propiedad con un índice:

{$modo objfpc}

104
Machine Translated by Google

CAPÍTULO 6. CLASES

Tipo
PuntoT = Clase(TOobjeto)
Privado
FX,FY: Entero largo;
Función GetCoord (Índice: Entero): Entero largo;
Procedimiento SetCoord (Índice: Entero; Valor: entero largo);
Público
Propiedad X: índice de entero largo 1 leer GetCoord escribir SetCoord;
Propiedad Y: Índice de entero largo 2 leer GetCoord Escribir SetCoord;
Coords de propiedad[Índice: entero]:Entero largo Leer GetCoord; fin;

Procedimiento [Link] (Índice: Entero; Valor: Entero largo); comenzar índice de casos de

1: FX:= Valor;
2: Año fiscal: = Valor; fin; fin;

Función [Link] (ÍNDICE: Entero): Entero largo; comenzar

Índice de casos de
1: Resultado:= FX;
2: Resultado: = ejercicio
fiscal;
fin; fin;

Era
P: PuntoT;

comenzar
P := [Link]; PX := 2; PA :=

Con P hacer
WriteLn ('X=',X,' Y=',Y);
fin.

Cuando el compilador encuentra una asignación a X, se llama a SetCoord con como primer parámetro
el índice (1 en el caso anterior) y como segundo parámetro el valor a establecer. Por el contrario, al
leer el valor de X, el compilador llama a GetCoord y le pasa el índice 1. Los índices solo pueden ser
valores enteros.

6.7.3 Propiedades de matriz También

existen propiedades de matriz. Estas son propiedades que aceptan un índice, tal como lo hace una matriz. El
índice puede ser unidimensional o multidimensional. A diferencia de los arreglos normales (estáticos o dinámicos),
el índice de una propiedad de un arreglo no tiene que ser de tipo ordinal, pero puede ser de cualquier tipo.

Un especificador de lectura para una propiedad de matriz es la función del método de nombre que tiene el mismo tipo de
retorno que el tipo de propiedad. La función debe aceptar como único argumento una variable del mismo tipo que el tipo
de índice. Para una propiedad de matriz, no se pueden especificar campos como especificadores de lectura.

Un especificador de escritura para una propiedad de matriz es el nombre de un procedimiento de método que acepta dos

105
Machine Translated by Google

CAPÍTULO 6. CLASES

argumentos: el primer argumento tiene el mismo tipo que el índice y el segundo argumento es un
parámetro del mismo tipo que el tipo de propiedad. Como ejemplo, vea la siguiente declaración:

Tipo
TIntList = Clase
Privado
Función GetInt (I: Entero largo): entero largo;
Función GetAsString (A: Cadena): Cadena;
Procedimiento SetInt (I: Entero largo; Valor: Entero largo;);
Procedimiento SetAsString (A: Cadena; Valor: Cadena);
Público
Elementos de propiedad [i: Entero largo]: Entero largo Leer GetInt
Escribe SetInt;
Propiedad StrItems [S: Cadena]: Cadena Leer GetAsString
Escribe SetAsstring;
fin;

Era
ListaAInt: ListaTInt;

Entonces serían válidas las siguientes afirmaciones:

[Link][26] := 1;
[Link]['veinticinco'] := 'cero'; WriteLn ('Artículo 26:
',[Link][26]); WriteLn ('Artículo 25: ',[Link]['veinticinco']);

Mientras que las siguientes declaraciones generarían errores:

[Link]['veinticinco'] := 1; [Link][26] := 'cero';

Porque los tipos de índice son incorrectos.

Las propiedades de la matriz pueden ser multidimensionales:

Tipo
TGrid = Clase
Privado
Función GetCell (I,J: Entero largo): Cadena;
Procedimiento SetCell (I,J: Entero largo; Valor: Cadena);
Público
Propiedad Cellcs [Fila, Col: Entero largo]: Cadena Leer GetCell
Escribe SetCell;
fin;

Si hay N dimensiones, entonces los tipos de los primeros N argumentos del getter y setter deben corresponder
a los tipos de los N especificadores de índice en la definición de propiedad de la matriz.

6.7.4 Propiedades predeterminadas


Las propiedades de la matriz se pueden declarar como propiedades predeterminadas. Esto significa que
no es necesario especificar el nombre de la propiedad al asignarla o leerla. En el ejemplo anterior, si la
definición de la propiedad items hubiera sido

106
Machine Translated by Google

CAPÍTULO 6. CLASES

Elementos de propiedad [i: Entero largo]: Entero largo Leer GetInt


Escribe SetInt; Por defecto;

Entonces la tarea

[Link][26] := 1;

Sería equivalente a la siguiente abreviatura.

ListaAInt[26] := 1;

Sólo se permite una propiedad predeterminada por clase, pero las clases descendientes pueden volver a declarar la propiedad
predeterminada.

6.7.5 Propiedades publicadas Las clases

compiladas en el estado {$M+} (como TPersistent de la unidad de clases) pueden tener una sección publicada. Para los
métodos, campos y propiedades de la sección Publicado, el compilador genera información RTTI (Información de tipo de
tiempo de ejecución), que se puede utilizar para consultar los métodos, campos y propiedades definidos en las secciones
publicadas. La unidad typinfo contiene las rutinas necesarias para consultar esta información, y esta unidad se utiliza en
el sistema de streaming en FPC en la unidad de clases.

El RTTI se genera independientemente de cuáles sean los especificadores de lectura y escritura: campos, funciones/
procedimientos o funciones/procedimientos indexados.

Sólo se pueden publicar campos escritos por clase. Para las propiedades, se puede declarar publicada cualquier propiedad
simple cuyo tamaño sea menor o igual a un puntero: flotantes, enteros, conjuntos (con menos de 32 elementos distintos),
enumerados, clases o matrices dinámicas (no propiedades de matriz).

Aunque la información de tipo en tiempo de ejecución está disponible para otros tipos, estos tipos no se pueden utilizar
para una propiedad o definición de campo en una sección publicada. La información está presente para describir, por
ejemplo, argumentos de procedimientos o funciones.

6.7.6 Información de almacenamiento

El compilador ofrece dos especificadores para controlar si una propiedad se transmite mediante un mecanismo de
transmisión, como el implementado en la unidad de clases. Estos especificadores escriben información adicional en el
RTTI generado, que luego se utiliza en el sistema de transmisión:

1. El especificador almacenado.

2. El especificador predeterminado y su homólogo nodefault.

Estos dos especificadores se evalúan para decidir si una propiedad debe transmitirse.

El argumento del especificador almacenado debe ser una constante booleana, un campo booleano de la clase o una
función sin parámetros que devuelva un resultado booleano. Si el argumento del especificador almacenado se evalúa
como Falso, la propiedad no se transmitirá.

Observación La ausencia de un especificador almacenado equivale a True almacenado.

Si la evaluación de Stored resultó en True, se considera el valor predeterminado para una propiedad: el

especificador predeterminado se puede especificar para tipos y conjuntos ordinales. Las propiedades que tienen como
valor este valor predeterminado no serán escritas en la transmisión por el sistema de transmisión, incluso si Stored es True.
El valor predeterminado se almacena en el RTTI que se genera para la clase.

107
Machine Translated by Google

CAPÍTULO 6. CLASES

Las propiedades de cadena, punto flotante y puntero tienen un valor predeterminado implícito de cadena vacía, 0 o nulo,
respectivamente. Las propiedades ordinales y de conjuntos no tienen ningún valor predeterminado implícito.

El especificador nodefault debe usarse para indicar que una propiedad no tiene un valor predeterminado. El efecto es
que el valor de esta propiedad siempre se escribe en la secuencia cuando se transmite la propiedad, si se almacena en
True.

Observación

1. Cuando se crea una instancia de la clase, el valor predeterminado no se aplica automáticamente a la propiedad,
es responsabilidad del programador hacer esto en el constructor de la clase.

2. El valor ­2147483648 no se puede utilizar como valor predeterminado, ya que se utiliza internamente para indicar
ningún valor predeterminado.

3. No es posible especificar un valor predeterminado para las propiedades de la matriz.

4. No es posible especificar la directiva Stored para las propiedades de la matriz.

5. Todos los especificadores de almacenamiento se pueden anular en clases descendientes.

propiedad Prueba almacenada Falso;

6. El especificador nodefault se puede utilizar para anular el especificador predeterminado implícito de cadena,
propiedades de punto flotante y puntero.

7. El mecanismo de transmisión aquí descrito es el implementado en la unidad de clases del RTL. Se pueden
implementar otros mecanismos de transmisión y pueden utilizar la información RTTI de una manera diferente.

6.7.7 Anulación y redeclaración de propiedades Las propiedades se pueden

anular y redeclarar en clases descendientes.

La redeclaración de propiedad toma acción si se declara el tipo de propiedad; de lo contrario, se anula la propiedad.
La única diferencia es que la anulación de propiedad reemplaza o extiende los modificadores heredados con los nuevos
modificadores, mientras que la redeclaración de propiedad oculta todos los modificadores heredados que no están
presentes en la redeclaración. El tipo de propiedad redeclarada no tiene que ser el mismo que el tipo de propiedad de
clase principal.

El siguiente ejemplo demuestra la diferencia entre anulación de propiedad y redeclaración.

tipo
TAncestro = clase
privado
FP1: Entero; propiedad
pública P:
entero Leer FP1 escribir FP1; fin;

TP1 = clase(TAncestor) public //


propiedad
anula propiedad P valor
predeterminado 1; fin;

TPReadOnly = clase (TAncestor) pública

108
Machine Translated by Google

CAPÍTULO 6. CLASES

// redeclaración de propiedad propiedad P:


entero Leer FP1; fin;

TP1 extiende la propiedad P con un valor predeterminado, TPReadOnly vuelve a declarar la propiedad P como de solo
lectura.
Observación TP1 debe establecer el valor predeterminado de P en 1 en su constructor.

En caso de redeclaración de propiedad y anulación de propiedad, el acceso al captador y al definidor es


siempre estático. Es decir, la anulación de propiedad actúa sólo sobre el RTTI del objeto y no debe confundirse
con la anulación de método.

La palabra clave "heredado" se puede utilizar para hacer referencia a la definición principal de la propiedad. Por
ejemplo, considere el siguiente código:

tipo
TAncestro = clase
privado
FP1: Entero; propiedad
pública P:
entero Leer FP1 escribir FP1; fin;

TClassA = clase(TAncestor) procedimiento


privado
SetP(const AValue: char); función getP: Char; público

constructor Crear; propiedad P:


char Leer GetP escribir SetP; fin;

procedure [Link](const AValue: char);

comenzar
P heredado:=Ord(AValue); fin;

procedimiento [Link]: char;

comenzar
Resultado:=Char((P heredado) y $FF); fin;

TClassA redefine P como una propiedad de carácter en lugar de una propiedad de número entero, pero usa la propiedad P
del padre para almacenar el valor.

Se debe tener cuidado al utilizar rutinas virtuales get/set para una propiedad: al establecer la propiedad
heredada se siguen las reglas normales de herencia para los métodos. Considere el siguiente ejemplo:

tipo
TAncestor = procedimiento
privado de
clase SetP1(const AValue: entero); virtual; propiedad pública P: escritura entera
SetP1;

109
Machine Translated by Google

CAPÍTULO 6. CLASES

fin;

TClassA = clase (TAncestor) procedimiento


privado
SetP1 (const AValue: entero); anular; procedimiento SetP2(const AValue: char);
constructor público Crear; propiedad P: escritura char SetP2; fin;

constructor [Link]; comenzar


heredado
P:=3; fin;

En este caso, al configurar la propiedad heredada P, se llamará a la implementación TClassA.SetP1, porque se anula
el método SetP1.

Si se debe llamar a la implementación de la clase principal de SetP1, entonces se debe llamar explícitamente:

constructor [Link]; comenzar SetP1(3)


heredado;
fin;

Las propiedades del ancestro redeclaradas también están disponibles desde dentro y fuera del objeto descendiente
con una conversión directa al ancestro:

función GetP(const AClassA: TClassA): Entero; comenzar resultado:=

TAncestor(AClassA).P; fin;

6.8 Propiedades de clase

Las propiedades de clase son muy parecidas a las definiciones de propiedades globales. Están asociados con la
clase, no con una instancia de la clase.

Una consecuencia de esto es que el almacenamiento del valor de la propiedad debe ser una var de clase, no un campo
o variable normal de la clase: los campos o variables normales se almacenan en una instancia de la clase.

Las propiedades de clase pueden tener un método getter y setter como las propiedades normales, pero deben ser
métodos estáticos de la clase.

Eso significa que lo siguiente contiene una definición de propiedad de clase válida:

TA = Clase (TOobjeto)
Privado
clase var myprivatea: entero; clase Función GetB:
Entero; estático; clase Procedimiento SetA (AValue: Entero); estático;
clase Procedimiento ConjuntoB (AValue: Entero); estático; público

Propiedad de clase MyA: Entero Leer MyPrivateA Escribir SetA;

110
Machine Translated by Google

CAPÍTULO 6. CLASES

Propiedad de clase MyA: Entero Leer GetB Escribir SetB; fin;

El motivo del requisito es que una propiedad de clase esté asociada a la clase particular en la que está definida,
pero no a clases descendientes. Dado que los métodos de clase pueden ser virtuales, esto permitiría que las
clases descendientes anulen el método, haciéndolos inadecuados para el acceso a las propiedades de la clase.

6.9 Tipos, constantes y variables anidados

Una definición de clase puede contener una sección de tipo, una sección constante y una sección variable. Las secciones
de tipo y constante actúan como una sección de tipo normal tal como se encuentra en una unidad o implementación de
método/función/procedimiento. Las variables actúan como campos regulares de la clase, a menos que estén en una sección
de var de clase, en cuyo caso actúan como si estuvieran definidas a nivel de unidad, dentro del espacio de nombres de la
clase (sección 6.3, página 90 ) .

Sin embargo, la visibilidad de estas secciones sí juega un papel: las constantes, tipos y variables privadas y
protegidas (estrictas o no) sólo se pueden utilizar en la medida en que su visibilidad lo permita.

Los tipos públicos se pueden utilizar fuera de la clase, por su nombre completo:

tipo
TA = Clase (TOobjeto)
Público
Escriba TEnum = (a,b,c);
Función de clase Hacer algo: TEnum; fin;

Función de clase [Link]: TEnum;

comenzar
Resultado:=a; fin;

era
E: [Link];

comenzar
E:=[Link]; fin.

Mientras

tipo
TA = Clase (TOobjeto)
Estricto privado
Escriba TEnum = (a,b,c);
Público
Función de clase Hacer algo: TEnum; fin;

Función de clase [Link]: TEnum;

comenzar
Resultado:=a;

111
Machine Translated by Google

CAPÍTULO 6. CLASES

fin;

era
E: [Link];

comenzar
E:=[Link]; fin.

No se compilará y devolverá un error:

[Link](20,10) Error: el identificador no identifica ningún miembro "TEnum"

Tenga en cuenta que para las constantes que se pueden escribir, se aplican las mismas reglas que para las variables de clase con respecto al
alcance y la anulación en descendientes:

{$modo delphi}{$J+} tipo

TA = clase // tipo base


CÓDIGO constante: entero = 99; fin;

TB = clase(TA);
TC = clase(TA);

comenzar
código TA:=0;
Có[Link]:=1;
Có[Link]:=2;
Writeln(Ta.Código:2,Tb.Código:2,Tc.código:2); fin.

Escribirá

222

Pero

{$modo delphi}{$J+} tipo

TA = clase // tipo base


CÓDIGO constante: entero = ­99; fin;

TB = clase(TA)
código constante: entero = ­98; fin;

TC = clase(TA)
Código constante: entero = ­97; fin;

comenzar
código TA:=0;
Có[Link]:=1;
Có[Link]:=2;
Writeln(Ta.Código:2,Tb.Código:2,Tc.código:2); fin.

112
Machine Translated by Google

CAPÍTULO 6. CLASES

Escribirá

012

113
Machine Translated by Google

Capítulo 7

Interfaces

7.1 De nición

A partir de la versión 1.1, FPC admite interfaces. Las interfaces son una alternativa a la herencia múltiple
(donde una clase puede tener varias clases principales) como se implementa, por ejemplo, en C++. Una
interfaz es básicamente un conjunto de métodos y propiedades con nombre: una clase que implementa la
interfaz proporciona todos los métodos tal como se enumeran en la definición de la interfaz. No es posible
que una clase implemente sólo una parte de la interfaz: es todo o nada.
Las interfaces también se pueden ordenar en una jerarquía, exactamente como clases: una definición de
interfaz que hereda de otra definición de interfaz contiene todos los métodos de la interfaz principal, así como
los métodos nombrados explícitamente en la definición de interfaz. Una clase que implementa una interfaz
debe implementar todos los miembros de la interfaz, así como los métodos de la(s) interfaz(es) principal(es).
Una interfaz puede identificarse de forma única mediante un GUID. GUID es un acrónimo de Globally Unique
Iden tifier, un entero de 128 bits que garantiza que siempre será único1. Especialmente en sistemas Windows, el
GUID de una interfaz puede y debe usarse cuando se usa COM.

La definición de una Interfaz tiene la siguiente forma:

Tipo de interfaz

­­ Interfaz fin ­
herencia [' GUID '] lista de componentes

­­herencia ( identificador de tipo de interfaz ) ­

­­ lista de componentes 6
definición del método ­
definición de la propiedad

Junto a esta definición cabe señalar lo siguiente:

• Las interfaces sólo se pueden utilizar en modo DELPHI o en modo OBJFPC.

• No hay especificadores de visibilidad. Todos los miembros son públicos (de hecho, tendría poco sentido
para hacerlos privados o protegidos).
1En teoría, por supuesto.

114
Machine Translated by Google

CAPÍTULO 7. INTERFACES

• Las propiedades declaradas en una interfaz sólo pueden tener métodos como especificadores de lectura y escritura.

• No hay constructores ni destructores. No se pueden crear instancias de interfaces directamente:


en su lugar, se debe crear una instancia de una clase que implemente la interfaz.

• Sólo los modificadores de convención de llamada pueden estar presentes en la definición de un método. Los
modificadores como virtuales, abstractos o dinámicos y, por lo tanto, también los de anulación, no pueden estar
presentes en la definición de la interfaz.

Los siguientes son ejemplos de interfaces:

IUnknown = interfaz ['{00000000­0000­0000­C000­000000000046}'] función QueryInterface(const iid: tguid;out


obj): longint; función _AddRef: entero largo; función _Release: entero largo; fin;

IInterfaz = IDesconocido;

IMyInterface = Interfaz
Función MyFunc: Entero;
Función MySecondFunc: Entero; fin;

Como puede verse, el GUID que identifica la interfaz es opcional.

7.2 Identificación de interfaz: un GUID


Una interfaz puede identificarse mediante un GUID. Este es un número de 128 bits, que se representa en una
representación de texto (una cadena literal):

['{HHHHHHHH­HHHH­HHHH­HHHH­HHHHHHHHHHHH}']

Cada carácter H representa un número hexadecimal (0–9, A–F). El formato contiene 8­4­4­4­12 números.
Un GUID también puede representarse mediante el siguiente registro, definido en la unidad objpas (incluido
automáticamente cuando está en modo DELPHI u OBJFPC):

PGuid = ^TGuid; TGuid =


entero de caja de registro empaquetado
de 1: (Datos1: DWord;
Datos2:
palabra; Datos3: palabra;
Datos4: matriz [0..7]
de byte;); ( D1 :
DPalabra; D2 : palabra; D3 : palabra; D4 :

2:
matriz[0..7] de
byte; );
(tiempo_bajo:
dword;

3: { campos uuid según RFC4122 }

115
Machine Translated by Google

CAPÍTULO 7. INTERFACES

time_mid: palabra;
time_hi_and_version: palabra;
clock_seq_hi_and_reserved: byte; clock_seq_low: byte;
nodo: matriz [0..5] de bytes; );

fin;

Se puede especificar una constante de tipo TGUID utilizando un literal de cadena:

{$modo objfpc} textuid


del programa;

constante

MiGUID: TGUID = '{10101010­1010­0101­1001­110110110110}';

comenzar
fin.

Normalmente, los GUID sólo se utilizan en Windows, cuando se utilizan interfaces COM. Más sobre esto en la
siguiente sección.

7.3 Implementaciones de interfaz

Cuando una clase implementa una interfaz, debe implementar todos los métodos de la interfaz. Si no se implementa un
método de una interfaz, el compilador dará un error. Por ejemplo:

Tipo
IMyInterface = Interfaz
Función MyFunc: Entero;
Función MySecondFunc: Entero; fin;

TMiClase = Clase(TInterfacedObject,IMyInterface)
Función MyFunc: Entero;
Función MyOtherFunc: Entero; fin;

Función [Link]: Entero;

comenzar
Resultado:=23; fin;

Función [Link]: Entero;

comenzar
Resultado:=24; fin;

resultará en un error del compilador:

Error: no se encontró ninguna implementación coincidente para el método de interfaz


"[Link]:LongInt"

116
Machine Translated by Google

CAPÍTULO 7. INTERFACES

Normalmente, los nombres de los métodos que implementan una interfaz deben ser iguales a los nombres de los
métodos en la definición de la interfaz. El compilador buscará métodos coincidentes en todos los métodos visibles:
los métodos de la clase y en los métodos de las clases principales con visibilidad protegida o superior.

Sin embargo, es posible proporcionar alias para los métodos que componen una interfaz: es decir, se le puede decir
al compilador que un método de una interfaz se implementa mediante un método existente con un nombre diferente.
Esto se hace de la siguiente manera:

Tipo
IMyInterface = Interfaz
Función MyFunc: Entero; fin;

TMiClase = Clase(TInterfacedObject,IMyInterface)
Función MiOtraFunción: Entero; Función [Link]
= MyOtherFunction; fin;

Esta declaración le dice al compilador que el método MyFunc de la interfaz IMyInterface está implementado en el método
MyOtherFunction de la clase TMyClass.

7.4 Herencia de interfaz

Es posible permitir que una interfaz sea descendiente de otra interfaz:

IParentInterface = interfaz
['{0F78D56E­85A6­4024­98D7­720D7C7B9573}'] procedimiento Foo; fin;

IChildInterface = interfaz(IParentInterface)
['{1AB2EB85­6843­462E­8CE4­32ECC065011E}'] barra de procedimiento;
fin;

IChildInterface tendrá dos métodos: foo y bar. Por lo tanto, cualquier clase que implemente esta interfaz deberá
implementar ambas interfaces:

TImplementor = clase(TInterfacedObject, IChildInterface) procedimiento público Foo; barra de

procedimientos; fin;

procedimiento [Link]; comenzar

fin;

procedimiento [Link]; comenzar

fin;

117
Machine Translated by Google

CAPÍTULO 7. INTERFACES

Tenga en cuenta que cuando una clase declara una interfaz secundaria, se puede asignar a una variable con la
interfaz secundaria. Dadas las declaraciones anteriores, se compilará lo siguiente:

era
Niño: IChildInterface;

comenzar
Niño := [Link];

Pero esto no implica que automáticamente también sea compatible con la asignación de una variable con el tipo de
interfaz principal. Lo siguiente no se compilará:

era
Padre: IParentInterface;

comenzar
padre := [Link];

Para realizar esta compilación es necesario declarar la clase como:

TImplementor = clase(TInterfacedObject,
interfaz padre,
IChildInterface)

procedimiento público Foo;


barra de procedimientos;
fin;

La razón de esto es que aunque la clase realmente implementa los métodos de IParentInterface, el compilador
solo verifica las interfaces realmente declaradas cuando verifica la compatibilidad de las asignaciones: todas
las interfaces declaradas se colocan en una tabla y solo se verifica el contenido de esta tabla.
La misma verificación se realiza en tiempo de ejecución: el compilador genera una tabla de todas las interfaces
que declara una clase y esta tabla se verifica en tiempo de ejecución. Eso significa que aunque lo siguiente
se compilará si solo se declara IChildInterface:

ParentImplementorInstance := ([Link] como IParentInterface);

seguirá fallando con un error de tiempo de ejecución:

home:~> ./ti Se
produjo una excepción no controlada en $0000000000411A27: EInvalidCast: conversión de
tipo no válida $0000000000411A27

7.5 Delegación de interfaz

A veces, los métodos de una interfaz se implementan mediante un objeto auxiliar (o delegado), o la instancia
de clase ha obtenido un puntero de interfaz para esta interfaz y debe usarse. Esto puede ocurrir, por ejemplo,
cuando se debe agregar una interfaz a una serie de clases totalmente no relacionadas: la funcionalidad de
interfaz necesaria se agrega a una clase separada, y cada una de estas clases usa una instancia de la clase
auxiliar para implementar la funcionalidad.

118
Machine Translated by Google

CAPÍTULO 7. INTERFACES

En tal caso, es posible indicarle al compilador que la interfaz no la implemente el objeto en sí, sino
que en realidad resida en una clase o interfaz auxiliar. Esto se puede hacer con el modificador de
propiedad implements.
Si la clase tiene un puntero a la interfaz deseada, lo siguiente le indicará al compilador que cuando se solicite la interfaz
IMyInterface, debe usar la referencia en el campo:

tipo
IMyInterface = procedimiento de interfaz
P1; fin;

TMyClass = clase (TInterfacedObject, IMyInterface) privada

FMyInterface: IMyInterface; // tipo de interfaz propiedad pública MyInterface:


IMyInterface
leer FMyInterface implementa IMyInterface;

fin;

La interfaz no necesariamente debe estar en un campo, se puede utilizar cualquier identificador de lectura.

Si la interfaz se implementa mediante un objeto delegado (un objeto auxiliar que realmente implementa la
interfaz), entonces también se puede usar con la palabra clave implements:

{$interfaces corba} tipo

IMyInterface = procedimiento de interfaz


P1; fin;

// NOTA: La interfaz debe especificarse aquí


TDelegateClass = clase(TObject, IMyInterface) procedimiento privado P1; fin;

TMyClass = clase (TInterfacedObject, IMyInterface) privada

FMyInterface: TDelegateClass; // propiedad de tipo de clase MyInterface:


TDelegateClass
leer FMyInterface implementa IMyInterface; fin;

Tenga en cuenta que, a diferencia de Delphi, la clase delegada debe especificar explícitamente la
interfaz: el compilador no buscará los métodos en la clase delegada, simplemente comprobará si la
clase delegada implementa la interfaz especificada.
Es posible implementar múltiples interfaces usando un único objeto delegado:

{$interfaces corba} tipo

IMyInterface = procedimiento de interfaz


P1; fin;

IMyInterface1 = interfaz

119
Machine Translated by Google

CAPÍTULO 7. INTERFACES

procedimiento P2; fin;

// NOTA: La interfaz debe especificarse aquí


TDelegateClass = clase(TObject, IMyInterface,IMyInterface1) procedimiento privado P1; procedimiento
P2; fin;

TMyClass = clase(TInterfacedObject, IMyInterface, IMyInterface1) privada

FMyInterface: TDelegateClass; // propiedad de tipo de clase MyInterface:


TDelegateClass
leer FMyInterface implementa IMyInterface,IMyInterface1; fin;

No es posible combinar la resolución del método y la delegación de la interfaz. Eso significa que no es
posible implementar parte de una interfaz mediante resolución de método e implementar parte de la
interfaz mediante delegación. Lo siguiente intenta implementar IMyInterface en parte mediante la
resolución de métodos (P1) y en parte mediante la delegación. El compilador no aceptará el siguiente código:

{$interfaces corba} tipo

IMyInterface = procedimiento de interfaz


P1; procedimiento P2;
fin;

TMyClass = clase(TInterfacedObject, IMyInterface)


FI: IMyInterface; procedimiento
protegido
IMyInterface.P1 = MyP1; procedimiento MyP1; propiedad
MyInterface: IMyInterface
lee FI implementa IMyInterface; fin;

El compilador arrojará un error:

Error: La interfaz "IMyInterface" no puede ser delegada por "TMyClass", ya tiene resoluciones de métodos

Sin embargo, es posible implementar una interfaz mediante resolución de método y otra mediante delegación:

{$interfaces corba} tipo

IMyInterface = procedimiento de interfaz


P1; fin;

IMyInterface2 = interfaz
procedimiento P2;

120
Machine Translated by Google

CAPÍTULO 7. INTERFACES

fin;

TMyClass = clase(TIrfacedObject, IMyInterface, IMyInterface2)

FI2: IMyInterface2; procedimiento


protegido
IMyInterface.P1 = MyP1; procedimiento MyP1; propiedad
pública MyInterface:

IMyInterface2
leer FI2 implementa IMyInterface2;
fin;

Tenga en cuenta que la delegación de interfaz se puede utilizar para especificar que una clase implementa interfaces principales:

IGMGetFileName = interfaz(IUDesconocido)
['{D3ECCB42­A563­4cc4­B375­79931031ECBA}'] función GetFileName:
Cadena; llamada estándar; propiedad FileName: cadena leída
GetFileName; fin;

IGMGetSetFileName = Interfaz(IGMGetFileName)
['{ECFB879F­86F6­41a3­A685­0C899A2B5BCA}'] procedimiento
SetFileName(valor constante: cadena); llamada estándar; propiedad FileName: cadena leída
GetFileName escritura SetFileName; fin;

TIntfDelegator = clase(TInterfacedObject, IGMGetFileName, IGMGetSetFileName)


protegido
FGetSetFileName: IGMGetSetFileName; constructor público
Crear;
destructor Destruir; anular;
Implementador de propiedades: IGMGetSetFileName
leído FGetSetFileName
implementa IGMGetFileName, IGMGetSetFileName; fin;

7.6 Interfaces y COM

Cuando se utilizan interfaces en Windows que deberían estar disponibles para el subsistema COM, la convención
de llamada debe ser stdcall; esta no es la convención de llamada predeterminada de Free Pascal, por lo que debe
especificarse explícitamente.

COM no conoce propiedades. Sólo conoce métodos. Por lo tanto, cuando especifique definiciones de propiedades
como parte de una definición de interfaz, tenga en cuenta que las propiedades sólo se conocerán en el programa
compilado de Free Pascal: otros programas de Windows no conocerán las definiciones de propiedades.

7.7 CORBA y otras interfaces

COM no es la única arquitectura donde se utilizan interfaces. CORBA conoce interfaces, UNO (la API de OpenOffice)
usa interfaces y Java también. Estos idiomas no conocen el IUnknown

121
Machine Translated by Google

CAPÍTULO 7. INTERFACES

interfaz utilizada como base de todas las interfaces en COM. Por lo tanto, sería una mala idea si una interfaz
descendiera automáticamente de IUnknown si no se especificaba ninguna interfaz principal. Por lo tanto, se introdujo
una directiva {$INTERFACES} en Free Pascal: especifica cuál es la interfaz principal de una interfaz, declarada sin
padre. Puede encontrar más información sobre esta directiva en la Guía del programador.

Tenga en cuenta que las interfaces COM se cuentan por referencia de forma predeterminada, porque descienden de IUnknown.

Las interfaces Corba se identifican mediante una cadena simple, por lo que su asignación es compatible con
cadenas y no con TGUID. El compilador no realiza ningún recuento automático de referencias para las interfaces
CORBA, por lo que el programador es responsable de la contabilidad de referencias.

7.8 Conteo de referencias

Todas las interfaces COM utilizan el recuento de referencias. Esto significa que cada vez que se asigna una interfaz
a una variable, se actualiza su recuento de referencias. Siempre que la variable sale del alcance, el recuento de
referencias disminuye automáticamente. Cuando el recuento de referencias llega a cero, normalmente se libera la
instancia de la clase que implementa la interfaz.

Se debe tener cuidado con este mecanismo. El compilador puede o no crear variables temporales al evaluar
expresiones y asignar la interfaz a una variable temporal, y solo entonces asignar la variable temporal a la variable
de resultado real. No se deben hacer suposiciones sobre el número de variables temporales o el momento en que
se finalizan; esto puede (y de hecho difiere) de la forma en que otros compiladores (por ejemplo, Delphi) manejan
expresiones con interfaces. [Link]. un tipo de conversión también es una expresión:

Era
B: Clase A;

comenzar // ...
AInterfaz([Link]).testproc; // ...fin;

Supongamos que la interfaz intf cuenta con referencias. Cuando el compilador evalúa [Link], crea una variable
temporal. Esta variable puede liberarse sólo cuando finaliza el procedimiento: por lo tanto, no es válido, por ejemplo,
liberar la instancia B antes de la salida del procedimiento, ya que cuando finalice la variable temporal, intentará
liberar B nuevamente.

Además, los resultados de la función pueden apuntar a una interfaz COM válida no nula al ingresar: esto se debe a que
el resultado de la función se trata como un parámetro var.

122
Machine Translated by Google

Capítulo 8

Genéricos

8.1 Introducción

Los genéricos son plantillas para generar otros tipos. Pueden ser clases, objetos, interfaces e incluso funciones,
matrices, registros. Es un concepto que proviene de C++, donde está profundamente integrado en el lenguaje.
A partir de la versión 2.2, Free Pascal también tiene oficialmente soporte para plantillas o Genéricos. Se
implementan como una especie de macro que se almacena en los archivos unitarios que genera el compilador
y que se reproduce tan pronto como se especializa una clase genérica.

La creación y el uso de genéricos es un proceso de dos fases.

1. La definición de genérico se define como un nuevo tipo: esta es una plantilla de código, una macro que
El compilador puede reproducirlo en una etapa posterior.

2. Un tipo genérico es especializado: esto define un segundo tipo, que es una implementación
específica del tipo genérico: el compilador reproduce la macro que se almacenó cuando se definió
el tipo genérico.

Hay varias unidades distribuidas con pascal libre que implementan contenedores y clases genéricos.
Por ejemplo la unidad fgl.

8.2 Definición de tipo genérico

Una definición de tipo genérico es muy parecida a una definición de tipo, con la excepción de que contiene una lista de
marcadores de posición para tipos, como se puede ver en el siguiente diagrama de sintaxis:

Tipos de clases genéricas

­­ tipo genérico identificador genérico < lista de plantillas > = tipo genérico ; ­

­­ lista de plantillas 6 lista de identi cadores ­


;

123
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

­­ lista de identi cadores 6 identificador ­


, : 6 tipos de identificador
,
registro
de objeto
de
interfaz de clase

­­ tipo genérico tipo de clase ­


tipo de objeto
tipo de interfaz
tipo de procedimiento
tipo de registro
tipo de matriz

Para clases, objetos, tipos procedimentales y registros extendidos, la declaración de tipo genérico debe ir seguida
de una implementación de tipo. Es lo mismo que una implementación de clase normal con una única excepción,
es decir, que cualquier identificador con el mismo nombre que uno de los identificadores de plantilla debe ser un
identificador de tipo.

Por lo tanto, la declaración de tipo genérico es muy parecida a una declaración de tipo normal, excepto que hay un tipo
aún desconocido presente. Los tipos desconocidos se enumeran en la lista de marcadores de posición y se desconocen
hasta que la clase se especializa.

La siguiente es una definición de clase genérica válida:

Escriba
TList genérico<_T>=clase(TObject)
Público
tipo
TCompareFunc = función (const Elemento1, Elemento2: _T): Entero;
era
datos: _T;
procedimiento Agregar (elemento: _T);
procedimiento Ordenar (comparar: TCompareFunc); fin;

Esta clase podría ir seguida de una implementación de la siguiente manera:

procedimiento [Link](elemento: _T); comenzar

datos:=elemento; fin;

procedimiento [Link](comparar: TCompareFunc); comenzar si

comparar(datos, 20) <= 0 entonces detener(1);

fin;

Hay algunas cosas dignas de mención sobre esta declaración e implementación:

1. Hay un único marcador de posición _T. Será sustituido por un identificador de tipo cuando la clase
genérica sea especializada. El identificador _T no se puede utilizar para nada más que un marcador
de posición de tipo. Esto significa que lo siguiente no sería válido:

124
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

procedimiento [Link](comparar: TCompareFunc);

Era
_t: número entero;

comenzar // hacer algo. fin;

2. El bloque de tipo local contiene un único tipo TCompareFunc. Tenga en cuenta que el tipo real aún no se
conoce dentro de la definición de clase genérica: la definición contiene una referencia al marcador de
posición _T. Todas las demás referencias de identificadores deben conocerse cuando se define la clase
genérica, no cuando la clase genérica está especializada.

3. El bloque de variables locales es equivalente a lo siguiente:

TList genérico<_T>=clase(TObject)
Público
tipo
TCompareFunc = función (const Elemento1, Elemento2: _T): Entero;
Público
datos: _T;
procedimiento Agregar (elemento: _T);
procedimiento Ordenar (comparar: TCompareFunc); fin;

4. Tenga en cuenta que los nombres de los parámetros de tipo en la declaración y la implementación deben ser los mismos.

No sólo se pueden definir clases genéricas, sino también otros tipos:

{$modo objfpc}
{$INTERFACES CORBA} tipo

Coordenada Planar genérica<t> = registro


x,y :t; fin;

TScreenCoordinate = especializar PLAnarCoordinate<palabra>;


TDiscreteCoordinate = especializar PlanarCoordinate<integer>;
TRealCoordinate = especializar PlanarCoordinate<extendido>;

TDistanceFunction genérica<t> = función (x,y: t): Extendida del objeto;

TScreenDistance = especializarse TDistanceFunction<palabra>;


TDiscreteDistance = especializar TDistanceFunction<integer>;
TRealDistance = especializar TDistanceFunction<Extendido>;

TArray genérico<t> = matriz de t;

TMyIntegerArray = especializar TArray<integer>;

IList genérico<_T> = Interfaz


Función GetItem(AIndex: Entero): _T;

125
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

Procedure SetItem(AIndex : Integer; AValue : _T);


Función GetCount: Entero;
Elementos de propiedad [AIndex: Entero] : _T Leer GetItem Escribir SetItem;
Recuento de propiedades: entero Leer GetCount; fin;

TList genérico<_T>=clase(TObject, especializar IList<_T>) tipo público

TCompareFunc = función (const Elemento1, Elemento2: _T): Entero;


Función GetItem(AIndex: Entero): _T;
Procedure SetItem(AIndex : Integer; AValue : _T);
Función GetCount: Entero;
Público
datos: _T;
procedimiento Agregar (elemento: _T);
procedimiento Ordenar (comparar: TCompareFunc); fin;

TPointSet genérico<t> = matriz de PlanarCoordinate<t> especializado;

TScreenPointSet = especializar TPointSet<palabra>;


TDiscretePointSet = especializar TPointSet<integer>;
TRealPointSet = especializar TPointSet<extendido>;

Observación Unas palabras sobre la visibilidad: los tipos de plantilla T o _T están disponibles como tipos privados estrictos.
Eso significa que los tipos no están disponibles en las clases descendientes, a menos que estén disponibles a
través de algún mecanismo privado o protegido, como en el siguiente ejemplo:

TList genérico<_T>=clase(TObject) tipo público

TItemType = _T; fin;

8.3 Especialización de tipo genérico

Una vez definido un tipo genérico, se puede utilizar para generar otros tipos: esto es como reproducir la definición
de los tipos, con los marcadores de posición de la plantilla completados con definiciones de tipos reales.

Esto se puede hacer en cualquier bloque de definición de tipo. El tipo especializado tiene el siguiente aspecto:

tipo especializado

­­ tipo especializado identificador especializado < lista de identificadores de tipo > ­

­­ lista de identificadores de tipo 6 identificador ­


,

Que es una definición muy simple. Dada la declaración de TList en la sección anterior, la siguiente sería una
definición de tipo válida:

126
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

Tipo
TPointerList = especializar TList<Pointer>;
TIntegerList = especializar TList<Integer>;

A partir de la versión 3.0 de Free Pascal, la palabra clave especializar también se puede utilizar en una declaración de variable:

Era
P : especializarse TList<Pointer>;

La palabra clave de especialización es parte del tipo especializado, por lo que cuando se utilizan nombres completos, la palabra clave
de especialización debe estar después del nombre de la unidad y de los nombres del tipo principal.

Se debe conocer el tipo en la declaración especializada, excepto en otra definición de tipo genérico. Dadas las
dos definiciones de clases genéricas:

tipo
Genérico TMyFirstType<T1> = Clase(TMyObject);
Genérico TMySecondType<T2> = Clase(TMyOtherObject);

Entonces la siguiente especialización no es válida:

tipo
TMySpecialType = especializar TMySecondType<TMyFirstType>;

porque el tipo TMyFirstType es un tipo genérico y, por lo tanto, no está completamente definido. El compilador
se quejará:

Error: los genéricos no se pueden utilizar como parámetros al especializar genéricos

Sin embargo, se permite lo siguiente:

tipo
TA = especializarse TMyFirstType<Tipo>;
TB = especializarse TMySecondType<TA>;

porque la AT ya está completamente definida cuando la TB se especializa.

Sin embargo, la palabra clave especializarse se puede utilizar en otra definición de tipo genérico, como se muestra en
el ejemplo anterior:

TList genérico<_T>=clase(TObject, especializar IList<_T>)

TPointSet genérico<t> = matriz de PlanarCoordinate<t> especializado;

En estas definiciones, la especialización sólo se realiza cuando el propio tipo genérico está especializado
y, en ese momento, se conocen los tipos.
Observación No es posible hacer una definición directa de una clase que sea una especialización de un genérico, es decir
lo siguiente no se compilará:

TMiClase = Clase;

// Otras declaraciones

TMyClass = especializar TList<T>;

127
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

8.4 Restricciones de tipo genérico

El diagrama de la sección 8.1, página 123 muestra que la lista de plantillas de tipos puede tener especificadores adicionales
para los tipos. Esto es especialmente útil para tipos de objetos: si el tipo de plantilla debe descender de una determinada
clase, entonces esto se puede especificar en la lista de plantillas:

{$mode objfpc} {$h+}


usa
sysutils, clases;

Escriba
TList genérico<_T: TComponent> = clase(TObject) pública

Escriba TCompareFunc = función (const Elemento1, Elemento2: _T): Entero;


Público
datos: _T;
procedimiento Agregar (elemento: _T);
procedimiento Ordenar (comparar: TCompareFunc); fin;

Dada la definición anterior, se compilará lo siguiente:

TPersistentList = especializar TList<TComponent>;

Pero esto no se compilará.

TPersistentList = especializar TList<TPersistent>;

El compilador devolverá un error:

Error: tipos incompatibles: se obtuvo "TPersistent" y se esperaba "TComponent"

Se pueden agrupar varios tipos:

Escriba
TList genérico<Clave1,Clave2: TComponent; Valor1: TObjeto> = clase(TObjeto)

Además, es posible especificar más de un identificador de tipo para restricciones de tipo de clase y de interfaz. Si se
especifica una clase, entonces el tipo utilizado para la plantilla debe ser igual o inferior al tipo indicado:

Escriba
TList genérico<T: TComponent, IEnumerable> = clase(TObject)

Una clase utilizada para especializar T debe descender de TComponent y debe implementar IEnumerable.

Si se especifica una interfaz, entonces el tipo de plantilla debe implementar al menos esta interfaz, pero
también puede ser una interfaz descendiente de esta interfaz:

Escriba
TGenList genérico<T: IEnumerable> = clase(TObject)

IMyEnum = Interfaz (IEnumerable)


procedimientos DoMy;

128
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

fin;

TList = especializar TGenList<IMyEnum>;


TSomeList = Especializar TGenList<TList>;

Se pueden especificar múltiples interfaces, en ese caso el tipo de clase debe implementar todas las interfaces
enumeradas: es posible mezclar un nombre de clase con varios nombres de interfaz.

Si no hay restricciones de tipo vigentes, el compilador asumirá que los tipos de plantilla no son compatibles con
la asignación.

Esto es especialmente importante cuando la clase genérica contiene métodos sobrecargados. Dada la siguiente
declaración de tipo genérico:

escriba
TTest genérico<T1, T2> = clase
prueba de procedimiento (aArg: LongInt); prueba de
procedimiento (aArg: T1); prueba de
procedimiento (aArg: T2); que;

Especializarse en lo anterior compilará si T1 y T2 son de dos tipos diferentes y ninguno de ellos es también LongInt. Lo
siguiente compilará:

T1 = especializarse TTest<String, TObject>;

Pero los dos siguientes no se compilarán:

T2 = especializarse TTest<String, String>;

T2 = especializarse TTest<String, Longint>;

8.5 Compatibilidad con Delphi

El soporte de genéricos de FPC se implementa de manera algo diferente a la de Delphi. En esta sección se
destacan las principales diferencias.

8.5.1 Elementos de sintaxis

La sintaxis que se muestra en los diagramas de sintaxis es la sintaxis requerida en el modo ObjFPC del compilador.
Sin embargo, en modo Delphi, no se deben utilizar las palabras clave especializadas y genéricas, como se
muestra en el siguiente ejemplo:

Tipo
TTest<T> = Clase(TOobjeto)
Privado
FObj : T;
Público
Propiedad Obj: T Leer FObj Escribir FObj; fin;

TIntegerTest = TTest<Entero>;

129
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

A diferencia del modo Objfpc, los nombres de los tipos de plantilla deben repetirse en las definiciones de métodos.

Tipo
TTest<T> = Clase(TOobjeto)
Privado
FObj : T;
Público
Procedimiento DoIt;
Propiedad Obj: T Leer FObj Escribir FObj; fin;

Procedimiento TTest<T>.DoIt;

empezar
fin;

Este requisito está directamente relacionado con la capacidad de sobrecarga de tipo genérico mencionada en la siguiente
sección.

8.5.2 Restricciones de tipo de registro

En modo Delphi, las restricciones de tipo de registro también permitirán el uso de tipos simples:

Tipo
TList<_T: registro> = clase(TObject) pública

Escriba TCompareFunc = función (const Elemento1, Elemento2: _T): Entero;


Datos
públicos: _T;
procedimiento Agregar (elemento: _T);
procedimiento Ordenar (comparar: TCompareFunc); fin;

ListaTInt = ListaT<Entero>;

La restricción se aplica al especializar el tipo. Eso significa que el modo activo al especializar un tipo determina si se
puede usar un tipo simple o no: si la restricción de registro se compiló usando el modo ObjFPC, el código escrito en
modo Delphi puede especializarlo con un tipo simple.
de todos modos.

Por ejemplo:

unidad tg;

interfaz

{$modo objfpc}

Escriba
TList genérico<_T: registro> = clase(TObject) pública

Escriba TCompareFunc = función (const Elemento1, Elemento2: _T): Entero;


Público
datos: _T;

130
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

procedimiento Agregar (elemento:


_T); procedimiento Ordenar (comparar: TCompareFunc); fin;

implementación

procedimiento genérico TList<_T>.Add(item: _T);

comenzar

fin;

procedimiento genérico TList<_T>.Sort(comparar: TCompareFunc);

empezar
fin;

fin.

se puede utilizar en {$MODE Delphi} para:

{$modo delphi} usa tg;

Escriba TIntList = TList<Entero>; empezar fin.

8.5.3 Sobrecargas de tipos El

modo Delphi permite sobrecargas de tipos genéricos. Esto significa que es posible declarar la misma clase
genérica con diferentes listas de tipos de plantilla. Por lo tanto, son posibles las siguientes declaraciones:

Tipo
TTest<T> = Clase(TOobjeto)
Privado
FObj : T;
Público
Propiedad Obj: T Leer FObj Escribir FObj; fin;

TTest<T,S> = Clase(TObjeto)
Privado
FObj1: T;
FObj2: S;
Público
Propiedad Obj1: T Leer FObj1 Escribir FObj1;
Propiedad Obj2: S Leer FObj2 Escribir FObj2; fin;

131
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

8.5.4 Consideraciones sobre el espacio de nombres


En modo Delphi, los genéricos no interfieren con el espacio de nombres de las variables, esto significa que
también se compilará lo siguiente:

Tipo
TTest<T> = Clase(TOobjeto)
Privado
FObj : T;
Público
Propiedad Obj: T Leer FObj Escribir FObj; fin;

Era
TTest: Entero;

Sin embargo, esto no funciona para constantes y funciones.

8.6 Compatibilidad de tipos

Siempre que una clase genérica se especializa, esto da como resultado un tipo nuevo y distinto. Estos tipos son
compatibles con la asignación si se utilizan los mismos tipos de plantilla.

Tome la siguiente definición genérica:

{$modo objfpc} unidad


ua;

interfaz

tipo
TMyClass genérico<T> = Clase(TObject)
Procedimiento Hacer algo (A: T; B: INteger); fin;

Implementación

Procedimiento [Link](A: T; B: Entero);

comenzar // Algún código.


fin;

fin.

Y las siguientes especializaciones:

{$modo objfpc} unidad


ub;

interfaz

usos realizados;

132
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

Tipo
TB = Especializar TMyClass<cadena>;

implementación

fin.

las siguientes especializaciones son idénticas, pero aparecen en una unidad diferente:

{$modo objfpc} unidad


uc;

interfaz

usos realizados;

Tipo
TB = Especializar TMyClass<cadena>;

implementación

fin.

Luego se compilará lo siguiente:

{$modo objfpc} unidad


ud;

interfaz

utiliza ua, ub, etc.;

Era
B: [Link]; C:
[Link];

implementación

comenzar
B:=C;
fin.

Los tipos [Link] y [Link] son compatibles con la asignación. No importa que los tipos estén definidos en
unidades diferentes. También podrían definirse en la misma unidad:

{$modo objfpc} unidad


ue;

interfaz

usos realizados;

Tipo
TB = Especializar TMyClass<cadena>;

133
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

TC = Especializar TMyClass<string>;

Era
B: tuberculosis;

C: TC;

implementación

comenzar
B:=C;
fin.

Cada especialización de una clase genérica con los mismos tipos que parámetros es un tipo nuevo y distinto, pero
estos tipos son compatibles con la asignación si los tipos de plantilla utilizados para especializarlos son iguales.

Si la especialización es con un tipo de plantilla diferente, los tipos siguen siendo distintos, pero ya no son
compatibles con la asignación. es decir, lo siguiente no se compilará:

{$modo objfpc} unidad


uf;

interfaz

usos realizados;

Tipo
TB = Especializar TMyClass<cadena>;
TC = Especializar TMyClass<entero>;

Era
B: tuberculosis;

C: TC;

implementación

comenzar
B:=C;
fin.

Al compilar, se producirá un error:

Error: tipos incompatibles: se obtuvo "TMyClass<[Link]>" y se esperaba


"TMyClass<[Link]>"

8.7 Uso del intrínseco predeterminado

Al escribir rutinas genéricas, a veces se debe inicializar una variable cuyo tipo no se conoce durante la declaración del
genérico. Aquí es donde también entra en juego el valor intrínseco predeterminado (sección 4.5, página 70). Dada la
siguiente declaración genérica:

tipo

134
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

TTest<T> genérico = prueba de


procedimiento de clase;
fin;

El siguiente código inicializará correctamente la variable myt durante la especialización:

procedimiento [Link];
era
mito: T;

comenzar // tendrá el valor predeterminado correcto si la clase está especializada myt := Default(T); fin;

8.8 Unas palabras sobre el alcance

Cabe destacar que todos los identificadores distintos de los marcadores de posición de la plantilla deben
conocerse cuando se declara la clase genérica. Al mismo tiempo, no se puede suponer nada sobre el tipo de
plantilla (a menos que se le imponga una restricción).

Esto funciona de varias maneras.

En ausencia de restricciones de tipo, el código genérico no puede hacer suposiciones sobre el tipo de
plantilla T. Considere la siguiente unidad:

unidad ts;

interfaz
{$modeswitch registros avanzados}

tipo
PListEl = ^TListEl;
TListEl = registro empaquetado
Anterior, Siguiente: PListEl; fin;

implementación

escriba
LstEnumerator genérico<T> = registrar lst privado, lst_save:
T; público

constructor Crear (Valor constante: T); función MoveNext:


booleana; fin;

función [Link]: booleano; comenzar Resultado:=lst <>


nil; si
resultado entonces

lista:=lista^.siguiente; fin;

135
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

constructor [Link] (Valor constante: T); comenzar lista:= Valor;

lst_save := nulo; fin;

Tipo
TMyListEnum = especializar LstEnumerator<TListEl>;

fin.

El compilador arrojará un error porque cuando compila la definición genérica, no puede verificar que

lista:=lista^.siguiente;

es correcto. lst es de tipo T, pero el compilador no sabe (todavía) qué es T y, por lo tanto, no puede saber si
tiene un campo siguiente.

Este problema se puede resolver con restricciones de tipo:

unidad ts;
{$modo delphi} interfaz

tipo
TListEl = clase
Anterior, Siguiente: TListEl; fin;

TMyRecord1 = Clase(TListEl)
MiCampo: Cadena; fin;

TMyRecord2 = Clase(TListEl)
MiEntero: Entero; fin;

implementación

tipo
TLstEnumerator<T : TListEl> = clase lista privada, lst_save:
T; público

constructor Crear (Valor constante: T); función MoveNext:


booleana; fin;

función TLstEnumerator<T>.MoveNext: booleano; comenzar Resultado:=lst


<>
T(nil); si resultado entonces

136
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

lista:=T([Link]); fin;

constructor TLstEnumerator<t>.Create(Valor constante: T); comenzar lista:= Valor;

lst_save := T(nulo); fin;

Tipo
TMyRecord1Enum = TLstEnumerator<TMyRecord1>;
TMyRecord2Enum = TLstEnumerator<TMyRecord2>;

Aquí, el compilador sabe que lst es al menos de tipo TListEl y, por tanto, contiene los miembros Prev y Next.

Además del tipo de plantilla, se deben conocer todos los demás tipos utilizados en la declaración genérica. Esto
significa que debe existir un identificador de tipo con el mismo nombre. La siguiente unidad producirá un error:

{$mode objfpc} unidad


miunidad;

interfaz

tipo
TMyClass genérico<T> = Clase(TObject)
Procedimiento HacerAlgo(A: T; B: TAlgúnTipo); fin;

Tipo
TSomeType = Entero;
TSomeTypeClass = especializar TMyClass<TSomeType>;

Implementación

Procedimiento [Link](A: T; B: TSomeType);

comenzar // Algún código.


fin;

fin.

El código anterior generará un error, porque el tipo TSomeType no se conoce cuando se analiza la declaración:

inicio: >fpc [Link]


[Link](8,47) Error: Identificador no encontrado "TSoType" [Link](11,1) Fatal: Hubo 1
error al compilar el módulo, deteniéndose

Una segunda forma en que esto es visible es la siguiente. Supongamos una unidad

{$modo objfpc} unidad


mya;

137
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

interfaz

tipo
TMyClass genérico<T> = Clase(TObject)
Procedimiento Hacer algo (A: T); fin;

Implementación

Procedimiento DoLocalThings;

comenzar
Writeln('[Link]'); fin;

Procedimiento [Link](A: T);

comenzar
hacer cosas locales; fin;

fin.

El compilador no permitirá compilar esta unidad, ya que la función DoLocalThings no será visible cuando el tipo genérico
sea especializado:

Error: la plantilla genérica global hace referencia a una tabla simbólica estática

Ahora, si se modifica la unidad y la función DoLocalThings se mueve a la sección de interfaz, la unidad se compilará. Al
usar este genérico en un programa:

{$modo objfpc} programa


myb;

usa mya;

procedimiento DoLocalThings;

comenzar
Writeln('[Link]'); fin;

Tipo
TB = especializarse TMyClass<Integer>;

Era
B: tuberculosis;

comenzar
B:=[Link]; [Link]
algo(1); fin.

138
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

A pesar de que los genéricos actúan como una macro que se reproduce en el momento de la especialización,
la referencia a DoLocalThings se resuelve cuando se define TMyClass, no cuando se define TB. Esto
significa que la salida del programa es:

inicio: >fpc ­S2 [Link] inicio: >myb


[Link]

Este comportamiento está dictado por la seguridad y la necesidad:

1. Un programador especializado en una clase no tiene forma de saber qué procedimientos locales se utilizan, por lo que
no puede “anularlo” accidentalmente.

2. Un programador especializado en una clase no tiene forma de saber qué procedimientos locales se
utilizan, por lo que tampoco puede implementarlo, ya que no conoce los parámetros.

3. Si se utilizan procedimientos de implementación como en el ejemplo anterior, no se puede hacer referencia a ellos
desde fuera de la unidad. Podrían estar en otra unidad y el programador no tiene forma de saber si debe incluirlos
antes de especializar su clase.

8.9 Sobrecarga de operadores y genéricos

La sobrecarga de operadores (capítulo 15, página 233) y los genéricos están estrechamente relacionados. Imagine una clase
genérica que tiene la siguiente definición:

{$modo objfpc} unidad


mya;

interfaz

tipo
TMyClass genérico<T> = Clase(TObject)
Función Agregar(A,B: T): T; fin;

Implementación

Función [Link](A,B: T): T;

comenzar
Resultado:=A+B; fin;

fin.

Cuando el compilador reproduce la macro genérica, la adición debe ser posible. Para una especialización
como esta:

TMyIntegerClass = especializar TMyClass<integer>;

Esto no es un problema, ya que el método Add pasaría a ser:

Procedimiento [Link](A,B: Entero): Entero;

139
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

comenzar
Resultado:=A+B; fin;

El compilador sabe cómo sumar dos números enteros, por lo que este código se compilará sin problemas. Pero
el siguiente código:

Tipo
TComplex = registro
Re, Im: Doble; fin;

Tipo
TMyIntegerClass = especializar TMyClass<TComplex>;

No se compilará, a menos que se defina la adición de dos tipos TComplex. Esto se puede hacer usando
operadores de registro:

{$modeswitch registros avanzados}


usa mya;

Tipo
TComplex = registro
Re, Im: Doble; operador
de clase +(a,b: TComplex): TComplex; fin;

operador de clase TComplex.+ (a,b: TComplex): TComplex;

comenzar
[Link]:=[Link]+[Link];
[Link]:=[Link]+[Link]; fin;

Tipo
TMyComplexClass = especializar TMyClass<TComplex>;

comenzar
// Código aquí
fin.

Actualmente, debido a una restricción de implementación, no funcionará utilizando un operador global, es


decir, lo siguiente aún no funciona:

usa mya;

Tipo
TComplex = registro
Re, Im: Doble; fin;

operador + (a,b: TComplex): TComplex;

140
Machine Translated by Google

CAPÍTULO 8. GENÉRICOS

comenzar
[Link]:=[Link]+[Link];
[Link]:=[Link]+[Link]; fin;

Tipo
TMyComplexClass = especializar TMyClass<TComplex>;

comenzar
// Código aquí
fin.

Se espera soporte para esta construcción en una versión futura de Free Pascal.

141
Machine Translated by Google

Capítulo 9

Registros extendidos

9.1 De nición
Los registros extendidos son en muchos sentidos equivalentes a objetos y, en menor medida, a clases: son
registros que tienen métodos y propiedades asociados. Al igual que los objetos, cuando se definen como una
variable, se asignan en la pila. No necesitan tener constructor. Los registros extendidos tienen limitaciones sobre
los objetos y clases en el sentido de que no permiten herencia ni polimorfismo. Es imposible crear un registro
descendiente de un registro1.

¿Por qué entonces introducir registros extendidos? Fueron introducidos por Delphi 2005 para admitir una de las
características introducidas por .NET. Delphi desaprobó el antiguo estilo TP de objetos y reintrodujo las
características de .NET como registros extendidos. Free Pascal pretende ser compatible con Delphi, por lo que
también se permiten registros extendidos en Free Pascal, pero sólo en modo Delphi.

Si se desean registros extendidos en el modo ObjFPC, entonces se debe usar un interruptor de modo:

{$modo objfpc}
{$modeswitch advancedrecords}

La compatibilidad no es la única razón para introducir registros ampliados. Existen algunas razones prácticas
para utilizar métodos o propiedades en los registros:

1. Está más en línea con un enfoque de programación orientado a objetos: el tipo también contiene cualquier
métodos que funcionan en ello.

2. A diferencia de un enfoque procedimental, colocar todas las operaciones que funcionan en un registro en el
registro mismo permite que un IDE muestre los métodos disponibles en el registro cuando muestra las
opciones de finalización de código.

Definir un registro extendido es muy parecido a definir un objeto o clase:

tipo de registro extendido

­­ registro ­
Fin de la lista de 6 componentes
lleno
1aunque se puede mejorar utilizando asistentes de registro, encontrará más información sobre esto en el capítulo sobre asistentes de registro.

142
Machine Translated by Google

CAPÍTULO 9. EXPEDIENTES EXTENDIDAS

­­ lista de componentes ­
especi cador de visibilidad de registros 6 definición de campo

­ ­
6 definición del método de registro 6 definición de propiedad

­ parte variante ­
6 de nición del operador de registros

­ lista de identi cadores de definición de campo : tipo ; ­

­­ especi cador de visibilidad de registro privado ­


publico

estricto

­­ definición del método de registro encabezado de función ; ­


encabezado de procedimiento modificadores de llamadas ;

­­ definición de operador de registro definición de ­


­ operador de clase ­

Algunas de las restricciones en comparación con clases u objetos son obvias en el diagrama de sintaxis:

• No herencia de registros.

• No existe ninguna sección publicada y protegida.

• No se pueden definir constructores ni destructores.

• Los métodos de clase (si es posible nombrarlos así) requieren la palabra clave estática.

• Los métodos no pueden ser virtuales ni abstractos; esto es una consecuencia del hecho de que no hay herencia.
tancia.

Aparte de eso, la definición se parece mucho a la de una clase u objeto, excepto que los operadores se pueden
definir en un registro extendido.

Observación En el caso de un registro variante, la palabra clave Case inicia implícitamente una sección var, lo que significa que
las variables o métodos de clase no están permitidos en la parte variante del registro.

Los siguientes son algunos ejemplos de definiciones de registros extendidos válidas:

TTest1 = registro

a: número entero;
función Prueba(aRecurse: Booleano): Entero; fin;

TTest2 = registro
privado
A,b: número entero;
conjunto
de procedimiento públicoA (AValue: entero);
propiedad SafeA: Entero Lectura A Escritura ConjuntoA; fin;

TTest3 = registro empaquetado

143
Machine Translated by Google

CAPÍTULO 9. EXPEDIENTES EXTENDIDAS

privado
fA,fb: byte;
procedimiento setA(AValue: Entero); función geta: número
entero; propiedad pública A: Entero Leer
GetA
Escribir SetA; fin;

TTest4 = registro

privado a:
entero; función
protegida getp:
entero; público b: cadena; configuración
del
procedimiento
(unValor: entero); propiedad p: entero leer Getp Write SetP;
caso público x: número entero de

1: (P: cadena[10]);
2: (S: Cadena[10]); fin;

Tenga en cuenta que es posible especificar una visibilidad para los miembros del registro. Esto es particularmente
útil, por ejemplo, al crear una interfaz para una biblioteca C: los campos reales se pueden declarar ocultos y se
pueden exponer más propiedades tipo "pascal" que actúan como los campos reales. La definición de registro
TTest3 muestra que la directiva empaquetada se puede utilizar en registros extendidos. Los registros extendidos
tienen el mismo diseño de memoria que sus contrapartes regulares: los métodos y propiedades no forman parte
de la estructura del registro en la memoria.

La definición de registro TTest4 en los ejemplos anteriores muestra que el registro extendido aún tiene la capacidad
de definir una parte variante. Al igual que con el registro regular, la parte variante debe aparecer al final. No puede
contener métodos.

9.2 Enumeradores de registros extendidos

Los registros extendidos pueden tener un enumerador. Para ello, se debe definir en el registro extendido una
función que devuelva un registro enumerador:

{$mode objfpc}
{$modeswitch advancedrecords} tipo TIntArray =

matriz[0..3] de Integer;

TEnumerador = registro

privado
Índice FI: Entero;
Farray: TIntArray; función
GetCurrent: Entero; función pública MoveNext:
booleana;
propiedad Actual: Entero leído GetCurrent; fin;

144
Machine Translated by Google

CAPÍTULO 9. EXPEDIENTES EXTENDIDAS

TMyArray = registro F:
matriz[0..3] de Entero; función GetEnumerator:
TEnumerator; fin;

función [Link]: booleano; comenzar inc(FIndex);

Resultado: = FIndex
<Longitud (FArray); fin;

función [Link]: Entero; comenzar resultado :=

FArray[FIndex]; fin;

función [Link]: TEnumerator; comenzar [Link] := F;

[Link] := ­1; fin;

Después de estas definiciones, el siguiente código compilará y enumerará todos los elementos en F:

era
Arr: TMyArray;
I: Entero; comenzar
por yo
en Arr do
EscribirLn(I);
fin.

Se puede lograr el mismo efecto con el operador enumerador:

{$mode objfpc}
{$modeswitch advancedrecords} tipo TIntArray =

matriz[0..3] de Integer;

TEnumerador = registro

privado
Índice FI: Entero;
Farray: TIntArray; función
GetCurrent: Entero; función pública MoveNext:
booleana;
propiedad Actual: Entero leído GetCurrent; fin;

TMyArray = registro F:
matriz[0..3] de Entero; fin;

145
Machine Translated by Google

CAPÍTULO 9. EXPEDIENTES EXTENDIDAS

función [Link]: booleano; comenzar inc(FIndex);

Resultado: = FIndex
<Longitud (FArray); fin;

función [Link]: Entero; comenzar resultado :=

FArray[FIndex]; fin;

operador Enumerador (const A: TMyArray): TEnumerador; comenzar [Link] := AF;

[Link] := ­1; fin;

Esto también permitirá que el código se ejecute.

9.3 Operadores de registro

La sobrecarga del operador se trata en detalle en el capítulo sobre sobrecarga del operador capítulo 15, página 233.
Sin embargo, dado que Delphi implementó la sobrecarga de operadores como parte de los registros avanzados, es necesario
decir algunas palabras al respecto aquí.

Como se puede ver en el diagrama de sintaxis para registros extendidos, FPC admite la sintaxis Delphi para operadores
en registros extendidos. Esta sintaxis está disponible tanto en modo ObjFPC como en modo Delphi.

En modo objfpc, los operadores deben definirse con sus nombres simbólicos, al igual que las sobrecargas de
operadores normales:

{$modo objfpc}
{$modeswitch advancedrecords}

Tipo
TComplex = registro
Re, Im: Doble; operador
de clase +(a,b: TComplex): TComplex; fin;

operador de clase TComplex.+ (a,b: TComplex): TComplex; comenzar [Link]:=[Link]+[Link];

[Link]:=[Link]+[Link]; fin;

Los operadores funcionan tal como lo harían si se definieran utilizando la sintaxis FPC normal para operadores, pero
esta sintaxis, por supuesto, se limita a los tipos de registros. Tenga en cuenta que tienen la palabra clave class como
prefijo y el nombre del tipo de registro antepuesto en la implementación.

Como se indicó anteriormente, en el modo ObjFPC, los operadores deben indicarse con sus nombres de símbolos.
Por el contrario, en modo Delphi también se pueden utilizar los nombres de los operadores, similar a la sintaxis en
Delphi:

{$modo delphi}

146
Machine Translated by Google

CAPÍTULO 9. EXPEDIENTES EXTENDIDAS

Tipo
TComplex = registro
Re, Im: Doble;
operador de clase agregar(a,b: TComplex): TComplex;
fin;

operador de clase [Link] (a,b: TComplex): TComplex;


comenzar
[Link]:=[Link]+[Link];
[Link]:=[Link]+[Link];
fin;

Por supuesto, esto se debe a que la sintaxis debe ser compatible con Delphi.
A continuación se muestra una tabla que vincula los nombres de los operadores simbólicos con el nombre en texto sin formato. Tenga en cuenta que algunos FPC

Los operadores no tienen un equivalente usando un nombre Delphi.

Tabla 9.1: Nombres de operadores

Nombre del símbolo


+ agregar o positivo
­
restar o negativo
* multiplicar
/ dividir
** (sin equivalente)
>< (sin equivalente)
=
igual
< menos que
<= menor o igual
> mas grande que
>= mayor que o igual
<> no es igual
:= implícito
en en
shr desplazamiento a la derecha
shl desplazamiento a la izquierda

división div
contra módulo
y bit a bit o lógico y
o bit a bit o lógico
xor bit a bit
no es lógicono

Por ejemplo, el operador de energía (**) se puede utilizar en modo Delphi usando su nombre simbólico:

{$modo delphi}
{$modeswitch registros avanzados}

Tipo
TComplex = registro
Re, Im: Doble;
operador de clase **(a,b: TComplex): TComplex;
fin;

147
Machine Translated by Google

CAPÍTULO 9. EXPEDIENTES EXTENDIDAS

operador de clase TComplex.** (a,b: TComplex): TComplex; comenzar [Link]:=­[Link];

[Link]:=­[Link]; fin;

Era
a,b : TComplejo; comenzar
a:=a**b;
fin.

Puede encontrar más información sobre la sobrecarga del operador en el capítulo sobre sobrecarga del operador
capítulo 15, página 233.

148
Machine Translated by Google

Capítulo 10

Ayudantes de clase, registro y tipo

10.1 Definición

Los ayudantes de clase, registro y tipo se pueden utilizar para agregar métodos a una clase, registro o tipo simple
existente, sin realizar una derivación de la clase ni volver a declarar el registro.

Para un registro o un tipo simple, el asistente de tipo actúa como si el registro o el tipo simple fuera una
clase y se declaran métodos para él. Dentro de los métodos, Self hará referencia al valor del registro o tipo
simple.
Para las clases, el efecto es como insertar un método en la tabla de métodos de la clase. Si la declaración
auxiliar está en el alcance actual del código, entonces los métodos y propiedades del asistente se pueden
usar como si fueran parte de la declaración de clase para la clase o registro que extiende el asistente.
A continuación se presenta el diagrama de sintaxis para una clase, registro o tipo auxiliar:

Tipo de ayudante

­­ tipo de ayudante para la lista de componentes auxiliares del Identi er 6 ­


registro ( ayudante de base )
de
clase : modificadores de sugerencia final ­

­­ lista de componentes auxiliares definición del método ­


definición de la propiedad

El diagrama muestra que una definición auxiliar se parece mucho a una definición de clase normal. Simplemente
declara algunos constructores, métodos, propiedades y campos adicionales para una clase: la clase, registro o
tipo simple para el cual el ayudante es una extensión se indica después de la palabra clave for.

Dado que un enumerador para una clase se obtiene mediante un método normal, los ayudantes de clase también se pueden
utilizar para anular los enumeradores de la clase.

Como se puede ver en el diagrama de sintaxis, es posible crear descendientes de ayudantes: los ayudantes pueden
formar una jerarquía propia, lo que permite anular los métodos de un ayudante principal. También tienen
especificadores de visibilidad, al igual que los registros y las clases.

Como en una instancia de la clase, el identificador Self en un método de una clase auxiliar se refiere a la
instancia de la clase (no a la instancia auxiliar). Para un registro, se refiere al registro.

149
Machine Translated by Google

CAPÍTULO 10. AYUDANTES DE CLASE, REGISTRO Y TIPO

El siguiente es un asistente de clase simple para la clase TObject, que proporciona una versión alternativa del método
ToString estándar.

TObjectHelper = ayudante de clase para TObject


función AsString(const aFormat: String): String; fin;

función [Link] (const aFormat: String): String; comenzar Resultado := Formato(aFormat,

[ToString]); fin;

era
o: TObjeto;
comenzar
Writeln([Link]('El nombre del objeto es %s')); fin.

Observación El modificador auxiliar es solo un modificador justo después de las palabras clave de clase o registro. Eso significa que el
primer miembro de una clase o registro no puede ser nombrado ayudante. A un miembro de una clase o registro se le puede
llamar ayudante, pero no puede ser el primero, a menos que se le escape con un &, como ocurre con todos los identificadores
que coinciden con una palabra clave.

Observación La compatibilidad con ayudantes de tipos requiere el uso del modo Delphi o el uso de un interruptor de modo de ayudantes de tipos.
en otros modos:

{$MODESWITCH TYPEHELPERS}

Observación El soporte para asistentes de registros requiere el uso del modo Delphi o el uso de registros avanzados.
cambio de modo en otros modos:

{$MODESWITCH ADVANCEDRECORDS}

10.2 Restricciones a los ayudantes de clase

No es posible ampliar una clase con ningún método o propiedad. Existen algunas restricciones sobre las posibilidades:

• No se permiten destructores ni destructores de clases.

• No se permiten constructores de clases.

• Los ayudantes de clase no pueden descender de los ayudantes de registros y no pueden ampliar los tipos de registros.

• No se permiten definiciones de campos. Tampoco lo son los campos de clase.

• No se permiten propiedades que hagan referencia a un campo. De hecho, esto es una consecuencia de lo anterior.
artículo.

• No se permiten métodos abstractos.

• Los métodos virtuales de la clase no se pueden anular. Se pueden ocultar dándoles el mismo nombre o se pueden
sobrecargar usando la directiva de sobrecarga.

• A diferencia de los procedimientos o métodos normales, el especificador de sobrecarga debe usarse explícitamente
al sobrecargar métodos de una clase en un asistente de clase. Si no se utiliza la sobrecarga, el método auxiliar
oculta el método del tipo extendido (como ocurre con las clases regulares).

150
Machine Translated by Google

CAPÍTULO 10. AYUDANTES DE CLASE, REGISTRO Y TIPO

Lo siguiente modifica el ejemplo anterior sobrecargando el método ToString:

TObjectHelper = ayudante de clase para TObject


función ToString(const aFormat: String): String; sobrecarga; fin;

función [Link] (const aFormat: String): String; comenzar Resultado := Formato(aFormat,

[ToString]); fin;

era
o: TObjeto;
comenzar
Writeln([Link]('El nombre del objeto es %s')); fin.

10.3 Restricciones a los ayudantes de registros

Los registros no ofrecen las mismas posibilidades que las clases. Esto se refleja en las posibilidades a la hora de crear
ayudas de registro. A continuación se enumeran las restricciones sobre los asistentes de registros:

• No se puede utilizar un asistente de registro para ampliar una clase. Lo siguiente fallará:

TTestHelper = asistente de registro para el final de TObject;

• Dentro de la declaración de una ayuda, no se puede acceder a los métodos/campos del registro extendido, por
ejemplo, en una definición de propiedad. Por supuesto, se puede acceder a ellos en la implementación. Esto
significa que lo siguiente no se compilará:

TTest = registro

Prueba: Entero; fin;

TTestHelper = asistente de registro para TTest


propiedad AccessTest: prueba de lectura de enteros; fin;

• Los asistentes de registro solo pueden acceder a campos públicos (en caso de que se trate de un registro extendido con visibilidad específica).
se utiliza fuegos).

• La herencia de ayudas de registros sólo se permite en el modo ObjFPC; En modo Delphi, no es


permitido.

• Los ayudantes de registro sólo pueden descender de otros ayudantes de registro, no de ayudantes de clase.

• A diferencia de los ayudantes de clase, un ayudante de registro descendiente debe extender el mismo tipo de registro.

• En modo Delphi, no es posible llamar al método del registro extendido usando heredado. Es posible hacerlo
en modo ObjFPC. El siguiente código necesita el modo ObjFPC para compilarse:

tipo
TTest = registro

151
Machine Translated by Google

CAPÍTULO 10. AYUDANTES DE CLASE, REGISTRO Y TIPO

función Prueba(aRecurse: Booleano): Entero; fin;

TTestHelper = asistente de registro para TTest


función Prueba(aRecurse: Booleano): Entero; fin;

función [Link](aRecurse: Boolean): Entero; comenzar Resultado: = 1; fin;

función [Link](aRecurse: Boolean): Entero; comenzar si una recurrencia entonces

Resultado: = Prueba heredada (Falso)


demás
Resultado := 2;
fin;

10.4 Consideraciones para ayudantes de tipo (simples)

Para tipos simples, las reglas son prácticamente las mismas que para los registros, además hay algunos requisitos
adicionales:

• La compatibilidad con los ayudantes de tipo debe activarse mediante los ayudantes de tipo del interruptor de modo:

{$ ayudantes de tipo de cambio de modo}

Este interruptor de modo está habilitado de forma predeterminada solo en el modo Delphi y DelphiUnicode.

• En el modo Delphi (y DelphiUnicode), para una compatibilidad más estricta con Delphi, los asistentes de registro deben
usarse en lugar de un ayudante de tipo.

• Los modos ObjFPC y MacPas usan ayuda de tipo, pero el interruptor de modo TypeHelpers debe estar
usado.

• Los siguientes tipos no son compatibles:

– Todo tipo de archivos (Texto, archivo de...)


– Variables procesales

– Tipos como registros, clases, clases Objective C, clases C++, objetos e interfaces también están prohibidos;
el asistente de clase debe usarse para las clases. Eso significa que, por ejemplo, lo siguiente fallará:

TTestHelper = escriba ayuda para el final de TObject;

Por supuesto, esto significa que se admiten todos los demás tipos simples.

• Los ayudantes de tipos pueden implementar constructores.

• La herencia de ayudas de registros sólo se permite en el modo ObjFPC; En modo Delphi, no es


permitido.

152
Machine Translated by Google

CAPÍTULO 10. AYUDANTES DE CLASE, REGISTRO Y TIPO

• Los ayudantes de tipo sólo pueden descender de otros ayudantes de tipo, no de ayudantes de clase o de registro.

• Un ayudante de tipo descendiente debe extender el mismo tipo.

A continuación se da una idea de las posibilidades:

{$mode objfpc}
{$modeswitch typehelpers}

tipo
TLongIntHelper = ayuda de tipo para el constructor LongInt
create(AValue: LongInt); procedimiento de clase Prueba;
estático; procedimiento DoPrint; fin;

constructor [Link](AValue: LongInt);

comenzar
Self:=Evalue;
HacerImprimir;
fin;

procedimiento de clase [Link];

comenzar
Writeln('Prueba'); fin;

procedimiento [Link];

comenzar
Writeln('Valor:',Self);
fin;

era
i: Int Largo;
comienzo
I:=123; Yo
evalúo;
$[Link];
Prueba LongInt;
Yo:=123;
[Link];
$[Link]; fin.

10.5 Una nota sobre el alcance y la vida útil de los asistentes de registro y tipo

Para las clases, el programador gestiona explícitamente la vida útil de una instancia de una clase. Por tanto, queda
claro qué significa el parámetro Self y cuándo es válido.

Los registros y otros tipos simples se asignan en la pila, lo que significa que quedan fuera de alcance cuando sale la
función, procedimiento o método en el que se utilizan.

153
Machine Translated by Google

CAPÍTULO 10. AYUDANTES DE CLASE, REGISTRO Y TIPO

Combinado con el hecho de que los métodos auxiliares son compatibles con los tipos de métodos de clase y, por lo tanto, pueden
usarse como controladores de eventos, esto puede conducir a situaciones sorprendentes: el puntero de datos en un método
auxiliar se establece en la dirección de la variable.

Considere el siguiente ejemplo:

{$mode objfpc}
{$modeswitch typehelpers}
usos
Clases;

tipo
TInt32Helper = escriba ayuda para el procedimiento Int32
Foo(Sender: TObject); fin;

procedimiento [Link](Remitente: TObject); comenzar

Writeln(Self); fin;

era
yo: Int32 = 10; m:
TNotifyEvent; comenzar
m :=
@[Link];
WriteLn('Datos: ',PtrUInt(TMethod(m).Datos)); m(nulo);

fin.

Esto imprimirá algo como (el valor real de los datos puede diferir):

Datos : 6848896
10

La variable i todavía está dentro del alcance cuando se llama a m.

Pero cambiando el código a

{$mode objfpc}
{$modeswitch typehelpers}
usos
Clases;

tipo
TInt32Helper = escriba ayuda para el procedimiento Int32
Foo(Sender: TObject); fin;

procedimiento [Link](Remitente: TObject); comenzar

Writeln(Self); fin;

Función GetHandler :TNotifyEvent;

154
Machine Translated by Google

CAPÍTULO 10. AYUDANTES DE CLASE, REGISTRO Y TIPO

era
yo: Int32 = 10;

comenzar
Resultado:=@[Link]; fin;

Era
m: TNotifyEvent; comenzar
m :=
GetHandler;
WriteLn(PtrUInt(TMethod(m).Data)); m(nilo);

fin.

La salida será:

140727246638796
0

El resultado real dependerá de la arquitectura, pero el punto es que i ya no está dentro del alcance, lo que hace que
el resultado de su valor carezca de significado y posiblemente incluso provoque violaciones de acceso y fallas del
programa.

10.6 Herencia

Como se señaló en la sección anterior, es posible crear descendientes de clases auxiliares. Dado que solo se puede
utilizar la última clase auxiliar en el ámbito actual, es necesario descender una clase auxiliar de otra si se deben
utilizar métodos de ambos auxiliares. Más sobre esto en una sección posterior.

Un descendiente de un ayudante de clase puede extender una clase diferente a la de su padre. El siguiente es un
asistente de clase válido para TMyObject:

TObjectHelper = ayudante de clase para TObject


procedimiento Algún método; fin;

TMyObject = clase(TObject) fin;

TMyObjectHelper = ayudante de clase (TObjectHelper) para el procedimiento TMyObject


SomeOtherMethod; fin;

TMyObjectHelper extiende TObjectHelper, pero no extiende la clase TObject, solo extiende la clase TMyObject.

Dado que los registros no conocen herencia, es obvio que los descendientes de los ayudantes de registros sólo pueden
extender el mismo registro.

Observación Para lograr la máxima compatibilidad con Delphi, es imposible crear descendientes de asistentes de registros en Delphi.
modo.

155
Machine Translated by Google

CAPÍTULO 10. AYUDANTES DE CLASE, REGISTRO Y TIPO

10.7 Uso
Una vez que se define una clase auxiliar, sus métodos se pueden usar siempre que la clase auxiliar esté dentro del alcance.
Esto significa que si se define en una unidad separada, entonces esta unidad debe estar en la cláusula de usos dondequiera
que se utilicen los métodos de la clase auxiliar.

Considere la siguiente unidad:

{$modo objfpc} {$h+}


unidad
oha;

interfaz

Tipo
TObjectHelper = ayudante de clase para TObject
función AsString(const aFormat: String): String; fin;

implementación

utiliza sysutils;

función [Link] (const aFormat: String): String;

comenzar
Resultado := Formato(aFormat, [ToString]); fin;

fin.

Luego se compilará lo siguiente:

Ejemplo de programa 113;

usa oha;

{Programa para demostrar el alcance del asistente de clase. }

Era
o : TObjeto;

comenzar
O:=[Link];
Writeln([Link]('O como cadena: %s')); fin.

Pero, si se crea una segunda unidad (ohb):

{$modo objfpc} {$h+}


unidad
ohb;

interfaz

156
Machine Translated by Google

CAPÍTULO 10. AYUDANTES DE CLASE, REGISTRO Y TIPO

Tipo
TAObjectHelper = ayudante de clase para la función TObject
MemoryLocation: String; fin;

implementación

utiliza sysutils;

función [Link]: Cadena;

comenzar
Resultado := formato('%p',[puntero(Self)]); fin;

fin.

Y se agrega después de la primera unidad en la cláusula de usos:

Ejemplo de programa 113;

utiliza oha, ohb;

{Programa para demostrar el alcance del asistente de clase. }

Era
o : TObjeto;

comenzar
O:=[Link];
Writeln([Link]('O como cadena: %s')); Writeln([Link]);
fin.

Entonces el compilador se quejará de que no conoce el método "AsString". Esto se debe a que el compilador
deja de buscar ayudantes de clase tan pronto como encuentra el primer ayudante de clase. Dado que la unidad
ohb ocupa el último lugar en la cláusula de usos, el compilador solo utilizará TAObjectHelper como ayudante
de clase.
La solución es volver a implementar la unidad ohb:

{$modo objfpc} {$h+}


unidad
ohc;

interfaz

usa oha;

Tipo
TAObjectHelper = ayudante de clase (TObjectHelper) para TObject
función Ubicación de la memoria: Cadena; fin;

implementación

157
Machine Translated by Google

CAPÍTULO 10. AYUDANTES DE CLASE, REGISTRO Y TIPO

utiliza sysutils;

función [Link]: Cadena;

comenzar
Resultado := formato('%p',[puntero(Self)]); fin;

fin.

Y después de reemplazar la unidad ohb con ohc, el programa de ejemplo se compilará y funcionará como se esperaba.

Tenga en cuenta que no es suficiente incluir una unidad con un ayudante de clase una vez en un proyecto; La unidad debe
incluirse siempre que se necesite el ayudante de clase.

158
Machine Translated by Google

Capítulo 11

Clases objetivas­Pascal

11.1 Introducción

El lenguaje de programación preferido para acceder a los marcos del sistema Mac OS X es Objective­C. Para aprovechar
plenamente el potencial que ofrecen las interfaces del sistema escritas en ese lenguaje, existe una variante de Object Pascal
en el compilador Free Pascal que intenta ofrecer la misma funcionalidad que Objective C. Esta variante se llama Objective­
Pascal.

El compilador tiene conmutadores de modo para permitir el uso de estas construcciones relacionadas con Objective­C. Hay
dos tipos de características del lenguaje Objective­C, distinguidas por un número de versión: Objective­C 1.0 y Objective­C
2.0.

Las características del lenguaje Objective­C 1.0 se pueden habilitar agregando un interruptor de modo al archivo fuente:

{$cambio de modo objetivoc1}

o utilizando el modificador de línea de comando ­Mobjectivec1 del compilador.

Las funciones del lenguaje Objective­C 2.0 se pueden habilitar usando un interruptor de modo similar:

{$cambio de modo objetivoc2}

o la opción de línea de comando ­Mobjectivec2.

Las características del lenguaje Objective­C 2.0 son un superconjunto de las características del lenguaje Objective­C 1.0 y,
por lo tanto, el último cambio implica automáticamente el primero. Los programas que utilizan funciones del lenguaje
Objective­C 2.0 solo funcionarán en Mac OS X 10.5 y posteriores.

El hecho de que las funciones de Objective­C se habiliten mediante interruptores de modo en lugar de modos de sintaxis
reales, significa que se pueden usar en combinación con todos los modos de sintaxis generales (fpc, objfpc, tp, delphi, mac
pas). Tenga en cuenta que un interruptor de directiva {$Mode } restablecerá los interruptores de modo, por lo que la
instrucción {$modeswitch } debe ubicarse después de él.

11.2 Declaraciones de clase Objective­Pascal


Las clases Objective­C o ­Pascal se declaran de forma muy parecida a como se declaran las clases Object Pascal, pero
utilizan la palabra clave objcclass:

Tipos de clase objetiva C

159
Machine Translated by Google

CAPÍTULO 11. CLASES OBJETIVO­PASCAL

­­ clase de objeto ­
externo
constante de cadena de nombre
­ ­
herencia fin

6 especificación de

visibilidad de clase de lista de componentes

­­ herencia ( ) ­
identificador de tipo de clase objetivo­C protocolos implementados

­­ protocolos implementados 6 identificador de protocolo ­


,

­­ lista de componentes ­
especificador de visibilidad definición de campo
6

­ ­
6 clase declaración de variable parte
definición del método
definición de propiedad

­­ parte de declaración de variable de clase parte de declaración de variable de clase ­

­ lista de identi cadores de definición de campo : tipo ; ­


estático;

­­ definición del método encabezado de función


;­
clase encabezado del procedimiento

­ ­
;
anular la
constante de cadena de mensaje

­­ especi cador de visibilidad de clase ­


privado
público

estrictamente
protegido

Como puede verse, la sintaxis es aproximadamente equivalente a la sintaxis de Object Pascal, con algunas extensiones.

Para utilizar clases Objective­C, existe un modificador externo: esto indica al compilador que la clase
está implementada en un archivo o biblioteca objeto externo, y que la definición está destinada a fines
de importación. El siguiente es un ejemplo de una definición de clase Objective­C externa:

NSView = objcclass externo (NSResponder) privado _subview:


id; función
pública initWithFrame
(rect:
NSRect): id; mensaje 'initWithFrame:'; procedimiento
addSubview(aview: NSView);

160
Machine Translated by Google

CAPÍTULO 11. CLASES OBJETIVO­PASCAL

mensaje 'addSubview:'; procedimiento


setAutoresizingMask(máscara: NSUInteger);
mensaje 'setAutoresizingMask:';
procedimiento setAutoresizesSubviews(bandera: LongBool); mensaje
'setAutoresizesSubviews:'; procedimiento drawRect(dirtyRect:
NSRect);
mensaje 'drawRect:';
fin;

Como puede verse, la definición de clase no es tan diferente de una definición de clase de Object Pascal; Solo
la directiva de mensaje está presente de manera más destacada: cada método Objective­C u Objective­Pascal
debe tener un nombre de mensaje asociado. En el ejemplo anterior, no se especificó ningún nombre externo
para la definición de clase, lo que significa que el identificador Pascal se utiliza como nombre de la clase Objective­C.
Sin embargo, dado que Objective­C no es tan estricto en sus convenciones de nomenclatura, a veces se debe crear un
alias para un nombre de clase de Objective­C que no obedece las reglas del identificador de Pascal.

El siguiente ejemplo define una clase Objective­C que se implementa en Pascal:

MyView = objcclass(NSView) datos públicos:


entero;
procedimiento mensaje
personalizado (dirtyRect: NSRect);
mensaje 'mensaje personalizado';
procedimiento drawRect(dirtyRect: NSRect); anular; fin;

La ausencia de la palabra clave externa le dice al compilador que los métodos deben implementarse más adelante en el
archivo fuente: será tratado como una clase pascal de objeto normal. Tenga en cuenta la presencia de la directiva de
anulación: en Objective­C, todos los métodos son virtuales. En Object Pascal, la anulación de un método virtual debe
realizarse mediante la directiva override. Esto se ha extendido a las clases Objective­C: permite al compilador verificar la
exactitud de la definición.

A menos que la clase esté implementando el método de un protocolo (más sobre esto en una sección posterior), se
espera uno de mensaje o anulación: todos los métodos son virtuales y se inicia (o reintroduce) un nuevo método, o uno
existente. está anulado. Sólo en el caso de un método que forma parte de un protocolo, el método se puede definir sin
mensaje ni anulación.

Tenga en cuenta que la declaración de clase Objective­C puede especificar o no una clase principal. En Object Pas cal,
omitir una clase principal automáticamente hará que la nueva clase sea descendiente de TObject. En Objective­C, este
no es el caso: la nueva clase será una nueva clase raíz. Sin embargo, Objective­C sí tiene una clase que cumple la
función de clase raíz genérica: NSObject, que puede considerarse el equivalente de TObject en Object Pascal. Tiene
otras clases raíz, pero en general, las clases Objective Pascal deberían descender de NSObject. Si de todos modos se
construye una nueva clase raíz, debe implementar NSObjectProtocol, tal como lo hace la propia clase NSObject.

Finalmente, las clases de Objective­Pascal pueden tener propiedades, pero estas propiedades solo se pueden usar
en código Pascal: el compilador actualmente no exporta las propiedades de una manera que las haga utilizables
desde Objective­C.

11.3 Declaración formal

El objeto Pascal tiene el concepto de declaraciones directas. Objective­C lleva este concepto un poco más
allá: permite declarar una clase que está definida en otra unidad. Esto ha sido denominado “declaración
formal” en Objective­Pascal. Mirando el diagrama de sintaxis, la siguiente es una declaración válida:

MyExternalClass = objcclass externa;

161
Machine Translated by Google

CAPÍTULO 11. CLASES OBJETIVO­PASCAL

Esta es una declaración formal. Le dice al compilador que MyExternalClass es un tipo de clase Objective­C, pero
que no hay ninguna declaración de los miembros de la clase. El tipo se puede usar en el resto de la unidad, pero
su uso está restringido a la asignación de almacenamiento (en una definición de parámetro de método o campo) y
asignación (muy parecido a un puntero).

Tan pronto como se encuentra la definición de clase, el compilador puede imponer la compatibilidad de tipos.

La siguiente unidad utiliza una declaración formal:

unidad ContainerClass;

{$modo objfpc}
{$modeswitch objetivoc1}

interfaz

tipo
MyItemClass = objcclass externo;

MyContainerClass = objcclass elemento privado:


MyItemClass;
función pública getItem:

MyItemClass; mensaje 'obtenerArtículo'; fin;

implementación

función [Link]: MyItemClass; comenzar resultado:=elemento; //


La tarea
está bien. fin;

fin.

Una segunda unidad puede contener la declaración de clase real:

unidad ItemClass;

{$modo objfpc}
{$modeswitch objetivoc1}

interfaz

tipo
MyItemClass = objcclass(NSObject) contenido privado:
entero largo;
función pública initWithContent(c:
longint):
MyItemClass;
mensaje 'initWithContent:'; función getContent:
entero largo; mensaje 'obtenerContenido';

fin;

162
Machine Translated by Google

CAPÍTULO 11. CLASES OBJETIVO­PASCAL

implementación

función [Link](c: longint): MyItemClass; comenzar contenido:=c;


resultado:=yo; fin;

función [Link]: entero largo; comenzar

resultado:=contenido; fin;

fin.

Si se utilizan ambas unidades en un programa, el compilador sabe cuál es la clase y puede verificar la exactitud
de algunas asignaciones:

Prueba del programa;

{$modo objfpc}
{$modeswitch objetivoc1}

usos
Clase de artículo, clase de contenedor;

era
c: MiClaseContenedor; l: entero
largo; comenzar

c:=[Link]; l:=[Link]; fin.

11.4 Asignación y desasignación de instancias

El diagrama de sintaxis de las clases de Objective­C muestra que la noción de constructor y destructor no es
compatible con Objective­C. Las nuevas instancias se crean en un proceso de 2 pasos:

1. Llame al método "alloc" (envíe un mensaje "alloc"): este es un método de clase de NSObject y devuelve un puntero
a la memoria para la nueva instancia. El uso de alloc es una convención en Objective­C.

2. Envíe un mensaje "initXXX". Por convención, todas las clases tienen uno o más métodos "InitXXX" que inicializan
todos los campos de la instancia. Este método devolverá el puntero de instancia final, que puede ser Nil.

El siguiente código demuestra esto:

era
objeto: NSObject;
comenzar

163
Machine Translated by Google

CAPÍTULO 11. CLASES OBJETIVO­PASCAL

// Primero asigna la memoria.


obj:=[Link]; // A
continuación, inicializa.
obj:=[Link]; //
¡¡Comprueba siempre el resultado!! si
(Obj=Nil) entonces //
Algún error;

Por convención, el método initXXX devolverá Nil si falla la inicialización de algunos campos, por lo que
es imperativo que se pruebe el resultado de la función.
De manera similar, no existe ningún destructor privilegiado; Por convención, el método dealloc realiza la
limpieza de las instancias. Este método se puede anular para realizar cualquier limpieza necesaria. Al igual
que Destroy, nunca se debe llamar directamente; en su lugar, se debe llamar al método de liberación: todas
las instancias en Objective­C cuentan con referencias, y la liberación solo llamará a dealloc si el recuento de
referencias llega a cero.

11.5 Definiciones del protocolo

En Objective­C, los protocolos desempeñan el papel que desempeñan las interfaces en Object Pascal, pero existen
algunas diferencias:

• Los métodos de protocolo se pueden marcar como opcionales, es decir, la clase que implementa el protocolo puede decidir
no implementar estos métodos.

• Los protocolos pueden heredar de muchos otros protocolos.

Las clases de Objective­C pueden indicar qué protocolos implementan en la definición de clase, como se puede
ver en el diagrama de sintaxis de las clases de Objective­C.

El siguiente diagrama muestra cómo declarar un protocolo. Comienza con la palabra clave objcprotocol:

Tipo de protocolo

­­objcprotocolo _ ­
externo herencia
constante de cadena de nombre
­ ­
final de la lista de métodos de protocolo

)
­
­­ herencia ( 6 identificador de tipo de protocolo
,

­­ lista de métodos de protocolo 6 definición del método ­


requerido
opcional

Como en el caso de las clases Objective­Pascal, el especificador externo le dice al compilador que la declaración es una
importación de un protocolo definido en otro lugar. Para los métodos, se aplican casi las mismas reglas que para los
métodos en las declaraciones de clase Objective­Pascal. La excepción es que los especificadores de mensajes deben
estar presentes.

Los especificadores obligatorios y opcionales antes de una serie de declaraciones de métodos son opcionales.
Si no se especifica ninguno, se asume requerido. La siguiente es una definición de protocolo:

164
Machine Translated by Google

CAPÍTULO 11. CLASES OBJETIVO­PASCAL

tipo
MyProtocol = objccprotocol // por
defecto se requiere el
procedimiento aRequiredMethod;
mensaje 'un método requerido';
procedimiento
opcional anOptionalMethodWithPara(para: longint); mensaje 'un método
opcional con parámetro:';
procedimiento otro método opcional;
mensaje 'otroMétodoOpcional'; función requerida

aSecondRequiredMethod: longint; mensaje 'un segundo método


requerido'; fin;

MyClassImplementingProtocol = objcclass(NSObject,MyProtocol) procedimiento


aRequiredMethod; procedimiento
anOptionalMethodWithPara(para: longint); función unSegundoMétodoRequerido:
longint; fin;

Tenga en cuenta que en la declaración de clase, se omitió el especificador de mensaje. El compilador (y el tiempo de ejecución)
pueden deducirlo de la definición del protocolo.

11.6 Categorías
Al igual que los ayudantes de clase en Object Pascal, Objective­C tiene categorías. Las categorías permiten
ampliar clases sin crear realmente un descendiente de estas clases. Sin embargo, las categorías de Objective­
C proporcionan más funciones que un ayudante de clase:

1. En Object Pascal, solo una clase auxiliar puede estar dentro del alcance (la última). En Objective­C, múltiples
Las categorías pueden estar dentro del alcance al mismo tiempo para una clase en particular.

2. En Object Pascal, un método auxiliar no puede cambiar un método existente presente en la clase original
(pero puede ocultar un método). En Objective­C, una categoría también puede reemplazar métodos
existentes en otra clase en lugar de solo agregar nuevos. Dado que todos los métodos son virtuales en
Objective­C, esto también significa que este método cambia para todas las clases que heredan de la clase
en la que se reemplazó el método (a menos que lo anulen).

3. Los ayudantes de Object Pascal no se pueden utilizar para agregar interfaces a clases existentes. Por el contrario, un
La categoría Objective­C también puede implementar protocolos.

La definición de una clase Objective­C se parece mucho a una definición de protocolo y comienza con
la palabra clave objccategory:

tipo de categoría

­­ categoría de objeto ­
externo herencia
constante de cadena de nombre
­ fin de la lista de métodos de categoría ­

165
Machine Translated by Google

CAPÍTULO 11. CLASES OBJETIVO­PASCAL

­­herencia ( identificador de tipo de clase objetiva 6 identificador de tipo de protocolo ) ­


,

­­ lista de métodos de categoría 6 definición del método ­


reintroducir

Tenga en cuenta nuevamente la posibilidad de un alias para categorías definidas externamente: Objective­C
2.0 permite un nombre de categoría vacío. Tenga en cuenta que el modificador reintroducir debe usarse si se
reemplaza un método existente en lugar de agregar un nuevo método.
Al reemplazar un método, llamar a "heredado" no llamará al método original de la clase, sino que llamará a la
implementación del método de la clase principal.

El siguiente es un ejemplo de una definición de categoría:

MiProtocolo = protocoloobjc
método protocolo de procedimiento; mensaje 'método de protocolo'; fin;

MiCategoría = objetocategoría(NSObject,MiProtocolo)
hash de función: cuint; reintroducir; método protocolo de
procedimiento; // de MiProtocolo. procedimiento de clase nuevo método; mensaje
'nuevo método'; fin;

Tenga en cuenta que esta declaración reemplaza el método Hash de cada clase que desciende de NSObject (a
menos que lo anule específicamente).

11.7 Alcance del nombre e identificadores

En Object Pascal, cada identificador debe ser único en su espacio de nombres: la unidad. En Objective­C, este
no tiene por qué ser el caso y cada identificador de tipo debe ser único entre su tipo: clases, protocolos,
categorías, campos o métodos. Esto se muestra en las definiciones del protocolo básico y la clase de Objective­
C: tanto el protocolo como la clase se denominan NSObject.

Al importar clases y protocolos Objective­C, los nombres Objective­Pascal de estos tipos deben ajustarse a las
reglas de Object Pascal y, por lo tanto, deben tener nombres distintos. Asimismo, los nombres que son identificadores
válidos en Objective­C pueden ser palabras reservadas en Object Pascal. También se les debe cambiar el nombre
cuando se importen.

Para que esto sea posible, los modificadores Externo y "mensaje" permiten especificar un nombre: este es
el nombre del tipo o método tal como existe en Objective­C:

NSObjectProtocol = objcprotocol nombre externo función 'NSObject' _class: pobjc_class; nombre del
mensaje 'clase'; fin;

NSObject = objcclass función externa (NSObjectProtocol) _class: pobjc_class; función


de clase classClass: pobjc_class; mensaje 'clase';
fin;

166
Machine Translated by Google

CAPÍTULO 11. CLASES OBJETIVO­PASCAL

11.8 Selectores

Un Selector en Objective­C puede verse como un equivalente a un tipo de procedimiento en Object Pascal.

A diferencia del tipo procedimental, Objective­C tiene un solo tipo de selector: SEL. Se define en la unidad objc, que se incluye
automáticamente en la cláusula de usos de cualquier unidad compilada con el interruptor de modo Objectivec1.

Para asignar un valor a una variable de tipo SEL se debe utilizar el método objcselector:

{$cambio de modo objetivoc1}


era
a: SEL;
comenzar
a:=objcselector('initiWithWidth:andHeight:'); a:=objcselector('miMétodo'); fin.

La unidad objc contiene métodos para manipular y utilizar el selector.

11.9 El tipo de identificación

El tipo de identificación es especial en Objective­C/Pascal. Es muy parecido al tipo de puntero en Object Pascal, excepto que
es una clase real. Es compatible con asignaciones con instancias de cada tipo de objcclass y objcprotocol, en dos direcciones:

1. las variables de cualquier tipo objcclass/objcprotocol se pueden asignar a una variable del tipo
identificación.

2. las variables de tipo id se pueden asignar a variables de cualquier objcclass/objcprotocol en particular


tipo.

No se requiere encasillamiento explícito para ninguna de estas asignaciones.

Además, cualquier método de Objective­C declarado en una objcclass u objccategory que esté dentro del alcance se puede
llamar cuando se utiliza una variable de tipo id.

Si, en tiempo de ejecución, la instancia de objcclass real almacenada en la variable de tipo id no responde al mensaje enviado,
el programa terminará con un error de tiempo de ejecución: muy parecido al mecanismo de envío para variantes en MS­
Windows.

Cuando hay varios métodos con el mismo identificador Pascal, el compilador utilizará la lógica de resolución de sobrecarga
estándar para elegir el método más apropiado. En este proceso, se comportará como si todos los métodos objcclass/
objccategory dentro del alcance hubieran sido declarados como procedimientos/funciones globales con el especificador de
sobrecarga. Del mismo modo, el compilador imprimirá un error si no puede determinar qué método sobrecargado llamar.

En tales casos, se imprimirá como sugerencia una lista de todos los métodos que podrían usarse para implementar la llamada.

Para resolver el error, se debe utilizar una conversión de tipos explícita para indicarle al compilador qué tipo de objcclass
contiene el método necesario.

11.10 Enumeración en clases Objective­C

La enumeración rápida en Objective­C es una construcción que permite enumerar los elementos en una clase contenedora
Cocoa de forma genérica. Se implementa mediante un bucle for­in en Objective­C.

167
Machine Translated by Google

CAPÍTULO 11. CLASES OBJETIVO­PASCAL

Esto se ha traducido a Objective­Pascal utilizando el mecanismo de bucle for­in existente. Por tanto, la función
se comporta de forma idéntica en ambos idiomas. Tenga en cuenta que requiere que se active el interruptor
de modo Objective­C 2.0.

El siguiente es un ejemplo del uso de for­in:

{$modo delphi}
{$modeswitch objetivoc2}

usos
CacaoTodos;

era
arr: NSMutableArray; elemento:
NSString; grupo:
NSAutoreleasePool; i: entero largo;
comenzar

grupo:=[Link];
arr:=[Link](
NSSTR('Uno'),
NSSTR('Dos'),
NSSTR('Tres'),
NSSTR('Cuatro'),
NSSTR('Cinco'),
NSSTR('Seis'),
NSSTR('Siete'), nulo);

yo:=0;
para el elemento en arr comience
inc(i); si
i=2 entonces
continúa; si i = 5
entonces

romper;
si estoy en [2,5..10] entonces detener(1);

NSLog(NSSTR('elemento: %@'),elemento); fin; [Link]ón;


fin.

168
Machine Translated by Google

Capítulo 12

Expresiones

Las expresiones ocurren en declaraciones de tareas o en pruebas.

Las expresiones se construyen con dos componentes: operadores y sus operandos. La mayoría de los operadores son binarios,
es decir, requieren dos operandos. Los operadores binarios siempre ocurren entre los operandos (como en X/Y). Pocos
Los operadores son unarios, es decir, requieren sólo un operando. Un operador unario ocurre siempre antes del
operando, como en ­X.

Una expresión se resuelve en un valor de cierto tipo. El tipo resultante está determinado por los tipos.
de los valores en la expresión y los operadores en la expresión.

Cuando se utilizan múltiples operandos en una expresión, se utilizan las reglas de precedencia de la tabla (12.1) .

Tabla 12.1: Precedencia de operadores

Operador Precedencia Categoría


No, unario +, unario ­, @, ** *, /, div, Operadores unarios más altos (primeros), potencia
mod, y, shl, shr, as, <<, >> Segundo +, ­, o, xor, >< =, <>, < , >, <=, >=, Operadores multiplicadores
en, es Tercero Agregar operadores
Operadores relacionales más bajos (últimos)

Al determinar la precedencia, el compilador utiliza las siguientes reglas:

1. En operaciones con precedencias desiguales los operandos pertenecen al operador con mayor precedencia. Por
ejemplo, en 5*3+7, la multiplicación tiene mayor prioridad que la
además, por lo que se evalúa primero. El resultado sería 22.

2. Si se utilizan paréntesis en una expresión, primero se evalúa su contenido. Por lo tanto, 5*(3+7)
resultaría en 50.

3. De lo contrario, los operadores binarios de la misma precedencia son asociativos por izquierda. 5 * 3 div 7
evaluar a 2 y no a 0.

Observación No se garantiza que el orden en el que se evalúan las expresiones con la misma precedencia sea de izquierda a derecha. En
general, no se deben hacer suposiciones sobre cuál es la subexpresión que se evalúa primero en tal caso.
caso.

El compilador decidirá qué subexpresión evaluará primero en función de las reglas de optimización. Así, en
la siguiente expresión:

a := g(3) + f(2);

169
Machine Translated by Google

CAPITULO 12. EXPRESIONES

f(2) puede ejecutarse antes de g(3). Este comportamiento es claramente diferente del de Delphi o Turbo
Pascal.

Si una expresión debe ejecutarse antes que la otra, es necesario dividir la declaración utilizando resultados temporales:

e1 := g(3); a := e1
+ f(2);

Una excepción notable a este comportamiento es la evaluación de expresiones booleanas: si la evaluación


booleana de cortocircuito está habilitada (valor predeterminado), el compilador evaluará de izquierda a derecha,
pero aún respetará la precedencia, es decir, en partes con igual precedencia, el operando izquierdo. Siempre
será evaluado antes que el correcto. Así, el siguiente ejemplo:

Verdadero o Verdadero y Falso

se evaluará como Verdadero, porque es equivalente a

Verdadero o (Verdadero y Falso)

12.1 Sintaxis de expresión

Una expresión aplica operadores relacionales a expresiones simples. Las expresiones simples son una serie de términos
(a continuación se explica qué es un término), unidos mediante operadores de suma.

Expresiones

­­ expresión expresión simple ­


< expresión sencilla
<=
>
>=
=

<>
en
es

­
­­ expresión simple 6term
+
­
o
gratis

Las siguientes son expresiones válidas:

ResultadoGráfico<>grError
(Hacerlo hoy=Sí) y (Hacerlo mañana=No);
Día en fin de semana

Y aquí hay algunas expresiones simples:

170
Machine Translated by Google

CAPITULO 12. EXPRESIONES

A+B
­Pi
Ser o no ser

Los términos constan de factores, conectados por operadores de multiplicación.

Términos

­­ término 6factor ­
*
/
div
mod
y shl

shr
como

Aquí hay algunos términos válidos:

2 * pi
Una división B

(Hacerlo hoy=Sí) y (Hacerlo mañana=No);

Los factores son todas las demás construcciones:

Factores

­­ factor ( expresión ) ­
referencia variable
Llamada de función

constante sin signo no


factor signo
factor conjunto
constructor
valor factor de
dirección encasillado

­­ constante sin signo número sin signo cadena ­


de caracteres
identificador constante
Nulo

12.2 Llamadas a funciones

Las llamadas a funciones son parte de expresiones (aunque, usando sintaxis extendida, también pueden ser declaraciones).
Se construyen de la siguiente manera:

171
Machine Translated by Google

CAPITULO 12. EXPRESIONES

Llamadas a funciones

­­ llamada de función identificador de ­


función designador de lista de parámetros reales
método designador de método
calificado referencia de variable

­­ lista de parámetros reales ( ) ­


6 expresión
,

La referencia de variable debe ser una referencia de variable de tipo procesal. Un designador de método sólo
se puede utilizar dentro del método de un objeto. También se puede utilizar un designador de método calificado
fuera de los métodos de objeto. La función que se llamará es la función con una lista de parámetros declarada
que coincide con la lista de parámetros real. Esto significa que

1. La cantidad de parámetros reales debe ser igual a la cantidad de parámetros declarados (a menos que se utilicen valores
de parámetros predeterminados).

2. Los tipos de parámetros deben ser compatibles. Para parámetros de referencia variables, los tipos de
parámetros deben ser exactamente los mismos.

Si no se encuentra ninguna función coincidente, el compilador generará un error. El error depende, entre otras
cosas, de si la función está sobrecargada o no: es decir, varias funciones con el mismo nombre pero con
diferentes listas de parámetros.

Hay casos en los que el compilador no ejecuta la llamada a la función en una expresión. Este es el caso
cuando se asigna un valor a una variable de tipo procedimental, como en el siguiente ejemplo en modo
Delphi o Turbo Pascal:

Tipo
FuncType = Función: Entero; Var A: Entero;
Función AddOne: Entero;
comenzar A := A+1; AgregarUno := A; fin;
Var F:
Tipo de
función; norte:

número entero; comenzar


A := 0; F :=

AgregarUno;
{Asignar AddOne a F, no llamar a AddOne}
N := AgregarUno; { N := 1 !!} fin.

En el listado anterior, la asignación a F no hará que se llame a la función AddOne. La asignación a N, sin
embargo, llamará a AddOne.
A veces, la llamada es deseada, por ejemplo en recursividad, en ese caso, la llamada debe ser forzada. Esto se
puede hacer agregando el paréntesis al nombre de la función:

función rd: carbón;

172
Machine Translated by Google

CAPITULO 12. EXPRESIONES

era
c: carbón;

comenzar
a leer (c); si
(c='\') entonces c:=rd();
rd:=c; fin;

donde ch: char;

comenzar
canal:=rd;
escrito(ch);
fin.

Lo anterior leerá un carácter y lo imprimirá. Si la entrada es una barra invertida, se lee un segundo carácter.

Un problema con esta sintaxis es la siguiente construcción:

Si F = AgregarUno Entonces

hacer algo horrible;

¿Debería el compilador comparar las direcciones de F y AddOne, o debería llamar a ambas funciones y comparar el
resultado? En modo fpc y objfpc esto se resuelve considerando una variable de procedimiento como equivalente a un puntero.
Por lo tanto, el compilador dará un error de discrepancia de tipos, ya que AddOne se considera una llamada a una función
con resultado entero y F es un puntero.

Entonces, ¿cómo se debería comprobar si F apunta a la función AddOne? Para hacer esto, se debe usar el operador de
dirección @:

Si F = @AddOne Entonces
WriteLn ('Las funciones son iguales');

El lado izquierdo de la expresión booleana es una dirección. El lado derecho también, por lo que el compilador compara dos
direcciones. ¿Cómo comparar los valores que devuelven ambas funciones? Agregando una lista de parámetros vacía:

Si F()=Agregar entonces
WriteLn ('Las funciones devuelven los mismos valores');

Tenga en cuenta que este último comportamiento no es compatible con la sintaxis de Delphi. Activar el modo
Delphi le permitirá utilizar la sintaxis de Delphi.

12.3 Constructores de conjuntos

Cuando se debe ingresar una constante de tipo conjunto en una expresión, se debe proporcionar un constructor de
conjunto. En esencia, esto es lo mismo que cuando se define un tipo, sólo que no hay ningún identificador con el que
identificar el conjunto. Un constructor de conjuntos es una lista de expresiones separadas por comas, entre corchetes.

Constructores de conjuntos

173
Machine Translated by Google

CAPITULO 12. EXPRESIONES

­­ establecer constructor [ ] ­
6 establecer grupo

­­ establecer expresión de grupo ­


.. expresión

Todos los grupos de conjuntos y elementos de conjuntos deben ser del mismo tipo ordinal. El conjunto vacío se indica con [] y se
puede asignar a cualquier tipo de conjunto. Un grupo de conjuntos con un rango [A..Z] convierte todos los valores del rango en
un elemento de conjunto. Los siguientes son constructores de conjuntos válidos:

[hoy mañana]
[Lunes..Viernes,Domingo] [ 2, 3*2,
6*2, 9*2 ]
['A'..'Z','a'..'z', '0'..'9']

Observación Si el primer especificador de rango tiene un valor ordinal mayor que el segundo, el conjunto resultante
estará vacío, por ejemplo, ['Z'...'A'] denota un conjunto vacío. Hay que tener cuidado al indicar un rango.

12.4 Encasillamientos de valores

A veces es necesario cambiar el tipo de una expresión, o parte de la expresión, para que la asignación sea
compatible. Esto se hace mediante un encasillamiento de valores. El diagrama de sintaxis para un encasillado
de valores es el siguiente:

Encasillamientos

­­ identificador de tipo de encasillamiento de valor ( expresión ) ­

Los encasillamientos de valores no se pueden utilizar en el lado izquierdo de las asignaciones, como encasillamientos de variables. Aquí hay algunos

encasillamientos válidos:

Byte('A')
Char(48)
booleano(1)
entero largo(@Buffer)

En general, el tamaño de tipo de la expresión y el tamaño de la conversión de tipo deben ser los mismos. Sin
embargo, para los tipos ordinales (byte, char, palabra, booleano, enumera) esto no es así, se pueden usar indistintamente.
Es decir, lo siguiente funcionará, aunque las tallas no coincidan.

Entero('A');
Carbón (4875);
booleano(100);
Palabra (@Buffer);

Esto es compatible con el comportamiento de Delphi o Turbo Pascal.

174
Machine Translated by Google

CAPITULO 12. EXPRESIONES

12.5 Encasillamientos de variables

Una variable puede considerarse un factor único en una expresión. Por lo tanto, también se puede encasillar. Una
variable se puede encasillar a cualquier tipo, siempre que el tipo tenga el mismo tamaño que la variable original.

Es una mala idea encasillar tipos enteros en tipos reales y viceversa. Es mejor confiar en la compatibilidad de
asignación de tipos y utilizar algunas de las funciones estándar de cambio de tipos.

Tenga en cuenta que las encasillamientos de variables pueden ocurrir en cualquier lado de una asignación, es decir, las siguientes son
encasillamientos válidos:

Era
C: carbón;
B: Byte;

comenzar
B:=Byte(C);
Carbón (B): = C;
fin;

Las variables de puntero se pueden encasillar en tipos de procedimiento, pero no en punteros de método.

Un encasillamiento es una expresión del tipo dado, lo que significa que el encasillamiento puede ir seguido de un calificador:

Tipo
TWordRec = Registro empaquetado

L,H: Byte; fin;

Era
P: Puntero;
W: Palabra;
S: Cadena;

comenzar
TWordRec(W).L:=$FF;
TPalabraRec(W).H:=0;
S:=TObject(P).ClassName;

12.6 Encasillamientos no alineados

Un encasillamiento especial es el encasillamiento no alineado de una variable o expresión. Esto no es un encasillamiento


real, sino más bien una pista para el compilador de que la expresión puede estar desalineada (es decir, no en una
dirección de memoria alineada). Algunos procesadores no permiten el acceso directo a estructuras de datos desalineadas
y, por lo tanto, deben acceder a los datos byte por byte.

Encasillar una expresión con la palabra clave no alineada le indica al compilador que debe acceder a los datos byte
por byte.

Tenga en cuenta que el compilador supone que el acceso a todos los campos/elementos de las estructuras de datos empaquetadas no
está alineado.

Ejemplo:

programarme;

175
Machine Translated by Google

CAPITULO 12. EXPRESIONES

Era
A: Matriz empaquetada [1..20] de Byte; I: Int Largo;

comenzar
Para I:=1 a 20 hacer
A[Yo]:=Yo;
I:=PInteger(No alineado(@A[13]))^; fin.

12.7 El operador @
El operador de dirección @ devuelve la dirección de una variable, procedimiento o función. Se utiliza de la siguiente manera:

factor de dirección

­­ factor de dirección @ identificador de procedimiento de ­


referencia variable
identificador de
función identificador de método calificado

El operador @ devuelve un puntero escrito si el modificador $T está activado. Si el interruptor $T está


desactivado, el operador de dirección devuelve un puntero sin tipo, cuya asignación es compatible con todos
los tipos de puntero. El tipo de puntero es ˆT, donde T es el tipo de referencia de la variable. Por ejemplo, lo
siguiente compilará

Transmisión del programa;


{$T­} { @ devuelve un puntero sin tipo }

Tipo art = Matriz[1..100] de byte; Búfer Var: entero largo;

PLargeBuffer: ^arte;

comenzar
PLargeBuffer := @Buffer; fin.

Cambiar {$T­} a {$T+} evitará que el compilador compile esto. Dará un error de no coincidencia de tipos.

De forma predeterminada, el operador de dirección devuelve un puntero sin tipo: aplicar el operador de dirección
a un identificador de función, método o procedimiento dará un puntero al punto de entrada de esa función. El
resultado es un puntero sin tipo.

Esto significa que lo siguiente funcionará:

Procedimiento MyProc;

empezar
fin;

176
Machine Translated by Google

CAPITULO 12. EXPRESIONES

Era
P: PCar;

comenzar
P:=@MiProc; fin;

De forma predeterminada, se debe utilizar el operador de dirección si se debe asignar un valor a una variable de tipo
procesal. Este comportamiento se puede evitar utilizando los modificadores ­Mtp o ­MDelphi, que dan como resultado
una sintaxis de Delphi o Turbo Pascal más compatible.

12.8 Operadores

Los operadores se pueden clasificar según el tipo de expresión sobre la que operan. Los discutiremos tipo por tipo.

12.8.1 Operadores aritméticos


Los operadores aritméticos ocurren en operaciones aritméticas, es decir, en expresiones que contienen números enteros o reales.
Hay dos tipos de operadores: operadores aritméticos binarios y unarios. Los operadores binarios se enumeran en la
tabla (12.2), los operadores unarios se enumeran en la tabla (12.3).

Tabla 12.2: Operadores aritméticos binarios

Operación del operador


+ Suma
­
Sustracción
* Multiplicación
** exponenciación
/ División
división División entera
Contra Resto

Con la excepción de Div y Mod, que aceptan sólo expresiones enteras como operandos, todos los operadores
aceptan expresiones reales y enteras como operandos.

Observación El operador de exponenciación (**) está disponible para sobrecarga (capítulo 15, página 233), pero no está
definido en ninguno de los tipos estándar de Pascal (flotantes y/o enteros).

Para los operadores binarios, el tipo de resultado será un número entero si ambos operandos son expresiones de tipo entero.
Si uno de los operandos es una expresión de tipo real, entonces el resultado es real.

Como excepción, la división (/) siempre da como resultado valores reales.

Tabla 12.3: Operadores aritméticos unarios

Operación del operador


+ Firma de identidad
­
Inversión de signos

177
Machine Translated by Google

CAPITULO 12. EXPRESIONES

Para operadores unarios, el tipo de resultado siempre es igual al tipo de expresión. La división (/) y Mod
El operador causará errores en tiempo de ejecución si el segundo argumento es cero.

El signo del resultado de un operador Mod es el mismo que el signo del operando del lado izquierdo del Mod.
operador. De hecho, el operador Mod equivale a la siguiente operación:

I mod J = I ­ (I div J) * J

Pero se ejecuta más rápido que la expresión del lado derecho.

12.8.2 Operadores lógicos


Los operadores lógicos actúan sobre los bits individuales de las expresiones ordinales. Los operadores lógicos requieren operandos
que son de tipo entero y producen un resultado de tipo entero. Se enumeran los posibles operadores lógicos.
en la tabla (12.4).

Tabla 12.4: Operadores lógicos

Operación del operador


no Negación bit a bit (unaria)
y Bit a bit y
o Bit a bit o
gratis xor bit a bit
shl Desplazamiento bit a bit hacia la izquierda

shr Desplazamiento bit a bit hacia la derecha


<< Desplazamiento bit a bit hacia la izquierda (igual que shl)
>> Desplazamiento bit a bit hacia la derecha (igual que shr)

Las siguientes son expresiones lógicas válidas:

A shr 1 {igual que A div 2, pero más rápido}


no 1 {es igual a ­2}
No 0 {es igual a ­1}
No ­1 {es igual a 0}
B shl 2 {igual que B * 4 para números enteros}
1 o 2 {es igual a 3}
3 x o 1 {es igual a 2}

12.8.3 Operadores booleanos


Los operadores booleanos pueden considerarse operaciones lógicas en un tipo con un tamaño de 1 bit. Por lo tanto, el
Las operaciones shl y shr tienen poco sentido. Los operadores booleanos sólo pueden tener operandos de tipo booleano,
y el tipo resultante siempre es booleano. Los posibles operadores se enumeran en la tabla (12.5).

Tabla 12.5: Operadores booleanos

Operación del operador


no negación lógica (unaria)
y lógico y
o lógico o
gratis xor lógico

178
Machine Translated by Google

CAPITULO 12. EXPRESIONES

Observación De forma predeterminada, las expresiones booleanas se evalúan con evaluación de cortocircuito. Esto significa
que desde el momento en que se conoce el resultado de la expresión completa, se detiene la evaluación y se
devuelve el resultado. Por ejemplo, en la siguiente expresión:

B := Verdadero o Quizás Verdadero;

El compilador nunca mirará el valor de MaybeTrue, ya que es obvio que la expresión siempre será True. Como
resultado de esta estrategia, si MaybeTrue es una función, ¡no será llamada!
(Esto puede tener efectos sorprendentes cuando se usa junto con propiedades)

12.8.4 Operadores de cadena


Sólo hay un operador de cadena: +. Su acción es concatenar el contenido de las dos cadenas (o caracteres)
sobre las que actúa. No se puede utilizar + para concatenar cadenas terminadas en nulo (PChar). Las
siguientes son operaciones de cadena válidas:

'Esto es ' + 'MUY ' + '¡fácil!'


Dirección+'\'

Lo siguiente no es:

Era
Nombre de dirección: PChar;
...
Dirección := Dirección+'\';

Porque Dirname es una cadena terminada en nulo.


Tenga en cuenta que si todas las cadenas de una expresión de cadena son cadenas cortas, la cadena resultante también será
una cadena corta. Por lo tanto, puede ocurrir un truncamiento: no hay un escalado automático a ansistring.

Si todas las cadenas en una expresión de cadena son ansistrings, entonces el resultado es una ansistring.

Si la expresión contiene una combinación de ansistrings y shortstrings, el resultado es un ansistring.

El valor del modificador {$H} se puede utilizar para controlar el tipo de cadenas constantes; de forma predeterminada, son
cadenas cortas (y por lo tanto están limitadas a 255 caracteres).

12.8.5 Operadores de matriz dinámica


Sólo hay un operador de matriz dinámica: +. Este operador está disponible en modo Delphi, pero debe habilitarse
explícitamente usando los operadores de matriz de interruptor de modo en modo objfpc:

{$mode objfpc}
{$modeswitch arrayoperators}

Cuando está habilitado, su acción es similar a la concación de cadenas: concatenar el contenido de las dos matrices
sobre las que actúa. Por supuesto, el tipo de elemento de la matriz debe ser idéntico para ambas matrices, es decir,
lo siguiente funcionará:

{$mode objfpc}
{$modeswitch arrayoperators}
era
a,b, c: matriz de bytes;

179
Machine Translated by Google

CAPITULO 12. EXPRESIONES

comenzar a:=[0,1,2];
b:=[3,4,5]; c:=a+b;
writeln('C
tiene ',longitud(c),' elementos');

Pero lo siguiente no funcionará:

{$mode objfpc}
{$modeswitch arrayoperators}

era
b, c: matriz de bytes; a: matriz de
números enteros;

comenzar a:=[0,1,2];
b:=[3,4,5]; c:=a+b;
writeln('C
tiene ',longitud(c),' elementos');

El compilador dará un error al compilar este código.

12.8.6 Establecer operadores

Con los operadores se pueden realizar las siguientes operaciones sobre conjuntos: unión, diferencia, diferencia
simétrica, inclusión e intersección. Se pueden agregar o eliminar elementos del conjunto con los operadores
Incluir o Excluir. Los operadores necesarios para esto se enumeran en la tabla (12.6).

Tabla 12.6: Operadores de conjunto

Operador Acción
+ Unión
­
Diferencia
* Intersección
>< diferencia simétrica
<= Contiene
>= El conjunto del lado izquierdo es un superconjunto del de la derecha.
incluir incluir un elemento en el conjunto excluir
excluir un elemento del conjunto verificar si un elemento
en está en un conjunto

El tipo establecido de los operandos debe ser el mismo o el compilador generará un error.

El siguiente programa ofrece algunos ejemplos válidos de operaciones con conjuntos:

Tipo
Día = (lunes, martes, miércoles, jueves, viernes, sábado, domingo);
Días = conjunto de Día;

Procedimiento PrintDays(W: Días);


constante

180
Machine Translated by Google

CAPITULO 12. EXPRESIONES

DayNames: matriz [Día] de String[3] =


('lunes','martes','miércoles','jueves',
'viernes', 'sábados', 'domingos');
Era
D: Día;
S: Cadena;
comenzar
S:='';
Para D:=lunes a domingos
si D en W entonces
comenzar
Si (S<>'') entonces S:=S+',';
S:=S+NombresDía[D]; fin;

Writeln('[',S,']'); fin;

constante
Semana laboral = [lunes, martes, miércoles, jueves, viernes];
Fin de semana = [sábado,domingo];

Era
W: Días;

comenzar
Mi:=[lunes,martes]+[miércoles,jueves,viernes]; // es igual a [lun,mar,mié,jue,vier]
Díasdeimpresión(W);
Mi:=[lunes,martes,miércoles]­[miércoles]; // es igual a [lunes,martes]
Díasdeimpresión(W);
Mi:=[lunes,martes,miércoles]­[miércoles,jueves]; // también es igual a [lunes,martes]
Díasdeimpresión(W);
Mi:=[lunes,martes,miércoles]*[miércoles,jueves,viernes]; // es igual a [casarse]
Díasdeimpresión(W);
Mi:=[lunes,martes,miércoles]><[miércoles,jueves,viernes]; // es igual a [lun,mar,jue,vier]
Díasdeimpresión(W);
si [lunes, martes]<=Semana laboral entonces
Writeln('Debe trabajar los lunes y martes');
si fin de semana>=[domingo] entonces
Writeln('Puedo descansar el domingo');
fin.

Como se puede observar, la unión equivale a un OR binario, mientras que la intersección equivale a un AND
binario, y la diferencia simétrica equivale a una operación XOR.

Las operaciones Incluir y Excluir equivalen a una unión o diferencia con un conjunto de un elemento. De este modo,

Incluir(W,mié);

es equivalente a

W:=W+[casar];

181
Machine Translated by Google

CAPITULO 12. EXPRESIONES

Excluir(W,mié);

es equivalente a

W:=W­[casar];

La operación In da como resultado Verdadero si el operando izquierdo (un elemento) está incluido en el operando derecho
(un conjunto); en caso contrario, el resultado será Falso.

12.8.7 Operadores relacionales Los operadores

relacionales se enumeran en la tabla (12.7)

Tabla 12.7: Operadores relacionales

Acción del operador


=
Igual
<> No es igual
< Estrictamente menos que
> Estrictamente mayor que
<= Menor o igual
>= Mayor que o igual
en Elemento de

Normalmente, los operandos izquierdo y derecho deben ser del mismo tipo. Hay algunas excepciones notables en las que el
compilador puede manejar expresiones mixtas:

1. Los tipos enteros y reales se pueden mezclar en expresiones relacionales.

2. Si el operador está sobrecargado y existe una versión sobrecargada cuyos tipos de argumentos coinciden con los tipos
de la expresión.

3. Se pueden mezclar tipos de cuerda corta, Ansi y ancha.

La comparación de cadenas se realiza en función de su representación de código de caracteres.

Al comparar punteros, se comparan las direcciones a las que apuntan. Esto también es válido para los punteros de tipo
PChar. Para comparar las cadenas a las que apunta PChar, se debe utilizar la función StrComp de la unidad de cadenas. In
devuelve True si el operando izquierdo (que debe tener el mismo tipo ordinal que el tipo de conjunto y que debe estar en el
rango 0..255) es un elemento del conjunto que es el operando derecho; de lo contrario, devuelve False.

12.8.8 Operadores de clase


Los operadores de clase son ligeramente diferentes de los operadores anteriores en el sentido de que sólo pueden usarse en
expresiones de clase que devuelven una clase. Sólo existen dos operadores de clase, como se puede ver en la tabla (12.8).

Tabla 12.8: Operadores de clase

Acción del operador


es Comprueba el tipo de clase
como Encasillamiento condicional

182
Machine Translated by Google

CAPITULO 12. EXPRESIONES

Una expresión que contiene el operador is da como resultado un tipo booleano. El operador is solo se puede
utilizar con una referencia de clase o una instancia de clase. El uso de este operador es el siguiente:

El objeto es clase

Esta expresión es completamente equivalente a

[Link](Clase)

Si el objeto es nulo, se devolverá Falso.

Los siguientes son ejemplos:

Era
A: TObjeto;
B: Clase T;

comenzar
si A es TComponent entonces;
Si A es B entonces; fin;

El operador as realiza un encasillamiento condicional. Resulta en una expresión que tiene el tipo de clase:

Objeto como clase

Esto equivale a las siguientes afirmaciones:

Si Objeto = Nil entonces


Resultado:=Nulo
de lo contrario, si el objeto es clase, entonces
Resultado:=Clase(Objeto) más

Generar excepció[Link](SErrInvalidTypeCast);

Tenga en cuenta que si el objeto es nulo, el operador as no genera una excepción.

Los siguientes son algunos ejemplos del uso del operador as:

Era
C: Componente T;
O : TObjeto;

comenzar
(C como TEdit).Text:='Algún texto'; C:=O como
TComponente; fin;

Los operadores as e is también trabajan en interfaces (tanto COM como CORBA). Se pueden utilizar para
comprobar si una interfaz también implementa otra interfaz como en el siguiente ejemplo:

{$modo objfpc}

usos

183
Machine Translated by Google

CAPITULO 12. EXPRESIONES

SysUtils;

tipo
IMyInterface1 = interfaz
['{DD70E7BB­51E8­45C3­8CE8­5F5188E19255}'] barra de
procedimiento; fin;

IMyInterface2 = interfaz
['{7E5B86C4­4BC5­40E6­A0DF­D27DBF77BCA0}'] procedimiento
Foo; fin;

TMyObject = clase (TInterfacedObject, IMyInterface1, IMyInterface2) barra de procedimientos; procedimiento


Foo; fin;

procedimiento [Link];
comenzar

fin;

procedimiento [Link];
comenzar

fin;

era
i: IMyInterface1; comenzar
i :=
[Link]; [Link];

Writeln(BoolToStr(i es IMyInterface2, Verdadero)); // imprime verdadero Writeln(BoolToStr(i es


IDispatch, True)); // imprime falso (i como IMyInterface2).Foo; fin.

Además, el operador is se puede utilizar para comprobar si una clase implementa una interfaz, y el
operador as se puede utilizar para encasillar una interfaz en la clase:

{$modo objfpc}
era
i: IMyInterface; comenzar
i :=
[Link]; Writeln(BoolToStr(i
es TMyObject,True)); // imprime verdadero Writeln(BoolToStr(i es TObject,True)); // imprime
verdadero Writeln(BoolToStr(i es TAggregatedObject,True)); // imprime falso (i como
TMyObject).Foo; fin.

Aunque las interfaces deben ser interfaces COM, la conversión encasillada a una clase solo funcionará
si la interfaz proviene de una clase Object Pascal. No funcionará en interfaces obtenidas del sistema.

184
Machine Translated by Google

CAPITULO 12. EXPRESIONES

por COM.

185
Machine Translated by Google

Capítulo 13

Declaraciones

El corazón de cada algoritmo son las acciones que realiza. Estas acciones están contenidas en las declaraciones
de un programa o unidad. Cada declaración se puede etiquetar y saltar (dentro de ciertos límites) con
declaraciones Goto. Esto se puede ver en el siguiente diagrama de sintaxis:

Declaraciones

­­ declaración ­
etiqueta : declaración simple
declaración estructurada
declaración de ensamblaje

Una etiqueta puede ser un identificador o un dígito entero.

13.1 Declaraciones simples

Una declaración simple no se puede descomponer en declaraciones separadas. Básicamente existen cuatro tipos de
declaraciones simples:

Declaraciones simples

­­ declaración simple declaración de asignación ­


declaración de
procedimiento
declaración ir a declaración elevar

De estas declaraciones, la declaración de aumento se explicará en el capítulo de Excepciones (capítulo 17,


página 257)

13.1.1 Asignaciones Las

asignaciones dan un valor a una variable, reemplazando cualquier valor anterior que la variable pudiera haber tenido:

186
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

Asignaciones

­­ identificador de función de referencia de variable de := ­


expresión
declaración de asignación +=
­=
*=
/=

Además del operador de asignación estándar de Pascal (:=), que simplemente reemplaza el valor de la variable con el
valor resultante de la expresión a la derecha del operador :=, Free Pascal admite algunas construcciones de estilo C.
Todas las construcciones disponibles se enumeran en la tabla (13.1).

Tabla 13.1: Construcciones C permitidas en Free Pascal

Tarea a += b Resultado

Agrega b a a y almacena el resultado en a.


a­= b Resta b de a y almacena el resultado en a.
a *= b Multiplica a por by almacena el resultado en a.
a/= b Divide de a a b y almacena el resultado en a.

Para que estas construcciones funcionen, se debe especificar el modificador de línea de comandos ­Sc.

Observación Estas construcciones son sólo para facilitar la escritura, no generan código diferente. Aquí están
Algunos ejemplos de declaraciones de asignación válidas:

X := X+Y;
X+=Y; {Igual que X:= X+Y, necesita el cambio de línea de comando ­Sc}
X/=2; {Igual que X:= X/2, necesita el cambio de línea de comando ­Sc}
Hecho := Falso;
Clima := Bueno;
MiPi := 4* Bronceado(1);

Teniendo en cuenta que la desreferenciación de un puntero escrito da como resultado una variable del tipo al que apunta
el puntero, las siguientes también son asignaciones válidas:

Era
L : ^Entero largo;
P: PPChar;

comenzar
L^:=3;
P^^:='A';

Nótese la doble desreferenciación en la segunda tarea.

13.1.2 Declaraciones de procedimiento

Las declaraciones de procedimiento son llamadas a subrutinas. Existen diferentes posibilidades para llamadas a procedimientos:

• Una llamada a procedimiento normal.

187
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

• Una llamada a un método de objeto (totalmente calificado o no).

• O incluso una llamada a una variable de tipo procedimental.

Todos los tipos están presentes en el siguiente diagrama:

Declaraciones de procedimiento

­­ declaración de procedimiento identificador de ­


procedimiento lista de parámetros reales
identificador de método identificador
de método calificado referencia variable

El compilador de Free Pascal buscará un procedimiento con el mismo nombre que el indicado en la declaración del
procedimiento y con una lista de parámetros declarada que coincida con la lista de parámetros real. Las siguientes son
declaraciones de procedimiento válidas:

Uso;
WriteLn('¡Pascal es un lenguaje fácil!'); Hazlo();

Observación Cuando se busca una función que coincida con la lista de parámetros de la llamada, los tipos de parámetros deben
ser compatibles con la asignación para los parámetros value y const, y deben coincidir exactamente con los
parámetros que se pasan por referencia.

13.1.3 Ir a declaraciones
Free Pascal admite la declaración goto jump. Su sintaxis prototipo es

Ir a declaración

­­ ir a declaración ir a etiqueta ­

Al utilizar declaraciones goto, se debe tener en cuenta lo siguiente:

1. La etiqueta de salto debe definirse en el mismo bloque que la declaración Goto.

2. Saltar desde fuera de un bucle hacia dentro de un bucle o viceversa puede tener efectos extraños.

3. Para poder utilizar la instrucción Goto, se debe utilizar el modificador del compilador ­Sg, o {$GOTO
ON} debe usarse.

Observación En modo iso o macpas, o con el interruptor de modo “nonlocalgoto”, el compilador también permitirá no
gotos locales.

Las declaraciones Goto se consideran una mala práctica y deben evitarse en la medida de lo posible. Siempre es posible
reemplazar una instrucción goto por una construcción que no necesita una instrucción goto, aunque esta construcción
puede no ser tan clara como una instrucción goto. Por ejemplo, la siguiente es una declaración goto permitida:

188
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

etiqueta

saltar
...
Salta a :
Declaración;
...
Ir a saltar a;
...

13.2 Declaraciones estructuradas

Las declaraciones estructuradas se pueden dividir en declaraciones simples más pequeñas, que deben ejecutarse
de forma repetida, condicional o secuencial:

Declaraciones estructuradas

­­ declaración estructurada declaración compuesta ­


declaración condicional
declaración repetitiva
con declaración
declaración de excepción

Las declaraciones condicionales vienen en dos tipos:

Declaraciones condicionales

­­ declaración condicional declaración de caso ­


si declaración

Las declaraciones repetitivas vienen en cuatro tipos:

Declaraciones repetitivas

­­ declaración repetitiva para declaración ­


para en declaración
repetir declaración
mientras declaración

Las siguientes secciones tratan de cada una de estas afirmaciones.

13.2.1 Declaraciones compuestas


Las declaraciones compuestas son un grupo de declaraciones, separadas por punto y coma,
rodeadas por las palabras clave Inicio y Fin. La última declaración (antes de la palabra clave Fin) no necesita

189
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

irá seguido de un punto y coma, aunque está permitido. Una declaración compuesta es una forma de
agrupar declaraciones, ejecutándolas secuencialmente. Se tratan como una sola declaración en los
casos en que la sintaxis de Pascal espera una declaración, como en las declaraciones if...then...else.

Declaraciones compuestas

­­ declaración compuesta comienza 6 declaración fin ­


;

13.2.2 La declaración del caso


Free Pascal apoya la declaración del caso. Su diagrama de sintaxis es

Declaración del caso

­­ declaración de caso expresión de caso de 6 caso fin ­


; otra fiesta ;

­­ caso 6
constante : declaración ­
.. constante
,

­­ otra fiesta demás lista de declaraciones ­


de lo contrario

Las constantes que aparecen en las distintas partes del caso deben conocerse en tiempo de compilación y pueden
ser de los siguientes tipos: tipos de enumeración, tipos ordinales (incluidos caracteres) o tipos de cadena. La expresión
case también debe ser de este tipo o se producirá un error del compilador. Todas las constantes de caso deben ser
del mismo tipo.

El compilador evaluará la expresión del caso. Si el valor de una de las constantes de caso coincide con
el valor de la expresión, se ejecuta la declaración que sigue a esta constante. Después de eso, el
programa continúa después del final.

Si ninguna de las constantes de caso coincide con el valor de la expresión, se ejecuta la lista de instrucciones
después de la palabra clave else o else. Puede ser una lista de declaraciones vacía. Si no hay otra parte presente
y ninguna constante de caso coincide con el valor de la expresión, el flujo del programa continúa después del final.

Las declaraciones case pueden ser declaraciones compuestas (es decir, un bloque Begin...End), pero para el
caso else se permiten declaraciones múltiples (como se puede ver en el diagrama de sintaxis). Puede incluirlos
en un bloque Comienzo...Fin adicional, pero no es necesario.

Observación A diferencia de Turbo Pascal, en Free Pascal no se permiten etiquetas de casos duplicados, por lo que el
siguiente código generará un error al compilar:

Donde en: entero;


...
Caso i de
3: hacer algo; 1..5: Hacer
algo más; fin;

190
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

El compilador generará un error de etiqueta de caso duplicado al compilar esto, porque el 3 también aparece
(implícitamente) en el rango 1..5. Esto es similar a la sintaxis de Delphi.

Las siguientes son declaraciones de casos válidas:

Caso C de
'a' : WriteLn ('A presionada'); 'b' : WriteLn ('B
presionado'); 'c' : WriteLn ('C presionada');

demás
WriteLn ('letra desconocida presionada: ',C); fin;

Caso C de
'a','e','i','o','u' : WriteLn ('vocal presionada'); 'y' : WriteLn ('Este depende del
idioma');
demás
WriteLn ('Consonante presionada'); fin;

Número de caso de
1..10: WriteLn ('Número pequeño'); 11..100 : WriteLn
('Normal, número medio'); else WriteLn ('número ENORME'); fin;

La cláusula else puede contener varias declaraciones:

Número de caso de
1..10: WriteLn ('Número pequeño'); 11..100 : WriteLn
('Normal, número medio'); else WriteLn ('número ENORME');

Writeln('¿Cómo conseguimos tanto?'); fin;

Free Pascal permite el uso de cadenas como etiquetas de caso, y en ese caso la variable de caso también debe ser una cadena.
Cuando se utilizan tipos de cadena, la variable de mayúsculas y minúsculas y las distintas etiquetas se comparan distinguiendo
entre mayúsculas y minúsculas.

Minúsculas (OS) de 'windows', 'dos'

: WriteLn ('plataforma Microsoft); 'macos', 'darwin':

Writeln('plataforma Apple'); 'linux', 'freebsd', 'netbsd' :

Writeln('Plataforma comunitaria');
demás
WriteLn ('Otra plataforma'); fin;

191
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

El caso con cadenas es equivalente a una serie de declaraciones if then else, no se realizan optimizaciones.

Sin embargo, se permiten rangos y son el equivalente a un

si (valor>= rango inicial) y (valor <= rango final) entonces


empezar
fin;

13.2.3 La declaración If...then...else


El si .. entonces .. si no .. la sintaxis del prototipo es

Si entonces declaraciones

­­si declaración si expresión entonces declaración ­


otra declaración

La expresión entre las palabras clave if y then debe tener un tipo de resultado booleano. Si la expresión se evalúa como
Verdadera, se ejecuta la declaración que sigue a la palabra clave entonces.

Si la expresión se evalúa como False, entonces se ejecuta la declaración que sigue a la palabra clave else, si está
presente.

Algunos puntos a tener en cuenta:

• Tenga en cuenta el hecho de que la expresión booleana por defecto se evaluará de forma abreviada, lo que
significa que la evaluación se detendrá en el punto donde se conoce con certeza el resultado.

• Además, antes de la palabra clave else, no se permite punto y coma (;), pero todas las declaraciones pueden ser com.
declaraciones de libras.

• En construcciones anidadas If... entonces... else, puede surgir cierta ambigüedad en cuanto a qué declaración
else se empareja con cuál declaración if. La regla es que la palabra clave else coincide con la primera palabra
clave if (buscando hacia atrás) que aún no coincide con una palabra clave else.

Por ejemplo:

Si exp1 entonces
Si exp2 entonces
Estadística1

demás
estadística2;

A pesar de su apariencia, la declaración es sintácticamente equivalente a

Si exp1 entonces
comienza
Si exp2 entonces
Estadística1

demás
estadística2

fin;

192
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

y no a

{NO EQUIVALENTE}
Si exp1 entonces
comienza
Si exp2 entonces
Estadística1

fin
demás
estadística2;

Si es esta última construcción la que se necesita, las palabras clave de inicio y fin deben estar presentes. En
caso de duda, es mejor añadirlos.

La siguiente es una declaración válida:

Si hoy es [lunes...viernes] entonces


WriteLn ('Debe trabajar más duro')
demás
WriteLn ('Tómate un día libre');

13.2.4 La declaración For..to/downto..do


Free Pascal soporta la construcción de bucles For. Se utiliza un bucle for en caso de que uno quiera
calcular algo un número fijo de veces. La sintaxis del prototipo es la siguiente:

Para declaración

­­ para declaración de variable de control := valor inicial ­


bajar a
­ declaración de valor final ­

­­ identificador de variable de variable de control ­

­­ expresión de valor inicial ­

­­ expresión del valor final ­

Aquí, Declaración puede ser una declaración compuesta. Cuando se encuentra la declaración For, la
variable de control se inicializa con el valor inicial y se compara con el valor final. Lo que sucede a
continuación depende de si se usa to o downto:

1. En el caso de que se utilice To, si el valor inicial es mayor que el valor final, entonces la Declaración
nunca ser ejecutado.

2. En el caso de que se utilice DownTo, si el valor inicial es menor que el valor final, entonces Declaración
nunca será ejecutado.

Después de esta verificación, se ejecuta la declaración posterior a Do. Después de la ejecución de la declaración,
la variable de control aumenta o disminuye en 1, dependiendo de si se utiliza To o Downto. La variable de
control debe ser de tipo ordinal; no se pueden utilizar otros tipos como contadores en un bucle.
Observación

193
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

• Free Pascal siempre calcula el límite superior exactamente una vez antes de inicializar la variable del contador con
el valor inicial.

• No está permitido cambiar (es decir, asignar un valor) el valor de una variable de bucle dentro del bucle.

• El valor de la variable de bucle no está definido después de que se ha completado un bucle o si un bucle no se
ejecuta en absoluto. Sin embargo, si el bucle finalizó prematuramente con una excepción o una instrucción break
o goto, la variable del bucle conserva el valor que tenía cuando se salió del bucle.

• Para procedimientos anidados, una variable de bucle debe ser una variable local. Si declara una variable de
bucle fuera del procedimiento anidado donde está el bucle, el compilador se quejará. Sin embargo, está
permitido utilizar una variable global en un procedimiento.

• El compilador no prohíbe explícitamente saltar con una instrucción goto a un bloque de bucle for,
pero hacerlo resultará en un comportamiento impredecible.

Los siguientes son bucles válidos:

Para el día: = de lunes a viernes, trabaje;


Para I: = 100 hasta 1 hacer
WriteLn ('Cuenta atrás: ',i);
Para I := 1 a 7*enanos haga KissDwarf(i);

Lo siguiente generará un error:

Para I:=0 a 100 hacer


comenzar
Hacer algo;
Yo:=Yo*2;
fin;

porque no se me puede asignar la variable de bucle dentro del bucle.

Lo siguiente también generará un error:

prueba del programa;

{$ifdef fpc} {$modo


delphi} {$h+} {$endif}

procedimiento Proc;
era
yo: número entero;
procedimiento anidado;

empezar
por i := 1 a 2 hacer ; fin;

empezar
fin;

empezar
fin.

194
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

porque la variable I no está definida en Nested y tampoco es una variable global.

Pero lo siguiente se compilará:

prueba del programa;

{$ifdef fpc} {$modo


delphi} {$h+} {$endif}

era
yo: número entero;

procedimiento anidado;

empezar
por i := 1 a 2 hacer ; fin;

comenzar
fin.

Si la declaración es una declaración compuesta, entonces las rutinas del sistema Interrumpir y Continuar se pueden
usar para saltar al final o justo después del final de la declaración For. Tenga en cuenta que Interrumpir y Continuar
no son palabras reservadas y, por lo tanto, pueden sobrecargarse.

13.2.5 La declaración For..in..do


A partir de la versión 2.4.2, Free Pascal admite la construcción de bucles For..in. Un bucle for..in se utiliza
en caso de que uno quiera calcular algo un número fijo de veces con una variable de bucle enumerable.
La sintaxis del prototipo es la siguiente:

Para declaración

­­ for in declaración para variable de control en declaración do enumerable ­

­­ identificador de variable de variable de control ­

­­ expresión de tipo enumerado ­


enumerable

Aquí, Declaración puede ser una declaración compuesta. El enumerable debe ser una expresión que consta de
un número fijo de elementos: la variable del bucle se hará igual a cada uno de los elementos por turno y se
ejecutará la declaración que sigue a la palabra clave do.
La expresión enumerable puede ser uno de cinco casos:

1. Un identificador de tipo de enumeración. El bucle abarcará entonces todos los elementos del tipo de
enumeración. La variable de control debe ser del tipo enumeración.

2. Un valor establecido. El bucle entonces abarcará todos los elementos del conjunto, la variable de control debe ser de
el tipo base del conjunto.

195
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

3. Un valor de matriz. El bucle abarcará todos los elementos de la matriz y la variable de control debe tener
el mismo tipo que un elemento de la matriz. Como caso especial, una cadena se considera una matriz
de caracteres.

4. Una clase, objeto o instancia de registro extendido enumerable. Esta es una instancia de cualquier tipo
estructurado que admita las interfaces IEnumerator e IEnumerable. En este caso, el tipo de variable de
control debe ser igual al tipo del valor de retorno de [Link].

5. Cualquier tipo para el que se defina un operador enumerador. El operador enumerador debe devolver un tipo
estructurado que implemente la interfaz IEnumerator. El tipo de la variable de control debe ser igual al tipo
del valor de retorno GetCurrent del enumerador.

El caso más simple del bucle for..in es utilizar un tipo enumerado:

Tipo
TWeekDay = (lunes, martes, miércoles, jueves, viernes, sábado, domingo);

Era
d : Tdía de la semana;

comience
por d en TWeekday y escriba en
(d);
fin.

Esto imprimirá todos los días de la semana en la pantalla.

La construcción for..in anterior es equivalente a la siguiente construcción for..to:

Tipo
TWeekDay = (lunes, martes, miércoles, jueves, viernes, sábado, domingo);

Era
d : Tdía de la semana;

comience
para d:=Bajo(TWeekday) a Alto(TWeekday) escriba writeln(d);

fin.

Un segundo caso de bucle for..in es cuando la expresión enumerable es un conjunto, y luego el bucle se
ejecutará una vez para cada elemento del conjunto:

Tipo
TWeekDay = (lunes, martes, miércoles, jueves, viernes, sábado, domingo);

Era
Semana: conjunto de TWeekDay =
[lunes, martes, miércoles, jueves, viernes];
d : Tdía de la semana;

comenzar

196
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

para d en la semana hacer


escrito(d); fin.

Esto imprimirá los nombres de los días de la semana en la pantalla. Tenga en cuenta que la variable d es del mismo
tipo que el tipo base del conjunto.

La construcción for..in anterior es equivalente a la siguiente construcción for..to:

Tipo
TWeekDay = (lunes, martes, miércoles, jueves, viernes, sábado, domingo);

Era
Semana: conjunto de TWeekDay =
[lunes, martes, miércoles, jueves, viernes];

d : Tdía de la semana;

comenzar
para d:=Bajo(TWeekday) a Alto(TWeekday) hacer si d en la Semana
entonces
escrito(d);
fin.

La tercera posibilidad para un bucle for..in es cuando la expresión enumerable es una matriz:

era
a: Matriz[1..7] de cadena =
('lunes','martes','miércoles','jueves', 'viernes','sábado','domingo');

Era
S: Cadena;

comenzar
Para s en un hacer
Escritura(s);
fin.

Esto también imprimirá todos los días de la semana y equivale a

era
a: Matriz[1..7] de cadena =
('lunes','martes','miércoles','jueves', 'viernes','sábado','domingo');

Era
yo: número entero;

comenzar
para i:=Bajo(a) a alto(a) hacer
Escribir(a[i]);
fin.

197
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

Un tipo de cadena es equivalente a una matriz de caracteres y, por lo tanto, se puede usar una cadena
en un bucle for...in. Lo siguiente imprimirá todas las letras del alfabeto, cada letra en una línea:

Era
c: carbón;

comience
para c en 'abcdefghijklmnopqrstuvwxyz' escriba writeln(c);

fin.

Tenga en cuenta que también se admiten matrices multidimensionales:

usos
SysUtils;

escriba
TTestStringArray = matriz [0..10] de cadena;

Era
TwoD: matriz[0..3] de TTestStringArray;

era
i,j: número entero;
S: Cadena;
comenzar
para i:=0 a 3 hacer
para j:=0 a 10 hacer
DosD[i,J]:=Format('%.2dx%.2d',[i,j]); para S en dos hacer

Escribir(S);
fin.

Esto recorrerá todas las dimensiones de izquierda a derecha.

La cuarta posibilidad para un bucle for..in es utilizar clases. Una clase puede implementar la interfaz
IEnumerable, que se define de la siguiente manera:

IEnumerable = interfaz(IInterfaz)
función GetEnumerator: IEnumerator; fin;

El tipo de retorno real de GetEnumerator no debe ser necesariamente una interfaz IEnumerator; en cambio, puede
ser una clase que implemente los métodos de IEnumerator:

IEnumerator = interfaz(IInterface) función GetCurrent:


TObject; función MoveNext: booleana;
procedimiento de reinicio; propiedad Actual:
TObject leído GetCurrent;
fin;

La propiedad Current y el método MoveNext deben estar presentes en la clase devuelta por el método
GetEnumerator. No es necesario que el tipo real de la propiedad actual sea un TObject.
Cuando encuentre un bucle for..in con una instancia de clase como operando "in", el compilador verificará
cada una de las siguientes condiciones:

198
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

• Si la clase en la expresión enumerable implementa un método GetEnumerator

• Si el resultado de GetEnumerator es una clase con el siguiente método:

Función MoveNext: Booleana

• Si el resultado de GetEnumerator es una clase con la siguiente propiedad de sólo lectura:

Propiedad actual: AType;

El tipo de propiedad debe coincidir con el tipo de variable de control del bucle for..in.

Ni las interfaces IEnumerator ni IEnumerable deben ser declaradas por la clase enumerable: el compilador detectará si
estas interfaces están presentes usando las comprobaciones anteriores.
Las interfaces sólo están definidas para la compatibilidad con Delphi y no se utilizan internamente. (también sería
imposible hacer cumplir su corrección).
La unidad Clases contiene una serie de clases que son enumerables:

TFPList Enumera todos los punteros de la lista.

TList Enumera todos los punteros de la lista.

TCollection Enumera todos los elementos de la colección.

TStringList Enumera todas las cadenas de la lista.

TComponent Enumera todos los componentes secundarios propiedad del componente.

Por lo tanto, el siguiente código también imprimirá todos los días de la semana:

{$mode objfpc} usa


clases;

Era
Días: TStrings;
D: Cadena;

comenzar
días:=[Link]; prueba

[Link]('Lunes'); Dí[Link]('Martes');
Dí[Link]('Miércoles');
Dí[Link]('Jueves'); Dí[Link]('Viernes');
Dí[Link]('Sábado');
Dí[Link]('Domingo'); Para D en
días, escriba Writeln(D); Finalmente
Dí[Link]; fin; fin.

Tenga en cuenta que el compilador aplica la seguridad de tipos: declarar D como un número entero dará como resultado un error del compilador:

199
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

[Link](20,9) Error: Tipos incompatibles: obtuvo "AnsiString" y se esperaba "LongInt"

El código anterior es equivalente al siguiente:

{$mode objfpc} usa


clases;

Era
Días: TStrings;
D: Cadena;
E: TStringsEnumerator;

comenzar
días:=[Link]; prueba

[Link]('Lunes'); Dí[Link]('Martes');
Dí[Link]('Miércoles');
Dí[Link]('Jueves'); Dí[Link]('Viernes');
Dí[Link]('Sábado');
Dí[Link]('Domingo');
E:=Dí[Link]; intente
mientras [Link] comienza
D:=[Link]; Escribir(D); fin;

Finalmente [Link]; fin; Finalmente

Dí[Link]; fin;

fin.

Ambos programas producirán el mismo resultado.

La quinta y última posibilidad de utilizar un bucle for..in se puede utilizar para enumerar casi
cualquier tipo, utilizando el operador enumerador. El operador enumerador debe devolver una clase
que tenga la misma firma que el método IEnumerator anterior. El siguiente código definirá un
enumerador para el tipo Integer:

Tipo

TEvenEnumerator = Clase
FActual: Entero;
FMax: Entero;
Función MoveNext: Booleana;
Propiedad actual: entero leído FCurrent; fin;

Función [Link]: booleano;

200
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

comenzar
FActual:=FActual+2;
Resultado:=FActual<=FMax; fin;

operador enumerador (i: entero): TEvenEnumerator;

comenzar
Resultado:=[Link]; [Link]:=i;
fin;

era
I: Entero; metro :
Entero = 4;

comenzar
Porque yo en M hago
Escribir(i);
fin.

El bucle imprimirá todos los números pares distintos de cero, menores o iguales al enumerable. (2 y 4 en el
caso del ejemplo).
Se debe tener cuidado al definir los operadores enumeradores: el compilador buscará y utilizará el
primer operador enumerador disponible para la expresión enumerable. Para las clases, esto también
significa que el método GetEnumerator ni siquiera se considera. El siguiente código definirá un operador
enumerador que extrae el objeto de una lista de cadenas:

{$mode objfpc} usa


clases;

Tipo
TDayObject = Clase
DíaDeLaSemana: Entero;
Constructor Crear(ADayOfWeek: Entero); fin;

TObjectEnumerator = Clase
Lista FL: TStrings;
Índice FI: Entero;
Función GetCurrent: TDayObject;
Función MoveNext: booleana;
Propiedad actual: TDayObject Leer GetCurrent; fin;

Constructor [Link](ADayOfWeek: Entero);

comenzar
DíaDeLaSemana:=UnDíaDeLaSemana;
fin;

Función [Link]: TDayObject; comenzar

201
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

Resultado:=[Link][Findex] como TDayObject; fin;

Función [Link]: booleano;

comenzar
Inc(FIndex);
Resultado:=(FIndex<[Link]); fin;

enumerador de operadores (s: TStrings): TObjectEnumerator;

comenzar
Resultado:=[Link]; [Link]:=S;
[Link]:=­1; fin;

Era
Días: TStrings;
D: Cadena;
O : TdayObject;

comenzar
días:=[Link]; pruebe

[Link]('Monday',[Link](1));
[Link]('Martes',[Link](2));
[Link]('Miércoles',[Link](3));
[Link]('Jueves',[Link](4));
[Link]('Viernes',[Link](5));
[Link]('Sábado',[Link](6));
Dí[Link]('Domingo',[Link](7)); Para O en Días, escriba
Writeln([Link]);
Finalmente Dí[Link]; fin;

fin.

El código anterior imprimirá el día de la semana para cada día de la semana.


Si una clase no es enumerable, el compilador informará un error cuando se encuentre en un bucle for...in.

Observación Al igual que el bucle for..to, no está permitido cambiar (es decir, asignar un valor) el valor de un bucle.
variable de control dentro del bucle.

13.2.6 La instrucción Repetir..hasta La instrucción repetir se

utiliza para ejecutar una instrucción hasta que se alcance una determinada condición. La declaración se ejecutará
al menos una vez. La sintaxis prototipo de la instrucción Repetir... hasta que es

202
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

Repetir declaración

­­ repetir declaración repetir 6 declaración hasta la expresión ­


;

Esto ejecutará las declaraciones entre repetición y hasta el momento en que la Expresión se evalúe como Verdadera.
Dado que la expresión se evalúa después de la ejecución de las declaraciones, estas se ejecutan al menos una vez.

Tenga en cuenta el hecho de que la expresión booleana Expression se evaluará de forma abreviada de forma
predeterminada, lo que significa que la evaluación se detendrá en el punto donde se conoce con certeza el resultado.

Las siguientes son declaraciones repetidas válidas.

repetir
EscribirLn('I =',i);
Yo:= Yo+2;
hasta I>100;

repetir
X := X/2 hasta
x<10e­3;

Tenga en cuenta que la última declaración antes de la palabra clave hasta no necesita un punto y coma final, pero
está permitida.

Las rutinas del sistema Romper y Continuar se pueden utilizar para saltar al final o justo después del final de la instrucción
repetir...hasta. Tenga en cuenta que Interrumpir y Continuar no son palabras reservadas y, por lo tanto, pueden
sobrecargarse.

13.2.7 La declaración Mientras...hacer


Una declaración while se utiliza para ejecutar una declaración siempre que se cumpla una determinada condición. A
diferencia del bucle de repetición, esto puede implicar que la declaración nunca se ejecute.

La sintaxis prototipo de la declaración While..do es

Mientras que las declaraciones

­­ declaración while expresión while declaración hacer ­

Esto ejecutará la Declaración siempre que la Expresión se evalúe como Verdadera. Dado que la Expresión se evalúa
antes de la ejecución de la Declaración, es posible que la Declaración no se ejecute en absoluto.
La declaración puede ser una declaración compuesta.

Tenga en cuenta el hecho de que la expresión booleana Expression se evaluará de forma abreviada de forma
predeterminada, lo que significa que la evaluación se detendrá en el punto donde se conoce con certeza el resultado.

Las siguientes declaraciones son válidas mientras:

Yo:= Yo+2;
mientras que yo <= 100 lo hago

203
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

comenzar
EscribirLn('I =',i);
Yo:= Yo+2; fin;

X := X/2;
mientras que x>=10e­3 lo hace
X := X/2;

Corresponden a los bucles de ejemplo para las declaraciones repetidas.


Si la declaración es una declaración compuesta, entonces las palabras reservadas Interrumpir y Continuar se
pueden usar para saltar al final o justo después del final de la declaración Mientras. Tenga en cuenta que
Interrumpir y Continuar no son palabras reservadas y, por lo tanto, pueden sobrecargarse.

13.2.8 La declaración With


La sentencia with sirve para acceder a los elementos de un registro u objeto o clase, sin tener que
especificar el nombre del elemento cada vez. La sintaxis de una declaración with es

Con declaración

­­ con declaración 6 referencia variable hacer declaración ­


,

La referencia de la variable debe ser una variable de tipo registro, objeto o clase. En la declaración with,
se verifica cualquier referencia de variable o referencia de método para ver si es un campo o método del
registro, objeto o clase. Si es así, se accede a ese campo o se llama a ese método. Dada la declaración:

Tipo
Pasajero = Registro
Nombre: Cadena [30];
Vuelo: Cadena[10]; fin;

Era
ElCliente: Pasajero;

Las siguientes afirmaciones son completamente equivalentes:

[Link] := 'Michael'; [Link] :=


'PS901';

Con TheCustomer comienza

Nombre := 'Miguel';
Vuelo := 'PS901'; fin;

La declaración

204
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

Con A,B,C,D hacer Declaración;

es equivalente a

con un hacer
Con B hacer
Con C hacer
Con D hacer Declaración;

Este también es un claro ejemplo del hecho de que las variables se prueban del último al primero, es decir, cuando el
compilador encuentra una referencia de variable, primero verificará si es un campo o método de la última variable. De
lo contrario, comprobará el penúltimo, y así sucesivamente. El siguiente ejemplo muestra esto;

¿Prueba del programa?


Tipo AR = registro
X,Y: Entero largo; fin;

PAR=^Ar;

VarS,T: Ar; comenzar


SX :=
1;SY := 1; TX := 2;TY := 2; Con
S,T haga WriteLn (X,'',Y);

fin.

El resultado de este programa es

22

Mostrando así que X,Y en la declaración WriteLn coincide con la variable de registro T.

Observación Cuando se utiliza una instrucción With con un puntero o una clase, no está permitido cambiar el puntero o la clase en
el bloque With. Con las definiciones del ejemplo anterior, a continuación se ilustra de qué se trata:

Donde p: PAR;

comenzar
Con P^ hacer

comenzar // Realizar algunas


operaciones
P:=OtherP; X:=0,0; // ¡¡Se utilizará una X incorrecta!! fin;

La razón por la que el puntero no se puede cambiar es que el compilador almacena la dirección en un registro temporal.
Cambiar el puntero no cambiará la dirección temporal. Lo mismo ocurre con las clases.

13.2.9 Declaraciones de excepción


Free Pascal admite excepciones. Las excepciones proporcionan una manera conveniente de programar errores y mecanismos
de recuperación de errores y están estrechamente relacionadas con las clases. El soporte de excepciones se explica en el
capítulo 17, página 257.

205
Machine Translated by Google

CAPÍTULO 13. DECLARACIONES

13.3 Declaraciones del ensamblador

Una declaración de ensamblador permite insertar código ensamblador directamente en el código Pascal.

Declaraciones de ensamblador

­­ declaración asm fin del código ensamblador asm ­


lista de registro

­­ lista de registro [ 6 cuerdas constantes ] ­


,

Puede encontrar más información sobre los bloques de ensamblador en la Guía del programador. La lista de registros se
utiliza para indicar los registros que son modificados por una declaración del ensamblador en el bloque del ensamblador.
El compilador almacena ciertos resultados en los registros. Si los registros se modifican en una declaración del
ensamblador, a veces se debe informar al compilador al respecto. Los registros se indican con sus nombres Intel para el
procesador I386, es decir, 'EAX', 'ESI', etc. Como ejemplo, considere el siguiente código ensamblador:

ENSAMBLE

Movimiento $1,%ebx
Movl $0,%eax addl
%eax,%ebx end
['EAX','EBX'];

Esto le indicará al compilador que debe guardar y restaurar el contenido de los registros EAX y EBX cuando encuentre
esta declaración asm.

Free Pascal admite varios estilos de sintaxis ensambladora. De forma predeterminada, se asume la sintaxis de AT&T
para la plataforma 80386 y compatibles. El estilo predeterminado del ensamblador se puede cambiar con el modificador
{$asmmode xxx} en el código o con la opción de línea de comandos ­R. Puede encontrar más información sobre esto
en la Guía del programador.

206
Machine Translated by Google

Capítulo 14

Uso de funciones y procedimientos

Free Pascal admite el uso de funciones y procedimientos. Es compatible

• Sobrecarga de funciones, es decir, funciones con el mismo nombre pero con diferentes listas de parámetros.

• Parámetros constantes.

• Arreglos abiertos (es decir, arreglos sin límites).

• Número variable de argumentos como en C.

• Construcción tipo retorno como en C, a través de la palabra clave Exit.

Observación En muchos de los párrafos siguientes se utilizarán las palabras procedimiento y función entre sí.
cambiantemente. Las declaraciones realizadas son válidas para ambos, salvo indicación en contrario.

14.1 Declaración de procedimiento

Una declaración de procedimiento define un identificador y lo asocia con un bloque de código. Luego se puede llamar
al procedimiento con una declaración de procedimiento.

Declaración de procedimiento

­­ encabezado del procedimiento de declaración de procedimiento ; bloque de subrutina ;


­

­­procedimiento de encabezado de procedimiento identificador ­


método calificado identificador
­ lista de parámetros formales sugerenciadirectivas ­
modificadores

­­ bloque de subrutina bloquear ­


directiva externa
bloque conjunto
adelante

Consulte la sección 14.4, página 209 para obtener la lista de parámetros. Una declaración de procedimiento seguida
de un bloque implementa la acción del procedimiento en ese bloque. El siguiente es un procedimiento válido:

207
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Procedimiento Hacer algo (Párr: Cadena); comenzar

Writeln ('Tienes parámetro: ',Para);


Writeln ('Parámetro en mayúsculas: ',Upper(Para)); fin;

Tenga en cuenta que es posible que un procedimiento se llame a sí mismo.

14.2 Declaración de función

Una declaración de función define un identificador y lo asocia con un bloque de código. El bloque de código
devolverá un resultado. Luego, la función se puede llamar dentro de una expresión o con una declaración de
procedimiento, si la sintaxis extendida está activada.

Declaración de función

­­ encabezado de función de declaración de función ; bloque de subrutina ; ­

­­ función de encabezado de función identificador ­


identificador del método calificado : tipo
­ lista de parámetros formales de resultado directivas de sugerencias ­
modificadores

­­ bloque de subrutina bloquear ­


directiva externa
bloque conjunto
adelante

El tipo de resultado de una función puede ser cualquier tipo previamente declarado. al contrario de Turbo Pascal,
donde sólo se podían devolver tipos simples.

14.3 Resultados de la función

El resultado de una función se puede establecer configurando la variable de resultado: este puede ser el identificador de
la función o (solo en modo ObjFPC o Delphi) el identificador de resultado especial:

Función MiFunción: Entero;

comenzar
MiFunción:=12; // Devuelve 12 fin;

En modo Delphi u ObjFPC, lo anterior también se puede codificar como:

Función MiFunción: Entero;

comenzar
Resultado:=12;
fin;

208
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Como extensión de la sintaxis de Delphi, el modo ObjFPC también admite una extensión especial del
procedimiento Exit:

Función MiFunción: Entero;

comenzar
Salir(12); fin;

La llamada Exit establece el resultado de la función y salta al final del bloque de declaración de función. Puede
verse como el equivalente de la instrucción de retorno de C.

Observación Los resultados de la función se tratan como parámetros de paso por referencia. Esto es especialmente importante para los tipos
administrados: el resultado de la función puede ser distinto de nulo en la entrada y establecerse en una instancia válida del tipo.

14.4 Listas de parámetros

Cuando se deben pasar argumentos a una función o procedimiento, estos parámetros deben declararse en la lista de
parámetros formales de esa función o procedimiento. La lista de parámetros es una declaración de identificadores a los
que solo se puede hacer referencia en ese procedimiento o bloque de función.

Parámetros

­­ lista de parámetros formales ( Declaración de 6 parámetros ) ­


;

­­ declaración de parámetros valor parámetro ­


parámetro variable
parámetro de
salida parámetro
constante parámetro de salida

Los parámetros constantes, los parámetros de salida y los parámetros variables también pueden ser parámetros sin tipo si no
tienen ningún identificador de tipo.

A partir de la versión 1.1, Free Pascal admite valores predeterminados tanto para parámetros constantes como para
parámetros de valor, pero sólo para tipos simples. El compilador debe estar en modo OBJFPC o DELPHI para aceptar
valores predeterminados.

14.4.1 Parámetros de valor


Los parámetros de valor se declaran de la siguiente manera:

Parámetros de valor

­­ parámetro de valor lista de identi cadores : tipo de parámetro ­


matriz de
identi er : tipo identi er = valor de parámetro predeterminado

209
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Cuando los parámetros se declaran como parámetros de valor, el procedimiento obtiene una copia de los
parámetros que pasa la declaración de llamada. Cualquier modificación de estos parámetros es puramente local
en el bloque del procedimiento y no se propaga al bloque de llamada.

Un bloque que desee llamar a un procedimiento con parámetros de valor debe pasar parámetros compatibles
con la asignación al procedimiento. Esto significa que los tipos no deben coincidir exactamente, pero pueden
convertirse a los tipos de parámetros reales. Este código de conversión lo inserta el propio compilador.

Se debe tener cuidado al utilizar parámetros de valor: los parámetros de valor hacen un uso intensivo de la pila,
especialmente cuando se utilizan parámetros grandes. El tamaño total de todos los parámetros en la lista de
parámetros formal debe ser inferior a 32 K por razones de portabilidad (la versión de Intel limita esto a 64 K).

Las matrices abiertas se pueden pasar como parámetros de valor. Consulte la sección 14.4.5, página 213 para obtener más información
sobre el uso de matrices abiertas.

Para un parámetro de tipo simple (es decir, no estructurado), se puede especificar un valor predeterminado.
Puede ser una constante sin tipo. Si la llamada a la función omite el parámetro, el valor predeterminado se pasará
a la función. Para matrices dinámicas u otros tipos que puedan considerarse equivalentes a un puntero, el único
valor predeterminado posible es Nil.

El siguiente ejemplo imprimirá 20 en la pantalla:

prueba del programa;

constante

MiConst = 20;

Procedimiento MyRealFunc(I: Entero = MyConst);

comenzar
Writeln('Función recibida: ',I); fin;

comenzar
MyRealFunc; fin.

14.4.2 Parámetros variables


Los parámetros variables se declaran de la siguiente manera:

Parámetros variables

­­ lista de identificadores var de parámetros variables ­


: tipo de identificador
gama de

Cuando los parámetros se declaran como parámetros variables, el procedimiento o función accede
inmediatamente a la variable que el bloque de llamada pasó en su lista de parámetros. El procedimiento obtiene
un puntero a la variable que se pasó y utiliza este puntero para acceder al valor de la variable. De esto se deduce
que cualquier cambio realizado en el parámetro se propagará de nuevo al bloque de llamada. Este mecanismo
se puede utilizar para devolver valores en los procedimientos. Debido a esto, el bloque que llama debe pasar un
parámetro de exactamente el mismo tipo que el tipo del parámetro declarado. Si no es así, el compilador generará
un error.

210
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Los parámetros variables y constantes pueden no escribirse. En ese caso, la variable no tiene tipo y, por tanto,
es incompatible con todos los demás tipos. Sin embargo, se puede utilizar el operador de dirección o se puede
pasar a una función que también tenga un parámetro sin tipo. Si se utiliza un parámetro sin tipo en una
asignación, o se le debe asignar un valor, se debe utilizar un encasillado.

Las variables de tipo de archivo siempre deben pasarse como parámetros variables.

Las matrices abiertas se pueden pasar como parámetros variables. Consulte la sección 14.4.5, página 213 para obtener más información
sobre el uso de matrices abiertas.

Observación

• Tenga en cuenta que los valores predeterminados no son compatibles con los parámetros variables. Esto tendría poco sentido
ya que anula el propósito de poder devolver un valor a la persona que llama.

• El resultado de una función se trata internamente como un parámetro variable y puede tener un valor inicial distinto de
cero (o distinto de nulo). Esto es especialmente importante para los tipos administrados.

14.4.3 Parámetros de salida


Los parámetros de salida (parámetros de salida) se declaran de la siguiente manera:

Parámetros de salida

­­ parámetro de salida lista de identificadores de salida ­


: tipo de identificador
gama de

El propósito de un parámetro de salida es devolver valores a la rutina de llamada: la variable se pasa por
referencia. El valor inicial del parámetro al ingresar la función se descarta y no debe usarse.

Si se debe usar una variable para pasar un valor a una función y recuperar datos de la función, entonces se
debe usar un parámetro variable. Si solo se debe recuperar un valor, se puede utilizar un parámetro de salida.

No hace falta decir que los valores predeterminados no son compatibles con nuestros parámetros.

La diferencia entre los parámetros y los parámetros por referencia es muy pequeña (sin embargo, consulte a
continuación los tipos administrados): el primero le da al compilador más información sobre lo que sucede con
los argumentos cuando se pasan al procedimiento: sabe que la variable no tiene por qué inicializarse antes de
la llamada. El siguiente ejemplo lo ilustra:

Procedimiento DoA(Var A: Entero);

comenzar
R:=2;
Writeln('A es ',A); fin;

Procedimiento DoB(Salida B: Entero);

comenzar
B:=2;
Writeln('B es ',B); fin;

211
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Era
C,D: Entero;

comenzar
DoA(C);
DoB(D); fin.

Ambos procedimientos DoA y DoB hacen prácticamente lo mismo. Pero la declaración de DoB proporciona
más información al compilador, permitiéndole detectar que D no tiene que inicializarse antes de llamar a DoB.
Dado que el parámetro A en DoA puede recibir un valor además de devolver uno, el compilador nota que C
no se inicializó antes de la llamada a DoA:

inicio: >fpc ­S2 ­vwhn [Link] [Link](19,8)


Sugerencia: La variable "C" no parece estar inicializada

Esto muestra que es mejor usar parámetros cuando el parámetro se usa solo para devolver un valor.
Los parámetros de salida de comentarios solo se admiten en los modos Delphi y ObjFPC. Para los otros modos, out es un
identificador válido.

Observación Para los tipos administrados (tipos con recuento de referencias), el uso de parámetros Out genera cierta sobrecarga: el
compilador debe asegurarse de que el valor esté inicializado correctamente (es decir, tenga un recuento de referencia de cero (0)).
Esta inicialización normalmente la realiza la persona que llama.

14.4.4 Parámetros constantes


Además de parámetros variables y parámetros de valor, Free Pascal también admite parámetros constantes. Un
parámetro constante se puede especificar de la siguiente manera:

Parámetros constantes

­
­­parámetro constante constante lista de identi cadores

: tipo de identificador
matriz de
­ identi er : tipo identi er = valor de parámetro predeterminado
­

Especificar un parámetro como Constante le da al compilador una pista de que la rutina llamada no cambiará
el contenido del parámetro. Esto permite al compilador realizar optimizaciones que de otra manera no podría
hacer, y también realizar ciertas comprobaciones en el código dentro de la rutina: es decir, puede prohibir
asignaciones al parámetro. Además, un parámetro constante no se puede pasar a otra función que requiera
un parámetro variable: el compilador también puede verificar esto. El uso principal de esto es reducir el
tamaño de la pila, mejorando así el rendimiento y aún conservando la semántica de pasar por valor...

Observación Al contrario de Delphi, no se deben hacer suposiciones sobre cómo se pasan los parámetros constantes
a la rutina subyacente. En particular, la suposición de que los parámetros de gran tamaño se pasan por
referencia no es correcta. Para ello se debe utilizar el tipo de parámetro constref, que está disponible a partir
de la versión 2.5.1 del compilador.
Una excepción es la convención de llamada stdcall: para compatibilidad con los estándares COM, los parámetros
constantes grandes se pasan por referencia.

212
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Observación Tenga en cuenta que especificar const es un contrato entre el programador y el compilador. Es el programador
quien le dice al compilador que el contenido del parámetro constante no se cambiará cuando se ejecute la rutina,
no es el compilador quien le dice al programador que el parámetro no se cambiará.

Esto es particularmente importante y visible cuando se utilizan tipos recontados. Para tales tipos, el incremento y la
disminución (invisible) de cualquier recuento de referencia se omite cuando se utiliza const. Hacerlo a menudo
permite que el compilador omita marcos try/finally invisibles para estas rutinas.

Como efecto secundario, el siguiente código no producirá el resultado esperado:

Era
S: Cadena = 'Algo';

Procedimiento Hacerlo (Const T: Cadena);

comenzar
S:='Algo más';
Escribir(T); fin;

comenzarHazlo(S);
fin.

Escribirá

Algo más

Este comportamiento es por diseño.

Los parámetros constantes también se pueden anular. Consulte la sección 14.4.2, página 210 para obtener más información
sobre los parámetros sin tipo.

En cuanto a los parámetros de valor, los parámetros constantes pueden obtener valores predeterminados.

Las matrices abiertas se pueden pasar como parámetros constantes. Consulte la sección 14.4.5, página 213 para obtener más información
sobre el uso de matrices abiertas.

14.4.5 Parámetros de matriz abierta Free Pascal

soporta el paso de matrices abiertas, es decir, un procedimiento puede declararse con una matriz de longitud no
especificada como parámetro, como en Delphi. Se puede acceder a los parámetros de matriz abierta en el
procedimiento o función como una matriz declarada con índice inicial 0 y índice de último elemento Alto (parámetro).
Por ejemplo, el parámetro

Fila: Matriz de números enteros;

sería equivalente a

Fila: Matriz[0..N­1] de número entero;

Donde N sería el tamaño real de la matriz que se pasa a la función. N­1 se puede calcular como Alto (Fila).

Específicamente, si se pasa una matriz vacía, Alto(Parámetro) devuelve ­1, mientras que bajo(Parámetro) devuelve 0.

213
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Los parámetros abiertos se pueden pasar por valor, por referencia o como parámetro constante. En los
últimos casos, el procedimiento recibe un puntero a la matriz real. En el primer caso, recibe una copia de la matriz.
En una función o procedimiento, los arreglos abiertos sólo se pueden pasar a funciones que también están declaradas
con arreglos abiertos como parámetros, no a funciones o procedimientos que aceptan arreglos de longitud fija. El
siguiente es un ejemplo de una función que utiliza una matriz abierta:

Función Promedio (Fila: Matriz de números enteros): Real;


Var I: entero largo;
Temperatura: Real;
comenzar
Temporal:= Fila[0];
Para I: = 1 a Alto (Fila) hacer
Temp := Temp + Fila [i];
Promedio: = Temp / (Alto (Fila) +1); fin;

A partir de FPC 2.2, también es posible pasar matrices parciales a una función que acepte una matriz
abierta. Esto se puede hacer especificando el rango de la matriz que debe pasarse a la matriz abierta.
Dada la declaración

Era
A: Matriz[1..100];

la siguiente llamada calculará e imprimirá el promedio de los 100 números:

Writeln('Promedio de 100 números: ',Promedio(A));

Pero lo siguiente calculará e imprimirá el promedio de la primera y segunda mitad:

Writeln('Promedio de los primeros 50 números: ',Promedio(A[1..50])); Writeln('Promedio de los últimos 50


números: ',Promedio(A[51..100]));

14.4.6 Matriz de constantes En modo

Object Pascal o Delphi, Free Pascal admite la construcción Matriz de constantes para pasar parámetros a una subrutina.

Este es un caso especial de la construcción de matriz abierta, donde se permite pasar cualquier expresión de una
matriz a una función o procedimiento. La expresión debe tener un tipo de resultado simple: las estructuras no se
pueden pasar como argumento. Esto significa que se pueden pasar todos los tipos ordinales, flotantes o de
cadena, así como punteros, clases e interfaces (ya que los dos últimos son en realidad punteros).

Los elementos de la matriz de const se convierten en un registro de variante especial:

Tipo
PVarRec = ^TVarRec;
TVarRec = registro

caso VType: Ptrint de vtInteger vtBoolean


: (VIntero: Entero largo);
: (VBooleano: booleano);
vtChar : (VCar: Carbón);
vtWideChar : (VWideChar: WideChar); vtExtendido: (VExtendido:
PExtendido); cadenavt
: (VString: PShortString);

214
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

vtPointer : (VPointer: puntero);


vtPChar : (VPChar: PChar);
vtObject : (VObjeto: TObjeto);
vtClass : (Clase V: Clase T);
vtPWideChar : (VPWideChar: PWideChar);
vtAnsiString: (VAnsiString: puntero);
vtCurrency: (VCurrency: PCurrency);
vtVariante : (Variante: PVariante);
vtInterface: (VInterface: puntero);
vtWideString: (VWideString: puntero);
vtInt64 : (VInt64: PInt64);
vtQPalabra : (PalabraVQ: PalabraPQ);
fin;

Por lo tanto, dentro del cuerpo del procedimiento, la matriz del argumento constante es equivalente a un código abierto.
matriz de TVarRec:

Procedimiento Testit (Args: Matriz de const);

Var I: entero largo;

comenzar
Si Alto (Args) <0 entonces
comenzar
Writeln ('Sin argumentos');
salida;
fin;
Writeln ('Got ',Alto(Args)+1,' argumentos:');
Para i:=0 a Alto(Args) haga
comenzar
escribir ('Argumento',i,'tiene tipo');
caso Args[i].vtipo de
vtinteger :
Writeln ('Entero, Valor:',args[i].vinteger);
vtbooleano :
Writeln ('Booleano, Valor:',args[i].vboolean);
vtchar :
Writeln ('Char, valor: ',args[i].vchar);
vtextendido:
Writeln ('Extendido, valor: ',args[i].VExtendido^);
vtString :
Writeln ('Cadena corta, valor:',args[i].VString^);
vtPointer :
Writeln ('Puntero, valor: ',Longint(Args[i].VPointer));
vtPChar :
Writeln ('PChar, valor: ',Args[i].VPChar);
vtObject :
Writeln ('Objeto, nombre: ',Args[i].[Link]);
vtClass :
Writeln ('Referencia de clase, nombre:',Args[i].[Link]);
vtAnsiString:
Writeln ('AnsiString, valor:',AnsiString(Args[I].VAnsiString);
demás
Writeln ('(Desconocido) : ',args[i].vtype);

215
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

fin;
fin; fin;

En código, es posible pasar una matriz arbitraria de elementos a este procedimiento:

S:='Ansistring 1'; T:='AnsiString


2'; Pruebas ([]); Pruebas ([1,2]);
Pruebas (['A','B']);
Pruebas
([VERDADERO,FALSO,VERDADERO]);
Pruebas (['Cadena','Otra cadena']); Pruebas
([S,T]); Pruebas ([P1,P2]); Pruebas ([@testit,Nil]); Pruebas
([ObjA,ObjB]); Pruebas
([1.234,1.234]); Pruébalo
([Clase A]);

Si el procedimiento se declara con el modificador cdecl, entonces el compilador pasará la matriz como lo haría un
compilador de C. Esto, de hecho, emula la construcción C de un número variable de argumentos, como lo mostrará el
siguiente ejemplo:

programa testaocc; {$modo


objfpc}

constante

P : PChar = 'ejemplo'; Fmt : PChar =


'Este %s usa printf para
imprimir números (%d) y cadenas.'#10;

// Declaración de la función estándar C printf: procedimiento printf (fm : pchar;


args : array of const);cdecl; 'c' externa;

comenzar
printf(Fmt,[P,123]); fin.

Observación Tenga en cuenta que esto no es cierto para Delphi, por lo que el código que depende de esta característica no será portátil.

Observación Tenga en cuenta que no hay soporte para argumentos DWord (o cardinales) en la matriz de const. Se convierten a
vtInteger/vtLongint. Esto es por compatibilidad con Delphi, y el compilador ignorará cualquier verificación de rango
resultante cuando esté en modo Delphi.

14.4.7 Parámetros sin tipo


Los parámetros variables, de salida y constantes pueden no escribirse. En ese caso, la variable no tiene tipo en el
procedimiento de la función y, por lo tanto, es incompatible con todos los demás tipos: el compilador simplemente
pasa la dirección de la variable pasada a la rutina, por lo que todo lo que está disponible en la rutina llamada es
una dirección. sin información de tipo adjunta. Esto también es válido para los parámetros constantes.

Usando una analogía

procedimiento Algo (datos constantes; Len: cint);

216
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Es equivalente a la siguiente declaración C/C++:

void Algo(void* Datos; int Len);

Esto significa que prácticamente todo lo que se puede hacer en la rutina es usar el operador de dirección o pasar el
argumento a otra función que también tenga un parámetro sin tipo.

Dentro de una rutina con un parámetro sin tipo, si el parámetro sin tipo se usa en una expresión, o se le debe asignar un
valor, siempre se debe usar un encasillado.

El siguiente ejemplo muestra esto:

{$mode objfpc} usa tipos;

el procedimiento debe (const d);

comenzar
Writeln('Como número entero: ',PInteger(@D)^);
Writeln('Como byte: ',PByte(@D)^); fin;

Era
D: Entero;

comenzar
D:=$0FFFFFF0;
Hazlo(D); fin.

Esto escribirá:

Como número entero: 268435440


Como bytes: 240

Tenga en cuenta que debido a que se necesita una dirección, no se pueden pasar expresiones constantes a la
función o procedimiento, es decir, dada la definición anterior, lo siguiente no funcionará:

Hazlo ($0FFFFFF0);

Esto dará como resultado el siguiente error:

Error: Se esperaba un identificador de variable

14.4.8 Tipos administrados y recuentos de referencias Algunos tipos

(Unicodestring, Ansistring, interfaces, matrices dinámicas) son tratados de manera algo especial por el compilador: los
datos tienen un recuento de referencias que aumenta o disminuye dependiendo de cuántas referencias a los datos existan.

Los calificadores de parámetros en llamadas a funciones o procedimientos influyen en lo que sucede con el recuento de
referencias de los tipos administrados:

• nada (pasar por valor): el recuento de referencia del parámetro aumenta al ingresar y eliminar.
arrugado al salir.

217
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

• out: el recuento de referencia del valor que se pasa se reduce en 1 y la variable que se pasa al procedimiento se
inicializa a "vacía" (generalmente Nil, pero ese es un detalle de implementación en el que no se debe confiar).

• var no pasa nada con el recuento de referencias. Se pasa una referencia a la variable original y cambiarla o leerla
tiene exactamente el mismo efecto que cambiar/leer la variable original.

• const este caso es un poco complicado. No sucede nada con el recuento de referencias porque aquí puede
pasar valores que no son valores. En particular, puede pasar una clase que implemente una interfaz en lugar
de la interfaz misma, lo que puede provocar que la clase se libere inesperadamente.

Observación El resultado de la función se trata internamente como un parámetro var de la función y se aplican las mismas reglas que para
Se aplican los parámetros var.

El siguiente ejemplo demuestra los peligros:

{$modo objfpc}

Tipo
Prueba de TI = Interfaz
Procedimiento DoTest(ACount: Entero); fin;

TTest = Clase(TInterfacedObject,ITest)
Procedimiento DoTest(ACount: Entero);
Destructor destruir; anular; fin;

Destructor [Link];

comenzar
Writeln('Destruir llamado'); fin;

Procedimiento [Link](ACount: Entero);

comenzar
Writeln('Prueba ',ACount,': recuento de referencias: ',RefCount); fin;

procedimiento DoIt1(x: ITest; ACount: Entero);

comenzar // El recuento de referencias aumenta


[Link](ACount); // Y final
disminuido;

procedimiento DoIt2(const x: ITest; ACount: Entero);

comenzar // No hay cambios en el recuento de referencias.


[Link](ACount); fin;

218
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Procedimiento Prueba1;

era
y: prueba de
TI;
comenzar y := [Link]; //
Árbitro. El recuento es 1 en este momento. [Link](1); //
Llamar a DoIT
aumentará el recuento de referencias y disminuirá al salir.
Hacer1(y,2); // El
recuento de referencias sigue siendo uno. [Link](3);
fin;

Procedimiento Prueba2;

era
Y: Prueba T;
comenzar
Y := [Link]; // aún no hay recuento del objeto // Ref. El recuento es 0 en este
punto. [Link](3); // El recuento de referencias seguirá
siendo cero.

Hacer2(y,4);
[Link](5);
[Link]; fin;

Prueba de Procedimiento3;

era
Y: Prueba T;
comenzar
Y := [Link]; // aún no hay recuento del objeto // Ref. El recuento es 0 en este
punto. [Link](6); // El recuento de referencias seguirá
siendo cero.

Hacer1(y,7);
[Link] (8); fin;

comenzar
Prueba1;
Prueba2;
Prueba3;
fin.

El resultado de este ejemplo es:

Prueba 1: recuento de referencias: 1


Prueba 2: recuento de árbitros: 2
Prueba 3: recuento de árbitros: 1

219
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

destruir llamado
Prueba 3: recuento de referencias: 0
Prueba 4: recuento de referencias: 0
Prueba 5: recuento de referencias: 0
destruir llamado
Prueba 6: recuento de referencias: 0
Prueba 7: recuento de árbitros: 1
destruir llamado
Prueba 8: recuento de referencias: 0

Como puede verse, en test3, el recuento de referencias disminuye de 1 a 0 al final de la llamada DoIt, lo que hace que la
instancia se libere antes de que regrese la llamada.

El siguiente pequeño programa demuestra los recuentos de referencia utilizados en cadenas:

{$modo objfpc}
{$H+}

// Función auxiliar para extraer el recuento de referencias. función SRefCount(P:


Puntero): entero;

Tipo
PAnsiRec = ^TAnsiRec;
TAnsiRec = Grabar
Página de código : TSystemCodePage;
Tamaño del elemento: Palabra;
{$ifdef CPU64} {alinear
campos}
Ficticio : DPalabra;
{$endif CPU64}
Árbitro : TamañoInt;
Solo : TamañoInt;
fin;

comenzar
si P=Nil entonces
Resultado:=0
demás
Resultado:=PAnsiRec(P­SizeOf(TAnsiRec))^.Ref;
fin;

Procedimiento ByVar(Var S: cadena);

comenzar
Writeln('Por var, recuento de referencias: ',SRefCount(Pointer(S))); fin;

Procedimiento ByConst(Const S: cadena);

comenzar
Writeln('Const, recuento de referencias: ',SRefCount(Puntero(S))); fin;

Procedimiento ByVal(S: cadena);

220
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

comenzar
Writeln('Valor, recuento de referencias: ',SRefCount(Pointer(S))); fin;

Función FunctionResult(Var S: Cadena): Cadena;

comenzar
Writeln('Argumento de función, recuento de referencias: ',SRefCount(Pointer(S)));
Writeln('Resultado de la función, recuento de referencias: ',SRefCount(Pointer(Resultado))); fin;

Era
S,T : Cadena;

comenzar
S:='Alguna cadena';
Writeln('Constant : ',SrefCount(Puntero(S)));
UniqueString(S);
Writeln('Unique T:=S; : ',SRefCount(Puntero(S)));

Writeln('Después de asignar: ',SRefCount(Pointer(S))); ByVar(S); ByConst(S); ByVal(S);


UniqueString(S);
T:=FunctionResult(S);
Writeln('Después
de la función:
',SRefCount(Pointer(S))); fin.

14.5 Sobrecarga de funciones

La sobrecarga de funciones simplemente significa que la misma función se define más de una vez, pero cada vez
con una lista de parámetros formales diferente. Las listas de parámetros deben diferir al menos en uno de sus
tipos de elementos. Cuando el compilador encuentra una llamada a una función, observará los parámetros de la
función para decidir cuál de las funciones definidas debe llamar. Esto puede resultar útil cuando se debe definir la
misma función para diferentes tipos.

Por ejemplo, en un RTL hipotético, el procedimiento aleatorio podría definirse como:

Aleatorio (Máx: Entero largo);


Aleatorio (Máx: Doble);
Aleatorio (Max, Ofsset: Entero largo);

Cuando el compilador encuentra una llamada a la función aleatoria, primero buscará qué función debe usar. Por lo tanto,
verifica los parámetros en una llamada de función y busca si hay una definición de función que coincida con la lista de
parámetros especificada. Si el compilador encuentra dicha función, se inserta una llamada a esa función. Si no se
encuentra dicha función, se genera un error del compilador.

Las funciones que tienen un modificador cdecl no se pueden sobrecargar. (Técnicamente, porque este modificador evita
que el compilador altere el nombre de la función).

Antes de la versión 1.9 del compilador, las funciones sobrecargadas debían estar en la misma unidad. Ahora el
compilador continuará buscando en otras unidades si no encuentra una versión coincidente de una función
sobrecargada en una unidad y si la palabra clave sobrecarga está presente.

221
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Si la palabra clave de sobrecarga no está presente, entonces todas las versiones sobrecargadas deben residir en la
misma unidad, y si se trata de métodos que forman parte de una clase, deben estar en la misma clase, es decir, el
compilador no buscará métodos sobrecargados en las clases principales si No se especificó la palabra clave de sobrecarga.

Observación Tenga en cuenta que si redefine (y sobrecarga) las funciones internas del compilador, las funciones originales del
compilador ya no estarán disponibles. Este es, por ejemplo, el caso de Inc() y Dec()

14.6 Funciones declaradas hacia adelante

Se puede declarar una función sin que le siga su implementación, haciendo que le siga el procedimiento de avance. La
implementación efectiva de esa función debe seguir más adelante en el módulo. La función se puede utilizar después de
una declaración directa como si ya se hubiera implementado. El siguiente es un ejemplo de una declaración directa.

Prueba del programa hacia


adelante; Procedimiento primero (n: entero largo); adelante;
Procedimiento Segundo;
comenzar
WriteLn ('En segundo. Llamando primero...'); Primero (1); fin;
Procedimiento

primero (n: entero largo); comenzar WriteLn ('Primera


recepción:
',n); fin; comenzar segundo; fin.

Una función se puede declarar hacia adelante solo una vez. Asimismo, en unidades, no se permite tener una función
declarada hacia adelante de una función que ha sido declarada en la parte de la interfaz. La declaración de interfaz
cuenta como una declaración directa. La siguiente unidad dará un error cuando se compila:

Prueba unitaria hacia adelante;


interfaz
Procedimiento primero (n: entero largo); Procedimiento
Segundo; Procedimiento de
implementación primero
(n: entero largo); adelante; Procedimiento Segundo; comenzar WriteLn
('En segundo. Llamando

primero...'); Primero (1); fin; Procedimiento primero (n: entero largo);


comenzar WriteLn

('Primera recepción: ',n); fin;

fin.

A la inversa, las funciones declaradas en la sección de interfaz no se pueden declarar en la sección de implementación.
Lógicamente, puesto que ya han sido declarados.

222
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

14.7 Funciones externas


El modificador externo se puede utilizar para declarar una función que reside en un archivo objeto externo. Permite
utilizar la función en algún código, y en el momento del enlace se debe vincular el archivo objeto que contiene la
implementación de la función o procedimiento.

Directiva externa

­­ directiva externa externa ­


constante de cadena
nombre cadena
constante índice entero constante

Reemplaza, en efecto, el bloque de código de función o procedimiento. Como ejemplo:

programa CmodDemo;
{$Enlacelib c}
Const P: PChar = '¡Esto es divertido!'; Función strlen (P:
PChar): Entero largo; cdec; externo; comenzar WriteLn ('Longitud de (',p,'): ',strlen(p)) fin.

Observación Los parámetros en la declaración de la función externa deben coincidir exactamente con los del
declaración en el archivo objeto.

Si el modificador externo va seguido de una constante de cadena:

'nombre' externo;

Luego esto le dice al compilador que la función reside en la biblioteca "lname". Luego, el compilador vinculará
automáticamente esta biblioteca al programa.

También se puede especificar el nombre que tiene la función en la biblioteca:

externo 'lname' nombre 'Fname';

Esto le dice al compilador que la función reside en la biblioteca "lname", pero con el nombre "Fname". Luego, el
compilador vinculará automáticamente esta biblioteca al programa y utilizará el nombre correcto para la función. En
WINDOWS y OS/2, también se puede utilizar el siguiente formulario:

Índice externo 'lname' Ind;

Esto le dice al compilador que la función reside en la biblioteca "lname", pero con el índice Ind. Luego, el compilador
vinculará automáticamente esta biblioteca al programa y utilizará el índice correcto para la función.

Finalmente, la directiva externa se puede utilizar para especificar el nombre externo de la función:

nombre externo 'Fname'; {$L mifunc.o}

Esto le dice al compilador que la función tiene el nombre "Fname". La biblioteca o el archivo objeto correcto (en
este caso myfunc.o) aún debe estar vinculado, asegurando que la función "Fname" esté realmente incluida en la
etapa de vinculación.

223
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

14.8 Funciones del ensamblador

Las funciones y procedimientos se pueden implementar completamente en lenguaje ensamblador. Para indicar esto,
utilice la palabra clave del ensamblador:

Funciones de ensamblador

­­ ensamblador de bloques ; declaración parte declaración asm ­

Al contrario de Delphi, la palabra clave ensamblador debe estar presente para indicar una función ensambladora. Para obtener
más información sobre las funciones del ensamblador, consulte el capítulo sobre el uso del ensamblador en la Guía del
programador.

14.9 Modi cadores

Una declaración de función o procedimiento puede contener modificadores. Aquí enumeramos las diversas posibilidades:

Modi cadores

­­ modi cadores 6 ; público ­


nombre cadena constante
exportar
alias: cadena constante
interrupción
no retorno
iocheck
modi cadores de llamada

­­ modificadores de llamadas cdecl ­


cppdecl
hardfloat
en línea
local
mwpascal
ms_abi_default
ms_abi_cdecl
nostackframe
sobrecarga
registro
pascal
llamada
segura

saveregisters softfloat
stdcall
sysv_abi_default sysv_abi_cdecl vectorcall
Vararg

224
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Free Pascal no admite todos los modificadores de Turbo Pascal (aunque los analiza para comprobar su
compatibilidad), pero sí admite varios modificadores adicionales. Se utilizan principalmente para ensamblador y
referencia a archivos de objetos C.

14.9.1 alias
El modificador de alias permite al programador especificar un nombre diferente para un procedimiento o función.
Esto es especialmente útil para hacer referencia a este procedimiento desde construcciones en lenguaje ensamblador
o desde otro archivo objeto. Como ejemplo, considere el siguiente programa:

Alias de programa;

Procedimiento Printit;[público, alias:'MUST']; comenzar

WriteLn('En Printit(también conocido como: "DEBE")'); fin;

Procedimiento myprint; nombre externo 'DEBE';

comenzar

ENSAMBLE

llamar al final de
myprint;

fin.

Observación El alias especificado se inserta directamente en el código ensamblador, por lo que distingue entre mayúsculas y minúsculas.

El modificador de alias no hace que el símbolo sea público para otros módulos, a menos que la rutina también
se declare en la parte de interfaz de una unidad, o se use el modificador público para forzarlo a ser público.
Considera lo siguiente:

une los escudos;

interfaz

rutina de prueba de procedimiento;

implementación

rutina de prueba del procedimiento; alias: 'ARoutine'; comenzar

WriteLn('Hola mundo'); fin;

fin.

Esto hará que la rutina testroutine esté disponible públicamente para archivos de objetos externos bajo el nombre
de etiqueta ARoutine.

Observación La directiva alias se considera obsoleta. Utilice la directiva de nombre público. Ver sección 14.9.17, página 230.

225
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

14.9.2 decl.
El modificador cdecl se puede utilizar para declarar una función que utiliza una convención de llamada de tipo C. Esto
debe usarse al acceder a funciones que residen en un archivo objeto generado por compiladores de C estándar, pero
también debe usarse para funciones de Pascal que se usarán como devoluciones de llamada para bibliotecas de C.

El modificador cdecl permite utilizar la función C en el código. Para funciones C externas, el archivo objeto que contiene
la implementación C de la función o procedimiento debe estar vinculado. Como ejemplo:

programa CmodDemo;
{$LINKLIB c}
Const P: PChar = '¡Esto es divertido!'; Función StrLen(P:
PChar): Longint;cdecl; nombre externo 'strlen'; comenzar WriteLn ('Longitud de (',p,'): ',StrLen(p));

fin.

Al compilar esto y vincularlo a la biblioteca C, se puede llamar a la función strlen en todo el programa. La directiva externa
le dice al compilador que la función reside en un archivo objeto externo o biblioteca con el nombre "strlen" (ver 14.7).

Observación Los parámetros en nuestra declaración de la función C deben coincidir exactamente con los de la declaración.
Cª.

Para funciones que no son externas, pero que se declaran mediante cdecl, no se necesita ningún enlace externo. Estas
funciones tienen algunas restricciones, por ejemplo, la matriz de la construcción const no se puede usar (debido a la
forma en que usa la pila). Por otro lado, el modificador cdecl permite que estas funciones se utilicen como devoluciones
de llamada para rutinas escritas en C, ya que estas últimas esperan la convención de llamada “cdecl”.

14.9.3 cppdecl
El modificador cppdecl se puede utilizar para declarar una función que utiliza una convención de llamada de tipo C++.
Esto debe usarse al acceder a funciones que residen en un archivo objeto generado por compiladores estándar de C++,
pero también debe usarse para funciones de Pascal que se usarán como devoluciones de llamada para bibliotecas de C++.

Tenga en cuenta que vincular al soporte de C++ es, en el mejor de los casos, experimental, por lo que debe usarse con extremo cuidado.

14.9.4 exportar El

modificador de exportación se utiliza para exportar nombres al crear una biblioteca compartida o un programa ejecutable.
Esto significa que el símbolo estará disponible públicamente y podrá importarse desde otros programas. Para obtener
más información sobre este modificador, consulte la sección sobre “Creación de bibliotecas” en la Guía del programador.

14.9.5 flotación dura


El modificador hardfloat se usa para indicar que el compilador debe usar una convención de llamada que pase ciertos
parámetros de punto flotante en los registros VFP. (Usado solo en ARM)

14.9.6 en línea
Los procedimientos que se declaran en línea se copian en los lugares donde se llaman. Esto tiene el efecto de que no
hay una llamada al procedimiento real, el código del procedimiento simplemente se copia donde está el

226
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

Se necesita un procedimiento, esto da como resultado una velocidad de ejecución más rápida si la función o procedimiento se usa mucho.
Es obvio que incluir funciones grandes no tiene sentido.

De forma predeterminada, no se permiten procedimientos en línea. El código en línea debe habilitarse mediante la directiva de cambio
de línea de comando ­Si o {$inline on}.

Observación

1. inline es solo una pista para el compilador. Esto no significa automáticamente que todas las llamadas estén integradas; A veces, el
compilador puede decidir que una función simplemente no se puede incluir en línea, o que una llamada particular a la función no
se puede incluir en línea. Si es así, el compilador emitirá una advertencia.

2. En versiones antiguas de Free Pascal, el código en línea no se exportaba desde una unidad. Esto significa que al llamar a un
procedimiento en línea desde otra unidad, se realizará una llamada a un procedimiento normal. Sólo dentro de las unidades, los
procedimientos en línea están realmente en línea. A partir de la versión 2.0.2, en línea funciona en todas las unidades.

3. No se permiten funciones recursivas en línea. es decir, no se permite una función en línea que se llame a sí misma.

14.9.7 interrupción
La palabra clave de interrupción se utiliza para declarar una rutina que se utilizará como controlador de interrupciones.
Al ingresar a esta rutina, se guardarán todos los registros y al salir, se restaurarán todos los registros y se ejecutará una interrupción o
retorno de trampa (en lugar del retorno normal de la instrucción de subrutina).

En plataformas donde no existe un retorno de una interrupción, en su lugar se realizará el código de salida normal de las rutinas. Para
obtener más información sobre el código generado, consulte la Guía del programador.

14.9.8 iocheck
La palabra clave iocheck se utiliza para declarar una rutina que genera un código de verificación de resultados de E/S dentro de un bloque
{$IOCHECKS ON} cada vez que se llama.

El resultado es que si se genera una llamada a este procedimiento, el compilador insertará un código de verificación de E/S si la llamada
está dentro de un bloque {$IOCHECKS ON}.

Este modificador está destinado a rutinas internas de RTL, no a su uso en el código de la aplicación.

14.9.9 locales
El modificador local permite al compilador optimizar la función: una función local no puede estar en la sección de interfaz de una unidad:
siempre está en la sección de implementación de la unidad. De esto se deduce que la función no se puede exportar desde una biblioteca.

En Linux, la directiva local genera algunas optimizaciones. En Windows no tiene ningún efecto. Fue introducido por compatibilidad con
Kylix.

14.9.10 MS_ABI_Default
El modificador MS_ABI_Default se utiliza para indicar que el compilador debe utilizar la versión de Microsoft de la convención de llamada
x86­64. Esta es la convención de llamadas predeterminada para Win64 y solo se admite en x86­64.

227
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

14.9.11 MS_ABI_CDecl
El modificador MS_ABI_CDecl se usa para indicar que el compilador debe usar la versión de Microsoft de la convención
de llamadas x86­64, con la excepción de que 'array of const' se interpreta como parámetros cdecl varargs, en lugar de la
matriz pascal normal de const.

14.9.12 MWPascal

El modificador MWPascal se utiliza para indicar que el compilador debe utilizar la convención de llamada Pascal de
Metrowerks. Esta convención de llamada se comporta igual que cdecl, excepto que los parámetros de registro constante
se pasan por referencia en lugar de por valor. Compatible con todas las plataformas.

14.9.13 sin retorno

El modificador noreturn se puede utilizar para indicarle al compilador que el procedimiento no regresa. El compilador
puede utilizar esta información para evitar emitir advertencias sobre variables no inicializadas o resultados que no se
configuran.

En el siguiente ejemplo, el compilador no emitirá una advertencia de que es posible que el resultado no esté configurado
en la función f:

procedimiento do_halt;noreturn; comenzar

detener(1); fin;

función f(i: entero): entero;

comenzar
si (i <0) entonces
hacer_halt
demás
resultado:=yo;
fin;

14.9.14 marco sin pila

El modificador nostackframe se puede utilizar para indicarle al compilador que no debe generar un marco de pila para
este procedimiento o función. De forma predeterminada, siempre se genera un marco de pila para cada procedimiento o
función, pero el compilador lo omitirá si puede.

Para rutinas asm (ensamblador puro), esta directiva se puede utilizar para omitir la generación de un marco de pila.

Se debe tener mucho cuidado al utilizar este modificador: la mayoría de los procedimientos o funciones necesitan un
marco de pila. Son necesarios especialmente para la depuración.

14.9.15 sobrecarga

El modificador de sobrecarga le dice al compilador que esta función está sobrecargada. Es principalmente por
compatibilidad con Delphi, ya que en Free Pascal, todas las funciones y procedimientos se pueden sobrecargar sin este modificador.

Sólo hay un caso en el que el modificador de sobrecarga es obligatorio: si se debe sobrecargar una función que reside
en otra unidad. Ambas funciones deben declararse con el modificador de sobrecarga: el modificador de sobrecarga le
dice al compilador que debe continuar buscando versiones sobrecargadas en otras unidades.

228
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

El siguiente ejemplo lo ilustra. Toma la primera unidad:

acción unitaria;

interfaz

procedimiento Hacerlo(A: Cadena); sobrecarga;

implementación

procedimiento Hacerlo(A: Cadena);

comenzar
Writeln('[Link] recibió ',A) finalizar;

fin.

Y una segunda unidad, que contiene una versión sobrecargada:

unidad ub;

interfaz

procedimiento Hacerlo(A: Entero); sobrecarga;

implementación

procedimiento DoIt(A: entero);

comenzar
Writeln('[Link] recibió ',A) finalizar;

fin.

Y el siguiente programa, que utiliza ambas unidades:

programa uab;

utiliza hacer, ub;

comenzar
DoIt('Alguna cadena'); fin.

Cuando el compilador comience a buscar la declaración de DoIt, encontrará una en la unidad ub. Sin la
directiva de sobrecarga, el compilador daría un error de discrepancia de argumentos:

inicio: >fpc [Link] [Link](6,21)


Error: tipo incompatible para el número de argumento. 1: Obtuve "Cadena constante", se
esperaba "SmallInt"

Con la directiva de sobrecarga implementada en ambas ubicaciones, el compilador sabe que debe continuar buscando
una versión sobrecargada con una lista de parámetros coincidente. Tenga en cuenta que ambas declaraciones deben tener

229
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

el modificador de sobrecarga especificado; no basta con tener el modificador en la unidad ub. Esto es para
evitar sobrecargas no deseadas: el programador que implementó la unidad ua debe marcar el procedimiento
como apto para sobrecarga.

14.9.16 pascales
El modificador Pascal se puede utilizar para declarar una función que utiliza la convención de llamada de tipo Pascal
clásica (pasando parámetros de izquierda a derecha). Para obtener más información sobre la convención de
llamadas de Pascal, consulte la Guía del programador.

14.9.17 público
La palabra clave Public se utiliza para declarar una función globalmente en una unidad. Esto es útil si no se debe
poder acceder a la función desde el archivo de la unidad (es decir, otra unidad/programa que usa la unidad no
ve la función), pero debe ser accesible desde el archivo objeto. Como ejemplo:

Unidad alguna unidad;


interfaz
Función Primera: Real;
Implementación
Función Primera: Real; comenzar

Primero := 0; fin;

Función Segunda: Real; [Público]; comenzar

Segundo := 1; fin;

fin.

Si otro programa o unidad usa esta unidad, no podrá usar la función Segundo, ya que no está declarada en la parte de
la interfaz. Sin embargo, será posible acceder a la función Segundo en el nivel del lenguaje ensamblador, utilizando su
nombre alterado (consulte la Guía del programador).

El modificador público también puede ir seguido de una directiva de nombre para especificar el nombre del ensamblador, de la
siguiente manera:

Unidad alguna unidad;


interfaz Función
Primero: Real; Función de
implementación primero:
Real; comenzar primero: = 0; fin;
Función
Segunda: Real;

Nombre público 'segundo'; comenzar segundo: = 1; fin; fin.

El símbolo del ensamblador especificado por la directiva de "nombre público" será "segundo", en todas las letras
minúsculas.

230
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

14.9.18 registrarse
La palabra clave de registro se utiliza por compatibilidad con Delphi. En la versión 1.0.x del compilador, esta directiva
no tiene ningún efecto sobre el código generado. A partir de las versiones 1.9.X, esta directiva es compatible.
Para la plataforma Intel i386, los primeros tres argumentos se pasan en los registros EAX, EDX y ECX.
Para otras plataformas, esta palabra clave no tiene ningún efecto; se utiliza la convención de llamada ABI de la plataforma predeterminada.
En general, no se recomienda el uso de esta directiva, a menos que sepa lo que está haciendo.

14.9.19 llamada segura


El modificador safecall se parece mucho al modificador stdcall. Envía parámetros de derecha a izquierda en la pila.
Además, el procedimiento llamado guarda y restaura todos los registros.

Se puede encontrar más información sobre este modificador en la Guía del Programador, en la sección sobre el
mecanismo de llamada y el capítulo sobre vinculación.

14.9.20 saveregisters El modificador

saveregisters le dice al compilador que todos los registros de la CPU deben guardarse antes de llamar a esta rutina.
Los registros de la CPU que se guardan dependen completamente de la CPU.

14.9.21 flotación suave


El modificador softfloat sólo tiene sentido en la arquitectura ARM. Le indica al compilador que consulte la convención
de llamada (específica de la plataforma) para pasar valores de punto flotante cuando el soporte de punto flotante
emulado por software está activo. (No es compatible con todas las plataformas: actualmente solo ARM).

14.9.22 llamada estándar

El modificador stdcall empuja los parámetros de derecha a izquierda en la pila y también alinea todos los parámetros
con una alineación predeterminada.

Se puede encontrar más información sobre este modificador en la Guía del Programador, en la sección sobre el
mecanismo de llamada y el capítulo sobre vinculación.

14.9.23 SYSV_ABI_Default
El modificador SYS_ABI_Default se utiliza para indicar que el compilador debe utilizar la convención de llamada
específica de x86­64 que se ajuste a System V AMD64 ABI. Esta es la convención de llamada predeterminada para
todas las plataformas x86­64 excepto Win64, y solo se admite en x86­64. .

14.9.24 SYSV_ABI_CDecl
El modificador SYSV_ABI_CDecl se usa para indicar que el compilador debe usar la convención de llamada
específica de x86­64 que se ajusta a System V AMD64 ABI, con la excepción de que 'array of const' se interpreta
como parámetros cdecl varargs, en lugar del pascal normal. conjunto de constantes.

14.9.25 Llamada vectorial


El modificador VectorCall se utiliza para indicar que el compilador debe utilizar la convención de llamada vectorial.
Esta es una variante de la convención de llamada MS_ABI_Default que pasa ciertos parámetros de punto flotante en
registros vectoriales. Solo se admite en CPU x86­64.

231
Machine Translated by Google

CAPÍTULO 14. USO DE FUNCIONES Y PROCEDIMIENTOS

14.9.26 Vararg
Este modificador sólo se puede utilizar junto con el modificador cdecl, para procedimientos externos de C. Indica que el
procedimiento acepta un número variable de argumentos después de la última variable declarada.
Estos argumentos se pasan sin ninguna verificación de tipo. Es equivalente a usar la matriz de construcción const para
procedimientos cdecl, sin tener que declarar la matriz de const. No es necesario utilizar los corchetes alrededor de los
argumentos variables cuando se utiliza esta forma de declaración.

Las siguientes declaraciones son dos formas de hacer referencia a la misma función en la biblioteca C:

Función PrintF1(fmt: pchar); cdec; varargs;


nombre 'c' externo 'printf';
Función PrintF2(fmt: pchar; Args: Matriz de const); cdec; nombre 'c' externo 'printf';

Pero hay que llamarlos de otra manera:

PrintF1('%d %d\n',1,1);
PrintF2('%d %d\n',[1,1]);

14.9.27 winapi Este

modificador le permite especificar las convenciones de llamada nativas para la plataforma actual: el compilador
seleccionará la convención de llamada correcta dependiendo de la arquitectura del sistema operativo: stdcall en windows­
i386 y cdecl en todas las demás plataformas.

El siguiente ejemplo demuestra esto:

función libusb_init(var ctx:plibusb_context):entero;winapi;libusb1 externo;

14.10 Modificadores Turbo Pascal no admitidos

Los modificadores que existen en Turbo Pascal, pero que Free Pascal no admite, se enumeran en la tabla (14.1).

Tabla 14.1: Modificadores no admitidos

Modificador ¿Por qué no es compatible?


Cerca Free Pascal es un compilador de 32 bits.
Lejos Free Pascal es un compilador de 32 bits.

El compilador dará una advertencia cuando encuentre estos modificadores, pero de lo contrario los ignorará por completo.

232
Machine Translated by Google

Capítulo 15

Sobrecarga del operador

15.1 Introducción

Free Pascal admite la sobrecarga de operadores. Esto significa que es posible definir la acción de algunos
operadores sobre tipos autodefinidos y así permitir el uso de estos tipos en expresiones matemáticas.
Además, la sobrecarga de operadores se puede utilizar para algunas acciones especiales que, estrictamente
hablando, no son operadores en el sentido matemático de la palabra. Esto es cierto para los procedimientos Inc y
Dec (que se pueden utilizar sólo como procedimiento) y para la asignación implícita y explícita (:= y encasillamiento).
El operador enumerador (para definir una serie enumerable para un tipo) también forma parte de esta clase de operadores.

Definir la acción de un operador es muy parecido a la definición de una función o procedimiento, sólo que
existen algunas restricciones en las posibles definiciones, como se mostrará a continuación.
La sobrecarga de operadores es, en esencia, una poderosa herramienta de notación; pero tampoco es más que eso,
ya que se pueden obtener los mismos resultados con llamadas a funciones regulares. Al utilizar la sobrecarga de
operadores, es importante tener en cuenta que algunas reglas implícitas pueden producir resultados inesperados.
Esto estará indicado.

Hay 2 formas de declarar sobrecargas de operadores. La primera forma es la forma original implementada
en Free Pascal y es la más poderosa. Se analiza en este capítulo. También existe una segunda forma,
que sólo se puede implementar para registros avanzados (o extendidos), y se analiza en el capítulo sobre
registros extendidos.

15.2 Declaraciones del operador

Definir la acción de un operador es muy parecido a definir una función:

Definiciones del operador

­ definición de operador asignación de operador definición de operador aritmético


­
definición de operador de
comparación definición de operador
lógico definición de otro
operador : tipo de resultado ;
­ identificador de resultado bloque de subrutina
­

233
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

­­ definición del operador de asignación := ­


( parámetro de valor )
explícito

­­ definición de operador aritmético +


­
( lista de parámetros )
­
*
/
**
><

­­ definición del operador de comparación = ­


( lista de parámetros )
<
<=
>
>=
<>
en

­
­­ definición de operador lógico y ( lista de parámetros )
o
gratis

no

­
­­ otro enumerador de definición de operador ( lista de parámetros )

aum dec

La lista de parámetros para un operador de comparación o un operador aritmético siempre debe contener
dos parámetros, con la excepción del menos o más unario, donde solo se necesita un parámetro. El tipo
de resultado de los operadores de comparación debe ser booleano.
Los tipos simples definidos por el usuario se pueden utilizar para operadores, así como para registros y matrices. Existen algunas
limitaciones en la sobrecarga de operadores:

• Esta forma de declarar operadores sólo se admite en los modos ObjFPC y FPC.

• No es posible definir operadores en clases.

• Los operadores + y ­ no se pueden definir en tipos enumerados.

• Cuando el interruptor de modo ArrayOperators está activo:

{$modeSwitch ArrayOperadores}

entonces el operador + no se puede sobrecargar en matrices dinámicas, porque el compilador lo


maneja internamente.

Observación Al compilar en modo Objfpc, es posible que se elimine el identificador de resultado. El resultado puede ser entonces
Se accede a través del símbolo de resultado estándar.

Si se elimina el identificador de resultado y el compilador no está en uno de estos modos, se producirá un error de sintaxis.
ocurrir.

El bloque de declaraciones contiene las declaraciones necesarias para determinar el resultado de la operación.
Puede contener fragmentos de código arbitrarios de gran tamaño; se ejecuta siempre que la operación se encuentra
en alguna expresión. El resultado del bloque de instrucciones siempre debe estar definido; El compilador no verifica
las condiciones de error y el código debe ocuparse de todos los casos posibles, generando un error de tiempo de
ejecución si se encuentra alguna condición de error.

234
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

A continuación se examinarán los tres tipos de definiciones de operadores. Como ejemplo, a lo largo de este capítulo
se utilizará el siguiente tipo para definir operadores sobrecargados en:

tipo
complejo = registro re: real;
soy real; fin;

Este tipo se utilizará en todos los ejemplos.

Las fuentes de la biblioteca en tiempo de ejecución contienen dos unidades que utilizan en gran medida la sobrecarga de operadores:

ucomplex Esta unidad contiene un cálculo completo para números complejos.

matriz Esta unidad contiene un cálculo completo para matrices.

15.3 Operadores de asignación

El operador de asignación define la acción de asignar un tipo de variable a otro. El tipo de resultado debe coincidir con
el tipo de variable a la izquierda de la declaración de asignación, el parámetro único del operador de asignación debe
tener el mismo tipo que la expresión a la derecha del operador de asignación.

Este sistema se puede utilizar para declarar un nuevo tipo y definir una asignación para ese tipo. Por ejemplo, para
poder asignar un tipo recién definido "Complejo"

Era
C,Z: Complejo; // Nuevo tipo complejo

comenzar
Z:=C; // asignaciones entre tipos complejos. fin;

Habría que definir el siguiente operador de asignación:

Operador := (C : Complejo) z : complejo;

Para poder asignar un tipo real a un tipo complejo de la siguiente manera:

era
R: real;
C: complejo;

comenzar
C:=R;
fin;

Se debe definir el siguiente operador de asignación:

Operador := (r : real) z : complejo;

Como puede verse en esta declaración, define la acción del operador := con a la derecha una expresión real y a la
izquierda una expresión compleja.

Un ejemplo de implementación de esto podría ser el siguiente:

235
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

operador := (r : real) z : complejo;

comenzar
[Link]:=r;
[Link]:=0,0; fin;

Como se puede ver en el ejemplo, el identificador de resultado (z en este caso) se utiliza para almacenar el resultado
de la asignación. Al compilar en modo Delphi o en modo ObjFPC, también se permite el uso del identificador especial
Result, que puede sustituirse por la z, por lo que lo anterior equivaldría a

operador := (r : real) z : complejo;

comenzar
[Link]:=r;
[Link]:=0.0; fin;

El operador de asignación también se utiliza para convertir tipos de un tipo a otro. El compilador considerará todos los
operadores de asignación sobrecargados hasta que encuentre uno que coincida con los tipos de expresiones de la mano
izquierda y de la mano derecha. Si no se encuentra dicho operador, se genera un error de "no coincidencia de tipos".

Observación El operador de asignación no es conmutativo; el compilador nunca invertirá el papel de los dos argumentos. En otras
palabras, dada la definición anterior de operador de asignación, no es posible lo siguiente:

era
R: real;
C: complejo;

comenzar
R:=C;
fin;

Si la asignación inversa fuera posible, entonces también se debe definir el operador de asignación para eso. (Esto no
es así para los números reales y complejos).

Observación El operador de asignación también se utiliza en conversiones de tipos implícitos. Esto puede tener efectos no deseados.
Considere las siguientes definiciones:

operador := (r : real) z : complejo; función exp(c: compleja):


compleja;

Entonces la siguiente asignación dará una discrepancia de tipo:

Era
r1,r2: real;

comenzar
r1:=exp(r2); fin;

La discrepancia se produce porque el compilador encontrará la definición de la función exp con el argumento complejo.
Implícitamente convierte r2 en un complejo, por lo que puede usar la función exp anterior.
El resultado de esta función es un complejo que no se puede asignar a r1, por lo que el compilador dará

236
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

un error de "tipo no coincidente". El compilador no buscará más exp que tenga los argumentos correctos.

Es posible evitar este problema particular especificando

r1:=[Link](r2);

Al realizar una conversión explícita, el compilador intentará una conversión implícita si hay un operador de
asignación presente. Eso significa que

Era
R1: T1;
R2: T2;

comenzar
R2:=T2(R1);

Será manejado por un operador.

Operador := (aDerecha: T1) Res: T2;

Sin embargo, se puede definir un operador explícito y luego se utilizará cuando el compilador encuentre un
encasillamiento.

Lo contrario no es cierto: en una asignación normal, el compilador no considerará operadores de asignación


explícitos.
Dadas las siguientes definiciones:

usos
utilidades del sistema;

tipo
TTest1 = registro

f: Int largo; fin;

TTest2 = registro

f: cadena; fin;

TTest3 = registro
f: booleano; fin;

Es posible crear operadores de asignación:

operador := (aDerecha: TTest1) Res: TTest2; comenzar Writeln('TTest1


implícito
=> TTest2'); Res.f := IntToStr(aRight.f); fin;

operador := (aDerecha: TTest1) Res: TTest3; comenzar Writeln('TTest1


implícito
=> TTest3'); Res.f := aDerecha.f <> 0; fin;

237
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

Pero también se pueden definir operadores de encasillamiento:

operador Explícito(aDerecha: TTest2) Res: TTest1; comenzar Writeln('TTest2


explícito
=> TTest1'); Res.f := StrToIntDef(aRight.f, 0); fin;

operador Explícito(aDerecha: TTest1) Res: TTest3; comenzar Writeln('TTest1


explícito
=> TTest3'); Res.f := aDerecha.f <> 0; fin;

Así, el siguiente código

era
t1: TTprueba1;
t2: TTprueba2;
t3: TTprueba3;

comenzar t1.f :=
42; // Implícito
t2 := t1; // se
utilizará una operación teóricamente explícita, pero implícita, // porque no se define ningún
operador explícito t2 := TTest2(t1); // lo siguiente no se compilaría, //
no se ha definido ningún
operador de asignación (aquí no se utilizará uno
explícito) //t1 := t2; // T1 explícito := TTest1(t2); // primero explícito (TTest2 => TTest1) luego implícito (TTest1
=> TTest3) t3 :=
TTest1(t2); //
implícito t3 := t1; // explícito
t3 := TTest3(t1); fin.

producirá el siguiente resultado:

TTest1 implícito => TTest2


TTest1 implícito => TTest2
TTest2 explícito => TTest1
TTest2 explícito => TTest1
TTest1 implícito => TTest3
TTest1 implícito => TTest3
TTest1 explícito => TTest3

15.4 Operadores aritméticos


Los operadores aritméticos definen la acción de un operador binario. Las posibles operaciones son:

238
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

multiplicación Para multiplicar dos tipos, el operador de multiplicación * debe estar sobrecargado.

división Para dividir dos tipos, el operador / división debe estar sobrecargado.

suma Para agregar dos tipos, el operador de suma + debe estar sobrecargado. Tenga en cuenta que + no se puede utilizar
para matrices dinámicas, ya que se trata de una operación interna del compilador para la concatenación de matrices
(consulte también el interruptor de modo ARRAYOPERATORS).

resta Para restar dos tipos, el operador de resta ­ debe estar sobrecargado.

exponenciación Para exponenciar dos tipos, el operador de exponenciación ** debe estar sobrecargado.

El menos unario se utiliza para tomar el negativo del argumento que le sigue.

Diferencia simétrica Para tomar la diferencia simétrica de 2 estructuras, el operador >< debe ser
sobrecargado.

La definición de un operador aritmético toma dos parámetros, excepto el menos unario, que solo necesita un parámetro.
El primer parámetro debe ser del tipo que ocurre a la izquierda del operador, el segundo parámetro debe ser del tipo
que está a la derecha del operador aritmético. El tipo de resultado debe coincidir con el tipo que resulta después de la
operación aritmética.

Para compilar una expresión como

era
R: real;
C,Z: complejo;

comenzar
C:=R*Z; fin;

Se necesita una definición del operador de multiplicación como:

Operador * (r : real; z1 : complejo) z : complejo;

comenzar
[Link] := [Link] * r; [Link] := [Link] *
r; fin;

Como puede verse, el primer operador es real y el segundo es complejo. El tipo de resultado es complejo.

La multiplicación y suma de reales y complejos son operaciones conmutativas. El compilador, sin embargo, no tiene
noción de este hecho, por lo que incluso si se define una multiplicación entre un real y un complejo, el compilador no
usará esa definición cuando encuentre un complejo y un real (en ese orden). Es necesario definir ambas operaciones.

Entonces, dada la definición anterior de multiplicación, el compilador no aceptará la siguiente declaración:

era
R: real;
C,Z: complejo;

comenzar
C:=Z*R; fin;

239
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

Dado que los tipos de Z y R no coinciden con los tipos en la definición del operador.

La razón de este comportamiento es que es posible que una multiplicación no siempre sea conmutativa.
[Link]. la multiplicación de una matriz (n,m) por una matriz (m,n) dará como resultado una matriz (n,n), mientras que la multiplicación
de una matriz (m,n) por una matriz (n,m) dará como resultado una matriz (m ,m) matriz, que no tiene por qué ser la misma en
todos los casos.

15.5 Operador de comparación

El operador de comparación se puede sobrecargar para comparar dos tipos diferentes o para comparar dos tipos
iguales que no son tipos básicos. Si los operandos no son tipos simples, el tipo de resultado de un operador de
comparación no siempre tiene que ser booleano, pero no se pueden usar en una declaración if, repetición o while.

Los operadores de comparación que se pueden sobrecargar son:

igual a (=) Para determinar si dos variables son iguales.

desigual a (<>) Para determinar si dos variables son diferentes.

menor que (<) Para determinar si una variable es menor que otra.

mayor que (>) Para determinar si una variable es mayor que otra.

mayor o igual a (>=) Para determinar si una variable es mayor o igual a otra.

menor o igual a (<=) Para determinar si una variable es mayor o igual a otra.

Si no hay un operador separado para desigual a (<>), entonces, para evaluar una declaración que contiene el
operador desigual a, el compilador usa el operador igual a (=) y niega el resultado. Lo contrario no es cierto: si no
existe un operador “igual a” pero sí “no igual a”, el compilador no lo utilizará para evaluar una expresión que
contenga el operador igual (=).

A modo de ejemplo, el siguiente operador permite comparar dos números complejos:

operador = (z1, z2: complejo) b: booleano;

la definición anterior permite comparaciones de la siguiente forma:

Era
C1,C2: Complejo;

comenzar
Si C1=C2 entonces
Writeln('C1 y C2 son iguales'); fin;

La definición del operador de comparación necesita dos parámetros, con los tipos que el operador debe comparar.
Aquí también el compilador no aplica la conmutatividad: si los dos tipos son diferentes, entonces es necesario
definir dos operadores de comparación.

En el caso de números complejos, es necesario, por ejemplo, definir 2 comparaciones: una con el tipo complejo
primero y otra con el tipo real primero.
Dadas las definiciones

operador = (z1: complejo;r: real) b: booleano; operador = (r: real; z1: complejo)
b: booleano;

240
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

Son posibles las dos comparaciones siguientes:

Era
R,S: Real;
C: Complejo;

comenzar
Si (C=R) o (S=C) entonces
Writeln('Aceptar'); fin;

Tenga en cuenta que el orden del tipo real y complejo en las dos comparaciones está invertido.

El siguiente ejemplo muestra que no es necesario que el tipo de resultado sea booleano:

Tipo
TMyRec = registro a,b : entero; fin;

operador = (x,y: TMyRec) r: cadena;

comenzar
si (xa=ya) y (xb=yb) entonces
R:='igual' más

R:='diferir'; fin;

era
x,y : TMyRec;

comenzar: = 1;
de:=1;
Escribe (x = y); de:=2;
de:=3;
Escribe (x
= y); fin.

Cuando se ejecute, este ejemplo imprimirá

iguales
difieren

obviamente, una declaración como

si (x=y) entonces
writeln('Igual');

No se compilará, ya que la declaración if necesita una verificación booleana:

Error: tipos incompatibles: se obtuvo "ShortString" y se esperaba "Boolean"

241
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

15.6 En operador

A partir de la versión 2.6 de Free Pascal, el operador In también puede sobrecargarse. El primer argumento
del operador in debe ser el operando a la izquierda de la palabra clave in. Lo siguiente sobrecarga el
operador in para registros:

{$modo objfpc}{$H+}

tipo
TMyRec = registro A: extremo entero;

operador en (const A: Entero; const B: TMyRec): booleano; comenzar Resultado := A = BA; fin;

era
R: TMyRec;

comenzar RA :=
10; Writeln(1 en R); // false Writeln(10 en
R); // verdadero final.

El operador in también se puede sobrecargar para otros tipos distintos de los ordinales, como en el siguiente
ejemplo:

{$modo objfpc}{$H+}

tipo
TMyRec = registro A: extremo entero;

operador en (const A: TMyRec; const B: TMyRec): booleano; comenzar Resultado := AA =


BA; fin;

era
S,R: TMyRec;

comenzar RA :=
10; SA:=1;
Escribir(S en R); // false Writeln(R en
R); // verdadero final.

15.7 Operadores lógicos

Los operadores lógicos and, or, xor y not pueden sobrecargarse. Estos operadores se utilizan normalmente en
tipos simples de dos maneras diferentes:

1. Como operadores booleanos, en cuyo caso el resultado es booleano (excepto no)

242
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

2. Como operadores bit a bit, en cuyo caso el resultado es de tipo ordinal.

Al sobrecargar estos operadores, el tipo de resultado no está restringido. Esto significa que puede definir los
operadores como operadores lógicos booleanos:

Tipo
Rec = grabar

a,b: booleano; fin;

Operador y (r1,r2: Rec) z: booleano;

comenzar z:=(R1.a y R2.a) o (R1.b y r2.b); fin;

Operador o (r1,r2: Rec) z: booleano;

comenzar z:=(R1.a o R2.a) y (R1.b o r2.b) terminar;

Operador xor (r1,r2: Rec) z: booleano;

comenzar z:=(R1.a xor R2.a) y (R1.b xor r2.b) terminar;

Operador no (r1: Rec) z: rec;

comenzar zA:=no R1.a;


zB:=no R1.b; fin;

era
r1,r2 : Rec;

comenzar Writeln(r1 y r2);


Writeln(r1 o r2); Writeln(r1
xor r2); Writeln((no r1).a);

fin.

Pero también es posible tener diferentes tipos de devolución:

Operador y (r1,r2: Rec) z: cadena;

comenzar Str(Ord((R1.a y R2.a) o (R1.b y r2.b)),Z); fin;

243
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

El compilador siempre verificará el tipo de retorno para determinar el tipo final de una expresión, y se
verificará la seguridad de tipos de las asignaciones.

15,8 operadores Inc/Dic

Los procedimientos Inc y Dec son, a efectos de optimización, intrínsecos del compilador. Se pueden anular de
la forma habitual declarando un procedimiento:

procedimiento Inc(var s: Cadena);

comenzar Inc(S,'.');
fin;

procedimiento Inc(var s: Cadena; T: Cadena);

comenzar
S:=S+T;
fin;

Pero debido a que son intrínsecos del compilador, también se pueden implementar como operadores para tipos personalizados:

operador Inc (s,T: Cadena): z: cadena;

comenzar
Z:=S+T;
fin;

15.9 El operador enumerador

El operador enumerador se puede utilizar para definir un enumerador para cualquier tipo. Debe devolver
una clase, objeto o registro extendido que tenga la misma firma que la interfaz IEnumerator. Tenga en
cuenta que el compilador no reconoce este operador en el modo de sintaxis de Delphi.
El siguiente código definirá un enumerador para el tipo Integer:

Tipo
TEvenEnumerator = Clase
FActual: Entero;
FMax: Entero;
Función MoveNext: Booleana;
Propiedad actual: entero leído FCurrent; fin;

Función [Link]: booleano;

comenzar
FActual:=FActual+2;
Resultado:=FActual<=FMax; fin;

244
Machine Translated by Google

CAPÍTULO 15. SOBRECARGA DEL OPERADOR

operador enumerador (i: entero): TEvenEnumerator;

comenzar
Resultado:=[Link];
[Link]:=i; fin;

era
I: Entero; metro :
Entero = 4;

comenzar
Porque yo en M hago
Escribir(i);
fin.

El bucle imprimirá todos los números pares distintos de cero, menores o iguales al enumerable. (2 y 4 en el
caso del ejemplo).
Puede encontrar más información sobre los enumeradores y el operador enumerador en la sección 13.2.5, página 195.

245
Machine Translated by Google

Capítulo 16

Programas, unidades, bloques.

Un programa Pascal puede constar de módulos llamados unidades. Una unidad se puede utilizar para agrupar
fragmentos de código o para darle código a alguien sin proporcionar las fuentes. Tanto los programas como las unidades
constan de bloques de código, que son mezclas de declaraciones, procedimientos y declaraciones de variables o tipos.

16.1 Programas

Un programa Pascal consta del encabezado del programa, seguido posiblemente de una cláusula de "usos" y un bloque.

Programas

­ encabezado del programa; bloquear . ­


utiliza cláusula

­­ encabezado del programa identificador del programa ­


(parámetros del programa)

­­ lista de identificadores de parámetros del programa ­

­­ utiliza cláusulas de uso 6 identificador ; ­


en cadena literal
,

El encabezado del programa se proporciona para compatibilidad con versiones anteriores y el compilador lo ignora.

La cláusula de usos sirve para identificar todas las unidades que necesita el programa. Todos los identificadores
que se declaran en la sección de interfaz de las unidades en la cláusula de usos se agregan a los identificadores
conocidos del programa. La unidad del sistema no tiene que estar en esta lista, ya que siempre la carga el compilador.

El orden en que aparecen las unidades es importante, determina en qué orden se inicializan.
Las unidades se inicializan en el mismo orden en que aparecen en la cláusula de usos. Los identificadores se buscan
en el orden opuesto, es decir, cuando el compilador busca un identificador, busca primero en la última unidad de la
cláusula de usos, luego en la penúltima, y así sucesivamente. Esto es importante en caso de que dos unidades declaren
tipos diferentes con el mismo identificador.

El compilador buscará versiones compiladas o versiones fuente de todas las unidades en la cláusula de usos en la ruta de
búsqueda de unidades. Si el nombre del archivo de la unidad se mencionó explícitamente usando la palabra clave in, la fuente
se toma del nombre del archivo especificado:

246
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

programa programab;

usa unita en '..\[Link]';

unita se busca en el directorio principal del directorio de trabajo actual del compilador. Puede agregar una
directiva {$UNITPATH ..} para asegurarse de que se encontrará la unidad sin importar dónde se encuentre
el directorio de trabajo actual del compilador.
Cuando el compilador busca archivos de unidad, agrega la extensión .ppu al nombre de la unidad. En LINUX y en
sistemas operativos donde los nombres de archivos distinguen entre mayúsculas y minúsculas al buscar una unidad, se
utiliza el siguiente mecanismo:

1. Primero se busca la unidad en el estuche original.

2. La unidad se busca en letras minúsculas.

3. La unidad se busca en letras mayúsculas.

Además, si el nombre de una unidad tiene más de 8 caracteres, el compilador primero buscará un nombre de unidad
con esta longitud y luego truncará el nombre a 8 caracteres y lo buscará nuevamente. Por motivos de compatibilidad,
esto también se aplica a las plataformas que admiten nombres de archivos largos.

Tenga en cuenta que la búsqueda anterior se realiza en cada directorio de la ruta de búsqueda.

El bloque de programa contiene las declaraciones que se ejecutarán cuando se inicie el programa. Tenga en cuenta que
estas declaraciones no tienen por qué ser necesariamente las primeras declaraciones que se ejecutan: el código de
inicialización de las unidades también puede contener declaraciones que se ejecutan antes que el código del programa.

La estructura de un bloque de programa se analiza a continuación.

16,2 unidades

Una unidad contiene un conjunto de declaraciones, procedimientos y funciones que pueden ser utilizadas por un
programa u otra unidad. La sintaxis de una unidad es la siguiente:

Unidades

­­ unidad parte de implementación de la parte de la interfaz del encabezado de la unidad ­


­ fin . ­

parte de inicialización parte de finalización


comienzo 6 declaración
;

.
­­ unidad de encabezado de unidad ?identi cador de unidad ; ­
directivas de pistas

­­ interfaz parte interfaz 6 ­


utiliza cláusula parte de declaración constante
parte de declaración de tipo
parte de declaración de variable
parte de declaración de propiedad
parte de encabezados de procedimiento

247
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

­­ encabezados de procedimiento parte encabezado de procedimiento ; ­


encabezado de función modificadores de llamadas;

­­ implementación de la parte de implementación parte de la declaración ­


utiliza cláusula

­­ inicialización de la parte de inicialización 6 declaración ­


;

­­ finalización de la parte de finalización 6 declaración ­


;

Como puede verse en el diagrama de sintaxis, una unidad siempre consta de una interfaz y una parte de
implementación. Opcionalmente, hay un bloque de inicialización y un bloque de finalización, que contienen
código que se ejecutará cuando se inicie el programa y cuando se detenga, respectivamente.
Tanto la parte de la interfaz como la parte de la implementación pueden estar vacías, pero se deben especificar las
palabras clave Interfaz e implementación. La siguiente es una unidad completamente válida;

unidad a;

interfaz

implementación

fin.

La parte de la interfaz declara todos los identificadores que deben exportarse desde la unidad. Pueden ser identificadores
de constantes, tipos o variables, así como declaraciones de identificadores de procedimientos o funciones. La parte de la
interfaz no puede contener código que se ejecuta: solo se permiten declaraciones. La siguiente es una parte de interfaz
válida:

unidad a;

interfaz

utiliza b;

Función MiFunción: SomeBType;

Implementación

El tipo SomeBType se define en la unidad b.


Todas las funciones y métodos que se declaran en la parte de interfaz deben implementarse en la parte
de implementación de la unidad, excepto las declaraciones de funciones o procedimientos externos. Si
un método o función declarado no se implementa en la parte de implementación, el compilador dará un
error, por ejemplo el siguiente:

unidos unidos;

interfaz

Función MiFunción: Entero;

248
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

implementación

fin.

Dará como resultado el siguiente error:

[Link](5,10) Error: Declaración directa no resuelta "MyFunction:SmallInt;"

La parte de implementación está destinada principalmente a la implementación de las funciones y procedimientos


declarados en la parte de la interfaz. Sin embargo, también puede contener declaraciones propias: las declaraciones
dentro de la parte de implementación no son accesibles fuera de la unidad.

La parte de inicialización y finalización de una unidad es opcional.

El bloque de inicialización se utiliza para inicializar determinadas variables o ejecutar código necesario para el
correcto funcionamiento de la unidad. Las partes de inicialización de las unidades se ejecutan en el orden en que
el compilador cargó las unidades al compilar un programa. Se ejecutan antes de que se ejecute la primera
declaración del programa.

La parte de finalización de las unidades se ejecuta en el orden inverso a la ejecución de inicialización. Se utilizan,
por ejemplo, para limpiar cualquier recurso asignado en la parte de inicialización de la unidad o durante la vida útil
del programa. La parte de finalización siempre se ejecuta en el caso de una terminación normal del programa: ya
sea porque se alcanza el final en el código del programa o porque se ejecutó una instrucción de detención en algún
lugar.

En caso de que el programa se detenga durante la ejecución de los bloques de inicialización de una de las
unidades, sólo se finalizarán las unidades que ya estaban inicializadas. Tenga en cuenta que, a diferencia de
Delphi, en Free Pascal puede haber un bloque de finalización sin un bloque de inicialización. Eso significa que lo
siguiente se compilará en Free Pascal, pero no en Delphi.

Finalización
Unidad de limpieza;
fin.

Una sección de inicialización por sí sola (es decir, sin finalización) puede simplemente reemplazarse por un bloque de
instrucciones. Es decir, lo siguiente:

Inicialización
Unidad de inicialización;
fin.

es completamente equivalente a

Comenzar InicializarUnidad;
fin.

16.3 Espacios de nombres: unidades punteadas

Como se puede ver en el diagrama de sintaxis de una unidad, el nombre de una unidad puede contener puntos. Esto significa que las
unidades se pueden organizar en espacios de nombres.

Entonces, la siguiente es una declaración de unidad correcta:

unidad ab;

249
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

interfaz

Función C: número entero;

implementación

Función C: número entero;

comenzar
Resultado:=1;
fin;

fin.

La unidad se puede utilizar de la siguiente manera:

programa d;

utiliza ab;

comenzar Writeln(c);
fin.

Al resolver símbolos, los alcances de las unidades siempre tienen prioridad sobre los símbolos dentro de las unidades.

Dadas las siguientes unidades:

unidad mi unidad;

interfaz

era
registro de la prueba

a: entero largo;
fin;

implementación

inicialización
prueba.a:=2; fin.

unidad [Link];

interfaz

era
a: entero largo;

implementación

inicialización

250
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

a:=1;
fin.

El siguiente programa resolverá [Link].a en la variable a en la unidad [Link]:

usos
miunidad, [Link];

comenzar Writeln('[Link].a: ',[Link].a); fin.

Entonces imprimirá:

[Link].a: 1

Invertir el orden de las unidades no cambiará esto:

usos
[Link], miunidad;

comenzar Writeln('[Link].a: ',[Link].a);


fin.

también imprimirá

[Link].a: 1

De manera similar, el siguiente programa resolverá [Link].a en la variable a en la unidad [Link]:

usos
[Link], miunidad;

comenzar
Writeln('a : ',a);
fin.

imprimirá:

una: 1

De manera similar, el siguiente programa resolverá test.a en la variable test.a en la unidad myunit:

usos
[Link], miunidad;

comenzar Writeln('prueba.a: ',prueba.a); fin.

imprimirá

prueba.a: 2

251
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

16.4 Dependencias de unidades

Cuando un programa usa una unidad (digamos unidadA) y esta unidad usa una segunda unidad, digamos unidadB, entonces el
programa depende indirectamente también de la unidadB. Esto significa que el compilador debe tener acceso a la unidad B
cuando intenta compilar el programa. Si la unidad no está presente en el momento de la compilación, se produce un error.

Tenga en cuenta que los identificadores de una unidad de la que depende indirectamente un programa no son accesibles
para el programa. Para tener acceso a los identificadores de una unidad, la unidad debe estar en la cláusula de usos del
programa o unidad donde se necesitan los identificadores.

Las unidades pueden ser mutuamente dependientes, es decir, pueden hacer referencia entre sí en sus cláusulas de uso.
Esto está permitido, siempre que al menos una de las referencias esté en la sección de implementación de la unidad.
Esto también se aplica a las unidades indirectas mutuamente dependientes.

Si es posible comenzar desde una cláusula de usos de interfaz de una unidad y regresar allí a través de cláusulas
de usos de interfaces únicamente, entonces existe una dependencia circular de la unidad y el compilador generará
un error. Por ejemplo, no se permite lo siguiente:

Unidad UnidadA;
interfaz
Utiliza la Unidad B;
final de la implementación.

Unidad UnidadB
interfaz
Utiliza UnidadA;
final de la implementación.

Pero esto está permitido:

Unidad UnidadA;
interfaz
Utiliza la Unidad B;
final de la implementación.

Unidad UnidadB
implementación
Utiliza UnidadA;
fin.

Porque UnitB usa UnitA solo en su sección de implementación.

En general, es una mala idea tener interdependencias entre unidades, aunque sea sólo en las secciones de implementación.

16,5 bloques

Las unidades y programas están formados por bloques. Un bloque está formado por declaraciones de etiquetas, constantes,
tipos, variables y funciones o procedimientos. Los bloques se pueden anidar de determinadas maneras, es decir, una
declaración de procedimiento o función puede tener bloques en sí mismos. Un bloque se parece a lo siguiente:

Bloques

252
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

­­ parte de declaración de bloque parte de declaración ­

­­ declaración parte 6 ­
parte de declaración de
etiqueta parte de declaración
constante parte de declaración de cadena
de recursos parte de
declaración de tipo parte de
declaración de variable parte de declaración
de variable de subproceso parte de declaración de procedimiento/función

­­ etiqueta de parte de declaración de etiqueta 6 etiqueta ; ­


,

­­ declaración constante parte constante 6 declaración constante ­


declaración constante escrita

­­ parte de declaración de cadena de recursos cadena de recursos 6 declaración constante de cadena ­


­ ­

­­ declaración de tipo parte tipo 6 declaración de tipo ­

­­ parte de declaración de variable var 6 declaración de variables ­

­­ declaración de variable de subproceso parte subproceso 6 declaración de variables ­

­­ declaración de procedimiento/función parte 6 declaración de procedimiento ­


declaración de función
declaración de constructor
declaración de destructor

­­ declaración parte declaración compuesta ­

Las etiquetas que se pueden utilizar para identificar declaraciones en un bloque se declaran en la parte de declaración de
etiquetas de ese bloque. Cada etiqueta sólo puede identificar una declaración.

Las constantes que se van a utilizar sólo en un bloque deben declararse en la parte de declaración de constantes de ese
bloque.

Las variables que se van a utilizar sólo en un bloque deben declararse en la parte de declaración de variables de ese
bloque.

Los tipos que se van a utilizar sólo en un bloque deben declararse en la parte de declaración de tipo de ese bloque.

Por último, las funciones y procedimientos que se utilizarán en ese bloque se pueden declarar en la parte de
declaración de procedimiento/función.

Estas cuatro partes de la declaración se pueden mezclar, no hay ningún orden requerido aparte de que no se pueden
usar (o hacer referencia a) identificadores que aún no han sido declarados.

Después de las diferentes partes de la declaración viene la parte de la declaración. Contiene cualquier acción que el bloque deba
ejecutar. Todos los identificadores declarados antes de la parte de la declaración se pueden utilizar en esa parte de la declaración.

253
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

16.6 Alcance

Los identificadores son válidos desde el momento de su declaración hasta el final del bloque en el que se produjo la
declaración. El rango donde se conoce el identificador es el alcance del identificador. El alcance exacto de un identificador
depende de la forma en que se definió.

16.6.1 Alcance del bloque El

alcance de una variable declarada en la parte de declaración de un bloque es válido desde el punto de declaración hasta
el final del bloque. Si un bloque contiene un segundo bloque en el que se vuelve a declarar el identificador, dentro de este
bloque la segunda declaración será válida. Al salir del bloque interior, la primera declaración vuelve a ser válida. Considere
el siguiente ejemplo:

Demostración del programa;


Donde X: Real;
{X es una variable real}
Procedimiento Nueva Declaración
Var X: Entero; { Redeclarar X como entero} comenzar // X := 1.234; {daría un
error al
intentar compilar}
X := 10; {Asignación correcta} fin; { De aquí en

adelante, X vuelve a ser Real} start X := 2.468;

fin.

En este ejemplo, dentro del procedimiento, X denota una variable entera. Tiene su propio espacio de almacenamiento,
independiente de la variable X fuera del procedimiento.

16.6.2 Alcance del registro Los

identificadores de campo dentro de una definición de registro son válidos en los siguientes lugares:

1. Hasta el final de la definición del registro.

2. Designadores de campo de una variable del tipo de registro dado.

3. Identificadores dentro de una declaración With que opera en una variable del tipo de registro dado.

16.6.3 Alcance de la clase Un

identificador de componente (uno de los elementos en la lista de componentes de la clase) es válido en los siguientes lugares:

1. Desde el momento de la declaración hasta el final de la definición de clase.

2. En todos los tipos descendientes de esta clase, a menos que esté en la parte privada de la declaración de clase.

3. En todos los bloques de declaración de métodos de esta clase y clases descendientes.

4. En una declaración With que opera en una variable de la definición de clase dada.

Tenga en cuenta que los designadores de métodos también se consideran identificadores.

254
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

16.6.4 Alcance de la unidad

Todos los identificadores en la parte de interfaz de una unidad son válidos desde el momento de la declaración hasta el
final de la unidad. Además, los identificadores son conocidos en programas o unidades que tienen la unidad en su cláusula
de usos.

Los identificadores de unidades indirectamente dependientes no están disponibles. Los identificadores declarados en la parte de
implementación de una unidad son válidos desde el momento de la declaración hasta el final de la unidad.

La unidad del sistema se utiliza automáticamente en todas las unidades y programas. Por lo tanto, sus identificadores siempre
se conocen en cada programa, biblioteca o unidad de Pascal.

Las reglas del alcance de la unidad implican que se puede redefinir el identificador de una unidad. Para tener acceso
a un identificador de otra unidad que fue redeclarada en la unidad actual, preceda con el nombre de esa otra unidad,
como en el siguiente ejemplo:

unidad unidadA;
interfaz
Tipo
MiTipo = Real;
final de la implementación.

programa progresivo;
Utiliza UnidadA;

{ Redeclaración de MyType}
Escriba MiTipo = Entero; {Será un
Var A: Mi tipo; B: número entero}
[Link] {Será real} comienzo fin.

Esto es especialmente útil al volver a declarar los identificadores de la unidad del sistema.

16.7 Bibliotecas

Free Pascal admite la creación de bibliotecas dinámicas (DLL en Win32 y OS/2) mediante el uso de la palabra clave
Biblioteca.

Una Biblioteca es como una unidad o un programa:

Bibliotecas

­­ encabezado de biblioteca biblioteca ; bloquear . ­


utiliza cláusula

­­encabezado de biblioteca identificador de biblioteca ­

De forma predeterminada, las funciones y procedimientos que se declaran e implementan en la biblioteca no están
disponibles para un programador que desee utilizar esta biblioteca.

Para que funciones o procedimientos estén disponibles desde la biblioteca, deben exportarse en una cláusula de
exportación:

255
Machine Translated by Google

CAPÍTULO 16. PROGRAMAS, UNIDADES, BLOQUES

Cláusula de exportación

­­ cláusula de exportaciones exportaciones lista de exportaciones ; ­

­­ lista de exportaciones 6 entradas de exportaciones


­
,

­­ identificador de entrada de exportaciones ­


­ índice entero constante constante de cadena de nombre
­

En Win32, se puede agregar una cláusula de índice a una entrada de exportación. Una entrada de índice debe
ser un número positivo mayor o igual que 1 y menor que MaxInt.

Opcionalmente, una entrada de exportaciones puede tener un especificador de nombre. Si está presente, el especificador de nombre proporciona el

nombre exacto (distingue entre mayúsculas y minúsculas) mediante el cual se exportará la función desde la biblioteca.

Si ninguna de estas construcciones está presente, las funciones o procedimientos se exportan con los nombres exactos como se
especifica en la cláusula de exportaciones.

256
Machine Translated by Google

Capítulo 17

Excepciones

Las excepciones proporcionan una manera conveniente de programar errores y mecanismos de recuperación de errores, y están
estrechamente relacionadas con las clases. El soporte de excepciones se basa en tres construcciones:

Levantar declaraciones. Para plantear una excepción. Esto generalmente se hace para señalar una condición de error.
Sin embargo, también se puede utilizar para cancelar la ejecución y regresar inmediatamente a un punto conocido
en el ejecutable.

Pruebe... Excepto bloques. Estos bloques sirven para detectar excepciones generadas dentro del alcance del bloque y para
proporcionar código de recuperación de excepciones.

Prueba... Finalmente bloquea. Estos bloques sirven para forzar la ejecución del código independientemente de si
ocurre o no una excepción. Generalmente sirven para limpiar memoria o cerrar archivos en caso de que
ocurra una excepción. El compilador genera muchos bloques Try... Finalmente implícitos alrededor del
procedimiento, para forzar la coherencia de la memoria.

Observación Debido a que las excepciones usan clases, necesita uno de los modos Pascal de objeto para poder usarlas:

{$MODO OBJFPC}

{$MODO DELPHI}

17.1 La declaración de aumento

La declaración de aumento es la siguiente:

elevar declaración

­­ aumento de declaración de aumento ­


instancia de excepción
dirección de excepción

­­ dirección de excepción en dirección de excepción ­


, expresión de dirección

257
Machine Translated by Google

CAPITULO 17. EXCEPCIONES

Esta declaración generará una excepción. Si se especifica, la instancia de excepción debe ser una instancia
inicializada de cualquier clase, que es el tipo de aumento. La dirección de excepción y el marco son opcionales.
Si no se especifican, el compilador proporcionará la dirección por sí mismo. Si se omite la instancia de excepción,
se vuelve a generar la excepción actual. Esta construcción sólo se puede utilizar en un bloque de manejo de
excepciones (ver más adelante).

Observación El control nunca regresa después de un bloque de excepción. El control se transfiere al primer intento...finalmente o
intento...excepto la declaración que se encuentra al desenrollar la pila. Si no se encuentra dicha declaración, la biblioteca
de tiempo de ejecución de Free Pascal generará un error de tiempo de ejecución 217 (consulte también la sección 17.5,
página 261). La dirección de excepción se imprimirá mediante las rutinas de manejo de excepciones predeterminadas.

A modo de ejemplo: la siguiente división comprueba si el denominador es cero y, de ser así, genera una excepción
de tipo EDivException

Escriba EDivException = Clase (Excepción);


Función DoDiv(X,Y: Entero largo): Entero; comenzar

Si Y=0 entonces
Levante [Link] ('Se produciría una división por cero'); Resultado := X División Y; fin;

La clase Exception se define en la unidad Sysutils del rtl. (sección 17.5, página 261)

Observación Aunque la clase Exception se utiliza como clase base para las excepciones en todo el código, esto es solo un
acuerdo no escrito: la clase puede ser de cualquier tipo y no necesita ser descendiente de la clase Exception.

Por supuesto, la mayor parte del código depende del acuerdo no escrito de que una clase de excepción desciende
de Exception.

El siguiente código muestra cómo omitir una rutina de informe de errores de la pila que se muestra en el controlador
de excepciones:

{$mode objfpc} usa


sysutils;

error de procedimiento (mensaje constante: cadena);

comenzar a generar excepció[Link](Msg) en


get_caller_addr(get_frame),
get_caller_frame(get_frame); fin;

prueba de procedimiento2;

comenzar error('Error'); fin;

comenzar
prueba2;
fin.

El programa, cuando se ejecute, mostrará un seguimiento de la siguiente manera:

Se produjo una excepción no controlada en $00000000004002D3:

258
Machine Translated by Google

CAPITULO 17. EXCEPCIONES

Excepción: error
$00000000004002D3 línea 15 de [Link] $00000000004002E6
línea 19 de [Link]

La línea 15 está en el procedimiento Prueba2, no en Error, que en realidad generó la excepción.

17.2 La declaración try...except

Un bloque de manejo de excepciones try...except tiene la siguiente forma:

Pruebe... excepto la declaración

­­ declaración de prueba lista de declaraciones de prueba excepto que finalizan los controladores de excepciones ­

­­ lista de declaraciones 6 declaración ­


;

­­ manejadores de excepciones ­
6 controlador de excepciones
; otra lista de declaraciones
lista de declaraciones

­­ controlador de excepciones activado identificador de tipo de clase hacer declaración ­


identificador:

Si no se genera ninguna excepción durante la ejecución de la lista de declaraciones, todas las declaraciones de
la lista se ejecutarán secuencialmente y el bloque de excepción se omitirá, transfiriendo el flujo del programa a
la declaración después del final.

Si ocurre una excepción durante la ejecución de la lista de declaraciones, el flujo del programa se transferirá al
bloque de excepción. Las declaraciones en la lista de declaraciones entre el lugar donde se generó la excepción
y el bloque de excepción se ignoran.

En el bloque de manejo de excepciones, se verifica el tipo de excepción, y si hay un controlador de excepciones


donde el tipo de clase coincide con el tipo de objeto de excepción, o es un tipo principal del tipo de objeto de
excepción, entonces la declaración que sigue al Do correspondiente ser ejecutado. Se utiliza el primer tipo
coincidente. Después de ejecutar el bloque Do, el programa continúa después de la instrucción End.

El identificador en una declaración de manejo de excepciones es opcional y declara un objeto de excepción. Se


puede utilizar para manipular el objeto de excepción en el código de manejo de excepciones. El alcance de esta
declaración es el bloque de declaraciones que sigue a la palabra clave Do.

Si ninguno de los controladores On coincide con el tipo de objeto de excepción, entonces se ejecuta la lista de
instrucciones después de else. Si no se encuentra dicha lista, la excepción se vuelve a generar automáticamente. Este
proceso permite anidar intentos... excepto bloques.

Si, por otro lado, se detectó la excepción, entonces el objeto de excepción se destruye al final del bloque de
manejo de excepciones, antes de que continúe el flujo del programa. La excepción se destruye mediante una
llamada al destructor Destroy del objeto.

Como ejemplo, dada la declaración anterior de la función DoDiv, considere lo siguiente

Intentar

Z := DoDiv (X,Y);
Excepto

259
Machine Translated by Google

CAPITULO 17. EXCEPCIONES

En EDivException haga Z: = 0; fin;

Si Y resulta ser cero, entonces el código de función DoDiv generará una excepción. Cuando esto sucede, el flujo
del programa se transfiere a la declaración excepto, donde el controlador de excepciones establecerá el valor de
Z en cero. Si no se genera ninguna excepción, el flujo del programa continúa más allá de la última declaración
final. Para permitir la recuperación de errores, se admite el bloque Intentar... Finalmente. Un bloque Try...Finally
garantiza que se garantice la ejecución de las declaraciones que siguen a la palabra clave Finalmente, incluso si
se produce una excepción.

17.3 La declaración try... nally

Una declaración Try...Finally tiene la siguiente forma:

Prueba... finalmente declaración

­­ trystatement lista de declaraciones de prueba finalmente finalmente terminan las declaraciones ­

­­ finalmente declaraciones lista de declaraciones ­

Si no ocurre ninguna excepción dentro de la Lista de declaraciones, entonces el programa se ejecuta como si las palabras
clave Try, Finalmente y End no estuvieran presentes, a menos que se dé un comando de salida: un comando de salida
primero ejecuta todas las declaraciones en los bloques finalmente antes de salir realmente.

Sin embargo, si ocurre una excepción, el flujo del programa se transfiere inmediatamente desde el punto
donde se generó la excepción a la primera declaración de las declaraciones Finalmente.
Todas las declaraciones después de la palabra clave finalmente se ejecutarán y luego la excepción se volverá
a generar automáticamente. Se omiten todas las declaraciones entre el lugar donde se planteó la excepción y
la primera declaración de las declaraciones finales.
Como ejemplo considere la siguiente rutina:

Procedimiento Doit (Nombre: cadena); Var F:


Texto; comenzar
Asignar
(F,Nombre); Reescribir
(nombre); Pruebe...

Manejo de archivos...
Finalmente
cerrar (F); fin;
fin;

Si durante la ejecución del manejo del archivo ocurre una excepción, entonces el flujo del programa continuará
en la declaración de cierre (F), omitiendo cualquier operación de archivo que pueda seguir entre el lugar donde
se generó la excepción y la declaración de cierre. Si no se produce ninguna excepción, se ejecutarán todas las
operaciones del archivo y el archivo se cerrará al final.

Tenga en cuenta que una declaración de salida encerrada en un bloque try...finalmente seguirá ejecutando el bloque
finalmente. Reutilizando el ejemplo anterior:

260
Machine Translated by Google

CAPITULO 17. EXCEPCIONES

Procedimiento Doit (Nombre: cadena);


Era
F: Texto; B:
booleano; comenzar
B:=Falso;
Asignar
(F,Nombre); Reescribir
(nombre); Pruebe //...

Manejo de archivos... si B entonces

salida; // Detener el procesamiento prematuramente


// Más manejo de archivos
Finalmente
Cerrar(F); fin;
fin;

El archivo seguirá cerrado, incluso si el procesamiento finaliza prematuramente mediante la instrucción Exit.

17.4 Anidamiento de manejo de excepciones

Es posible anidar bloques Try...Except con bloques Try...Finally. El flujo del programa se realizará de acuerdo con
un principio lifo (último en entrar, primero en salir): el código del último bloque Try...Except o Try...Finally encontrado
se ejecutará primero. Si la excepción no se detecta, o fue una declaración finalmente, el flujo del programa se
transferirá al penúltimo bloque, ad in nitum.

Si se produce una excepción y no hay ningún controlador de excepciones presente que maneje esta excepción,
se generará un error de tiempo de ejecución 217. Cuando se utiliza la unidad SysUtils, se instala un controlador
predeterminado que mostrará el mensaje del objeto de excepción y la dirección donde ocurrió la excepción,
después de lo cual el programa saldrá con una instrucción Detener.

17.5 Clases de excepción

La unidad sysutils contiene una gran cantidad de manejo de excepciones. Define la clase de excepción base,
Exception.

Excepción = clase (TObject) mensaje


privado:
cadena; contextofhelp: entero
largo; constructor público crear (const
msg:
cadena); constructor createres(sangría: entero largo); propiedad
contexto de ayuda: entero largo leer contexto de ayuda escribir
contexto de ayuda; mensaje de propiedad: cadena leer fmessage escribir fmessage; fin;

ExceptClass = Clase de excepción;

Y utiliza esta declaración para definir un buen número de excepciones, por ejemplo:

{excepciones matemáticas}

261
Machine Translated by Google

CAPITULO 17. EXCEPCIONES

EIntError = clase(Excepción);
EDivByZero = clase(EIntError);
ERangeError = clase(EIntError);
EIntOverflow = clase(EIntError);
EMathError = clase(Excepción);

La unidad SysUtils también instala un controlador de excepciones. Si un bloque de manejo de excepciones


no controla una excepción, la biblioteca Run­Time llama a este controlador. Básicamente, imprime la dirección
de excepción, imprime el mensaje del objeto de excepción y sale con un código de salida de 217. Si el objeto
de excepción no es un objeto descendiente del objeto de excepción, entonces se imprime el nombre de la
clase en lugar del mensaje de excepción.
Se recomienda utilizar el objeto de excepción o una clase descendiente para todas las declaraciones de aumento, ya
que entonces se puede utilizar el campo de mensaje del objeto de excepción.

262
Machine Translated by Google

Capítulo 18

Usando ensamblador

Free Pascal admite el uso de ensamblador en el código, pero no de macros de ensamblador en línea. Para obtener
más información sobre la sintaxis del ensamblador específica del procesador y sus limitaciones, consulte la Guía del
programador.

18.1 Declaraciones del ensamblador

El siguiente es un ejemplo de inclusión de ensamblador en código Pascal.

...
Declaraciones;
...
Asamblea

el código asm aquí


...
fin;
...
Declaraciones;

Las instrucciones del ensamblador entre las palabras clave Asm y end se insertarán en el ensamblador
generado por el compilador. Los condicionales se pueden utilizar en código ensamblador, el compilador los
reconocerá y los tratará como cualquier otro condicional.

18.2 Procedimientos y funciones del ensamblador

Los procedimientos y funciones del ensamblador se declaran mediante la directiva Assembler. Esto permite que el
generador de código realice una serie de optimizaciones de generación de código.

El generador de código no genera ningún marco de pila (código de entrada y salida de la rutina) si no
contiene variables locales ni parámetros. En el caso de funciones, los valores ordinales deben devolverse
en el acumulador. En el caso de los valores de coma flotante, éstos dependen del procesador de destino y
de las opciones de emulación.

263
Machine Translated by Google

Índice

Resumen, 83 Directivas
resumen, 76 Pista, 16
Dirección, 176 Despacho, 100
Alias, 225 DespachoStr, 101
Ansisting, 34, 39 Doble, 31
Matriz, 43, 213, 214
Constructor, 48 demás, 190, 192

Dinámico, 44 excepto, 259, 261


De constante, 214 Excepción, 257
Estático, 43 Excepciones, 257

matriz, 61 Atrapando, 257, 259

Constructor de matrices, 48 Clases, 261

Asamblea, 206 Manejo, 260, 261


Ensamblador, 206, 224, 263 Crianza, 257
exportación, 226
bloque, 252 Expresión, 203
booleano, 28 Expresiones, 169
Extendido, 31
Caso, 190 Registros extendidos, 142
cdec, 226 Externo, 223
Char, 32 externo, 67, 223
Clase, 86, 96
Ayudantes de clase, 149 Campos, 50, 77
clases, 86 Archivo,
Página de códigos, 34, 36 53 finalmente, 260, 261
Conversión, 36 Para, 193, 195
COM, 61, 121 hasta , 193 en ,
Comentarios, 13 195
Comp, 31 hasta, 193
Const, 23 Delantero, 56, 222
cuerda, 23 Función, 208
Constantes, 21 Funciones, 207
Ordinario, 21 Ensamblador, 224, 263
Cuerda, 19, 22, 39 Externo, 223
Mecanografiado, 22 Adelante, 222
Constructor, 79, 93, 173 Modificadores, 224
CURVA, 61, 121 sobrecargado, 221
cppdecl, 226
moneda, 31 genérico, 124
Genéricos, 123
Delfos Sobrecargas, 131
Compatibilidad, 129 Restricciones de tipo, 128
Sintaxis, 129
Tipo sobrecargas, 131 flotador duro, 226

Destructor, 79, 93 Directivas de pistas, 16

264
Machine Translated by Google

ÍNDICE

Identificadores, 16 crecimiento en, 232

Si, 192 MS_ABI_CDecl, 228


índice, 104, 223 MS_ABI_Default, 227
Heredado, 95 MWPascal, 228
heredado, 82, 109 en
línea, 226 nombre, 223

interfaz, 114 noreturn, 228

Interfaces, 61, 63, 114 nostackframe, 228

COM, 121 Números, 17

CORBA, 121 binario, 18

Implementaciones, 116 decimales, 18

interrupción, 227 hexadecimal, 18

iocheck, 227 octal, 18


reales, 17
Etiquetas, 19
Bibliotecas, 255 objeto, 75

biblioteca, 255 Objetivo­Pascal, 159


locales, 227 Clases objetivas de Pascal, 159
Objetos, 75
Mensaje, 100 Objetos, abstractos, 76.
mensaje, 100 Objetos sellados, 76
Métodos, 80, 94 Operadores, 21, 40, 55, 169, 176, 177
Resumen, 83 Aritmética, 177, 238
Clase, 96 Asignación, 235
Mensaje, 100 Binario, 238
Estático, 81, 98 booleano, 178
virtuales, 81, 83, 95 Comparación, 240
Modificadores, 15, 224, 232 Matrices dinámicas, 179
Alias, 225 Lógico, 178
cdecl, 226 Relacional, 182
cppdecl, 226 Juego, 180
exportación, Cuerda, 179
226 hardfloat, 226 Unario, 177
en línea, 226 operadores, 233
interrupción, 227 en caso contrario,
iocheck, 227 190 sobrecarga,
local, 227 228 operadores
MS_ABI_CDecl, 228 de sobrecarga, 233
MS_ABI_Default, 227 Anulación, 96
MWPascal, 228 anulación, 82
noreturn, 228
nostackframe, 228 Empaquetado, 50, 76, 93

sobrecarga, 228 Parámetros, 209

pascal, 230 Constante, 209, 212

público, 230 Matriz abierta, 213, 214

registro, 231 Fuera, 211

llamada segura, Referencia contada, 217

231 saveregisters, 231 Sin escribir, 216


softfloat, 231 Destipos, 209
stdcall, 231 Valor, 209

SYSV_ABI_CDecl, 231 Var, 104, 209, 210

SYSV_ABI_Default, 231 variables, pascales, 230

232 PCar, 35, 39

VectorLlamada, 231 Puntero, 54

265
Machine Translated by Google

ÍNDICE

Privado, 85, 88, 104 Ir a, 188 si,


estricto, 88 192
privado, 76 Bucle, 193, 195, 202, 203
Procesal, 56 Procedimiento, 187
Procedimiento, 56, 207 Repetir, 202
Trámites, programa Sencillo, 186
207 , 246 Estructurado, 189
Propiedades, 71, 102 Mientras, 203
Matriz, 105 con, 204
Indexado, 104 Métodos de clase estática, 98
Propiedad, 97, 103 stdcall, 231
Protegido, 85, 88 cuerda, 19
Público, 85, 88 Ansistring, 34
público, 76, 230 Página de códigos, 34

Publicado, 88, 104 Página de códigos, 36

PUnicodeChar, 39 constante, 39
Multibyte, 38
Subir, 257 Cadena de bytes sin procesar, 38
Cadena de bytes sin formato, 38 cuerda corta, 33
Leer, 103 Cadena de un solo byte, 32
verdadero, 31
Cadena Unicode, 38
Récord, 50 Cadena UTF8, 38
constante, 69 Cadena ancha, 39
Ayudantes de registro, Símbolos, 12
149 registros, Diagramas de sintaxis, 10
231 reintroducciones, 96 SYSV_ABI_CDecl, 231
Repetir, 202 SYSV_ABI_Default, 231
Palabras reservadas, 14
Delfos, 15 años Texto, 53
Modificadores, 15 luego, 192
Turbo Pascal, 14 Variables de hilo, 71
Cadena de recursos, 23 Threadvar, 71
Fichas, 12
llamada segura, Comentarios, 13
231 guardar registros, 231 Identificadores, 16
Alcance, 35, 46, 68, 75, 85, 87, 254 bloque, Números, 17
254 Palabras reservadas, 14
Clase, 254 Cuerdas, 19
récord, 254 Símbolos, 12
unidad, 255 intentos, 260, 261
sellado, 76 Tipo, 25
Yo, 80, 97 Compatibilidad, 28, 47, 64, 132, 174, 176,
conjunto, 53 210
Cuerda corta, 33 Genéricos, 123, 124
Sencillo, 31 Ayudantes, 149
softfloat, 231 Restricciones, 128
especializados, 126 Especializado, 126
Declaraciones, 186 Tipo sobrecargas, 131
Ensamblador, 206, 263 Encasillado, 34, 35, 39, 174, 175
Tarea, 186 No alineado, 175
Caso, 190 Valor, 174
Compuesto, 189 variable, 175
Excepción, 205 tipos, 25
Para, 193, 195 Ansistring, 34

266
Machine Translated by Google

ÍNDICE

Matriz, 43, 44 Mientras, 203


base, 25 Ancho Char, 32
booleano, 28 Cuerda ancha, 39
Char, 31, 32 winapi, 232
Clase, 86 con, 204
Ayudantes de clase, 149 escribir, 103
Constructor, 48
Enumeración, 29
Registro ampliado, 142
Archivo, 53
Declaración anticipada, 56
Entero, 26
Objeto, 75
Ordinario, 26
PCar, 35, 40
Puntero, 40, 54
Procesal, 56
Cadena de bytes sin formato, 38

verdadero, 31

Récord, 50
Asistentes de registro, 149
Referencia contada, 34, 36, 38, 44, 46, 122
conjunto, 53

cuerda corta, 33
Cuerda, 32
estructurado, 41
Subrango, 30
Escriba ayudantes, 149
Cadena Unicode, 38
Cadena UTF8, 38
Variante, 61
Cuerda ancha, 39

Unicodestring, 38
unidades, 247,
255 usos, 246
Cadena UTF8, 38

Var, 66
varargs, 232
variable, 66
variables, 66
Inicializado, 22, 68, 70
Variante, 61
VectorLlamada, 231
virtuales, 79, 81, 95, 100
Visibilidad, 75, 85, 114
Privado, 75, 88
Protegido, 88
Público, 75, 88
Publicado, 88
Privado estricto, 88
Estrictamente protegido, 88

267

También podría gustarte