Declaración de Constantes, const y volatile.Funciones inline.Operadores New y Delete.
Declaración de constantes
Una constante es como una variable pero como su
nombre indica, no varía si no que es constante. Se usa fundamentalmente para
definir valores que serán constantes a lo largo de todo el programa pero en
vez de poner el mismo valor una y otra vez a lo largo del código, lo
definimos sólo una vez al principio, de este modo si hubiera que cambiar
dicho valor no es necesario rastrear todo el código buscando donde cambiarlo,
basta con cambiar el valor de la constante definida al principio. Esto es
válido para cualquier lenguaje aunque los ejemplos se pongan para C++.
Calificador const
Se
puede anteponer el especificador const a la declaración de cualquier objeto o tipo de datos, con el fin
de hacer que el objeto sea en lugar de una variable, una constante.
const int
k = 12;
const int
v[v] = { 1,2,3,4 };
A un
objeto declarado como constante no se le puede asignar un valor más que al
principio en su declaración. Al declararlo si una constante k ha sido declarada
como constante las siguientes sentencias
darían error:
k = 100; //
error
k++; // error
Si declaramos
un puntero precedido por const, esto hace que el objeto apuntado sea una
constante. Pero no pasa lo mismo con el puntero.
const char *pc =
"abcd";
pc[0] = 'z' ; // error
pc = "efg"; //
correcto
Si en
lugar de esto queremos declarar un puntero como una constante, procederíamos
así.
char *const pc =
"abcd";
pc[0] = 'z'; // correcto
pc = " efg"; // error
Para
hacer que tanto el puntero como el objeto apuntado sean constantes,
procederemos como se indica a continuación:
const char
*const pc = "abcd";
pc[0] = 'z'; //error
pc = "efg"; // error
Una
variable global declarada como const se considera static.
Calificador volatile
Si
anteponemos el calificador volatile a la declaración de un objeto, hacemos que
dicho objeto pueda ser modificado por otros procesos diferentes al programa
actual. Una utilidad del calificador volatile es proveer acceso a las posiciones
de memoria utilizadas por procesos
asíncronos, tal como manejadores de interrupciones. Los calificadores const
y volatile pueden utilizarse conjuntamente o individualmente.
volatile int v;
Los
objetos declarados volátiles no se pueden utilizar en optimizaciones porque sus
valores pueden cambiar en cualquier momento. Cada vez que accedemos a un objeto
volatile, el sistema lee su valor actual. Además en una asignación, su valor es
escrito inmediatamente. Cuando se declara explícitamente un objeto volatile, es
un error que la función miembro invocada por este objeto no sea también
volatile.
Funciones en línea
C++
provee el especificador de función llamado inline que cuando precede al
nombre de la función hace que el compilador reemplace cualquier llamada a la
función por el cuerpo actual de la función. Para utilizar esta funcionalidad,
la función debe estar definida antes de ser invocada, en caso contrario, el
compilador no la expandirá. Debido a esta restricción, las funciones inline son
normalmente definidas en ficheros de cabecera .h.
Una
función como inline solamente es una recomendación para el compilador. Es decir,
el compilador puede tomar la iniciativa de expandir o no la función, por
ejemplo, por ser demasiado larga.
Las
funciones inline son similares a las macros declaradas con #define. Pero
es mejor utilizar funciones inline que macros, ya que sus parámetros son
chequeados automáticamente y no presentan los problemas de las macros
parametrizadas.
//Comparación
de una función en línea con una macro.
#include
<iostream.h>
#define
MENOR (X, Y) ((X) < (Y)) ? (X) : (Y)
inline int
menor ( int x, int y)
{
return ((x < y) ? x : y);
}
void main()
{
int m, a = 20, b = 30;
m = MENOR ( a--, b--); // efecto colateral. El valor menor //se decrementa dos veces
cout
<< "menor = " << m;
cout
<< " a = " << a << " b = " << b
<< endl;
a = 20; b = 30;
m = menor(a--, b--);
cout
<< " menor = " << m;
cout
<< "a = " << a << " b = " << b
<< endl;
}
Este ejemplo da lugar al siguiente resultado:
menor = 9 a = 8 b = 19
menor = 10 a = 9 b = 19
Las
funciones inline se ejecutan más rápidamente, ya que se evitan las llamadas a
cada una de estas funciones. Sin embargo, el abuso de inline en ocasiones puede
no ser bueno. Ya que la modificación de una función inline obligaría a
recompilar todos los módulos en los que ésta apareciera. Además el tamaño del
código puede aumentar extraordinariamente. Se recomienda utilizar solamente el
especificador inline cuando la función es muy pequeña o si se llama desde pocos
lugares.
Operadores new y delete
Para
manejar la memoria libre, C++ dispone de los operadores new y delete.
Operador new
El
operador new asigna recursos de memoria para un objeto del tipo y tamaño
especificados. En el caso de arrays, el tamaño se especifica explícitamente. En
otros casos el tamaño viene definido por el propio tipo. El operador new
devuelve un puntero a un objeto del tipo especificado que referencia el espacio
reservado. Si no hay espacio para crear un objeto del tipo especificado, el
operador new devuelve un puntero nulo ( 0 o NULL ).
int n_elementos;
cin >> n_elementos;
int *a = new int
[n_elementos]; // creación //dinámica del array a
long *pl = new long;
// asignación para un //long, notación
sin paréntesis
float *pf = new(float);
//asignación para //un float, notación funcional
struct
complejo
{
float
re, im;
};
complejo *pc = new complejo; // asignación //para una estructura
Si el
tipo especificado es un array, el operador new devolverá un puntero al primer
elemento de dicho array. Las dimensiones para crear un array pueden ser
expresiones.
int cols =
10;
int filas = 5;
double **puntArr
= new double[filas * 2][cols];
En este
ejemplo declara un puntero puntArr a un array de dos dimensiones dadas por las por las
expresiones filas * 2 y cols.
Cuando
en la expresión que sigue al operador new para especificar el tipo aparece
entre paréntesis, es obligatorio utilizar la versión con paréntesis del
operador new (notación funcional).
int (**puntArr fn)( );
puntArr fn
= new(int (*[20] )( ));
En
este ejemplo, new asigna memoria
para un array de punteros de 20 elementos a funciones que no requieren
argumentos y que devuelven un entero.
Si en
la anterior instrucción no se utiliza la notación funcional, el compilador
muestra un error. Es decir.
new int (*[30]
)( ); // error
Es un
error que se analiza como:
( new int )(*[30] )( ); // error
El
operador global ::new aparece originalmente declarado como void
*operador new ( size_t tamaño_en_bytes_del_objeto);
Según esto
en los ejemplos anteriores no se ha declarado explícitamente el tamaño en
bytes. Esto se debe a que es el sistema el que realiza automáticamente el
cálculo a partir del tipo definido.
El
operador new asigna memoria de un área de memoria del programa conocida como "free
store".
Sobrecargando new
Una
clase también puede gestionar por si misma el espacio de memoria libre que se necesita
para la creación de objetos. Para ello es necesario definir los operadores new
y delete como funciones miembro de la clase. El operador new llamará a la
función operator new, y el operador delete llamará a la función operator
delete. Cuando se sobrecarguen estos
operadores, la definición de estas funciones necesitan unos requerimientos que
se comentan a continuación.
Asignar
memoria: operator new
Si en
un programa encontramos una instrucción como la siguiente, se invoca la función
operator new para asignar un espacio del mismo tamaño especificado en
bytes. Una llamada al operador new invoca una llamada al constructor de la
clase.
int
*pint = new int [TMAX];
Si no hubiera
suficiente espacio de memoria para la asignación requerida, esta función devolverá NULL.
Cuando
el operador new se utiliza para asignar memoria para un objeto de una clase que
no tiene definido el operador new, o para un array de cualquier tipo se invoca
a la función global ::new. Este operador se define de la forma.
void
*operator new (new_t tamaño);
En el
caso de que operador new se utilice para asignar memoria de un objeto de una
clase con un operador new definido, entonces se invoca a la función operator
new de esa clase:
nombre_clase::operador new;
La función
operator new definida en una clase es necesario que sea una función
miembro estática por lo tanto, no puede ser virtual, pues debe devolver void* y
tener un primer argumento del tipo size_t. Este argumento recibirá
automáticamente un valor en bytes igual al tamaño a asignar. El tipo size_t se
encuentra definido en el fichero de cabecera stddef.h.
Al ejecutar
new, el compilador busca una definición para este operador en la clase del
objeto a crear; si no la encuentra ejecuta el operador global ::new
Operador delete
El
operador delete destruye un objeto previamente creado por el operador new, de
este modo libera la memoria que ocupaba dicho objeto. El operador delete
sólo se puede aplicar a punteros si estos han sido retornados a través del
operador new. Un puntero a una constante no se puede borrar. Sin embargo, si se
puede aplicar delete a un puntero nulo ( un puntero con valor cero ).
int
*p, *parray;
...
p = new
int [n];
parray
= new int [n];
...
delete p;
delete [ ] parray;
En las
líneas de arriba, la primera intrucción delete libera la memoria apuntada por p
y asignada a un entero. La segunda línea delete libera la memoria apuntada por
parray y asignada a un array.
La
sintaxis varía en el caso de tener que liberar la memoria ocupada por un array.
En este caso, el compilador deberá invocar al destructor apropiado para cada
elemento del array, desde parray[0] a parray[n-1].
El
operador global ::delete aparece originalmente declarado de la forma
siguiente:
void *operator delete(void *puntero_al_objeto);
void *operator delete(void *puntero_al_objeto, size_t
tamaño);
Se
puede ver que no se ha declarado explícitamente el tamaño en bytes. Esto es así
porque es el sistema el que realiza automáticamente el cálculo.
Liberar memoria: operator delete
Cuando
en un programa se encuentra una sentencia como alguna de las siguientes, la
función operator delete se
invoca para liberar la memoria asignada por new. Una llamada al operator delete
ejecuta una llamada del destructor de la clase.
delete c1;
delete []c3;
Se
puede escribir la función miembro operator delete de dos formas:
void
operator delete (void *);
void
operator delete (void *, size_t );
La
función operator delete puede ser de ámbito global, o de la clase. La función
global, en caso de ser definida, toma un único argumento de tipo void*. La
función operator delete con dos parámetros es muy útil cuando la utilizamos
desde su clase base para eliminar un objeto de una clase derivada. Es necesario
que una función operator delete definida en una clase tiene que ser una función
miembro estática ( por lo tanto, no puede ser virtual ), esta debe tener como
tipo del resultado void y un primer argumento de tipo void*. También se puede
añadir un segundo argumento de tipo size_t, el cual debemos inicializar con el
compilador con el tamaño en bytes del objeto direccionado por el primer
argumento. El tipo size_t se encuentra definido en el archivo de cabecera stddef.h.
Este cursillo continúa aqui:
No hay comentarios:
Publicar un comentario