2. Apuntadores
Un apuntador es una variable que
almacena la dirección de la memoria de
otra variable.
La variable apuntador es única ya que
salva la dirección de la memoria de otra
variable
La dirección de memoria es la localización
específica en la memoria principal donde
existe una variable durante la ejecución de
un programa
Mtl. Lourdes Cahuich 2
3. Apuntadores
Se usan apuntadores para acceder y
manipular indirectamente otras variables.
La quot;indirecciónquot; permite la creación de las
estructuras de datos complejas y
algoritmos poderosos.
Por ejemplo, sin apuntadores e indirección
no sería posible crear una estructura de
datos de la lista encadenada.
Mtl. Lourdes Cahuich 3
4. Operaciones Básicas
Declaración e Inicialización
La declaración de una variable apuntador
requiere el uso de cierta sintaxis
desconocida.
Una declaración del apuntador debe prefijar
su nombre de variable con un asterisco (*),
esto significa para el compilador que la
variable declarada es un apuntador
Mtl. Lourdes Cahuich 4
5. Operaciones Básicas
Aunque los apuntadores solamente
almacenan direcciones de la memoria, la
declaración de una variable apuntador
debe especificar un tipo de datos
específico cuya dirección de memoria
pueda almacenar el apuntador; esto es
indispensable para poder manipular la
variable indirectamente
Mtl. Lourdes Cahuich 5
6. Operaciones Básicas
La inicialización requiere sintaxis nueva;
para esto se requiere obtener a dirección
de memoria de la variable.
El operador address-of (&) regresa la
dirección de la memoria de donde está
almacenada la variable
Mtl. Lourdes Cahuich 6
7. De-referenciar (Dereference)
El acceso indirecto y la manipulación de
variables usando apuntadores se logra
usando el operador de-reference.
Se aplica el operador de-reference (*)
para acceder o modificar el valor de la
variable señalada por un apuntador.
Mtl. Lourdes Cahuich 7
8. De-referenciar
Los programadores pueden invocar
funciones miembro de un objeto a través
de un apuntador.
El operador de la flecha (->) primero de-
referencia el apuntador y en seguida
invoca la función miembro nombrada.
Mtl. Lourdes Cahuich 8
9. De-referenciar
Solamente podemos de-referenciar de
forma segura los apuntadores que
señalan hacia direcciones válidas de
memoria.
De-referenciar apuntadores que no se han
inicializado a direcciones de memoria
válidas causan un error de ejecución.
Mtl. Lourdes Cahuich 9
10. Una técnica utilizada para evitar este
problema es la inicialización de variables
apuntador al puntero nulo.
El puntero nulo representa un lugar en la
memoria que no puede ser de-
referenciado.
Esto evita que una de-referenciación
accidental pueda causar un error de
ejecución
Mtl. Lourdes Cahuich 10
11. El apuntador nulo es referenciado usando
la macro NULL
También se usan apuntadores nulos como
valores para indicar el fin de alguna
estructura o secuencia.
Mtl. Lourdes Cahuich 11
12. Aritmética del Apuntador
La aritmética del apuntador es el uso de
sumas y restas para cambiar la posición
de memoria que un apuntador almacena.
Estas operaciones son concernientes a la
posición de la memoria actualmente
almacenada en un apuntador
Si se agrega o resta un número de
memoria incluyendo el tamaño requerido
por el tipo de dato
Mtl. Lourdes Cahuich 12
13. Aritmética del Apuntador
C++ realiza esta multiplicación de
memoria automática
La aritmética del apuntador es una forma
de acceder diferentes posiciones en una
estructura de datos
La aritmética del apuntador tiene muchos
usos uno de ellos es el recorrido de
arreglos (traversal of arrays).
Mtl. Lourdes Cahuich 13
14. Mecanismos de paso de
parámetros
Paso de Parámetros por Valor
El paso de parámetros por valor es el
mecanismo default de paso de parámetros en
C++, se crea y se entrega una copia del
parámetro a la función
Esto es importante, debido a que si
realizamos un cambio a un parámetro que
sea pasado por valor, la variable original
permanece sin cambios ya que éstos se
realizan en una copia de la variable original
Mtl. Lourdes Cahuich 14
15. Paso de Parámetros por
Referencia
A diferencia del paso de parámetros por
valor, no se hacen copias de las variables
que son pasadas por referencia.
En su lugar, una función llamada recibe
una referencia, o alias, al parámetro real
provisto por la función llamada.
Por esta razón, el paso por referencia se
utiliza para construir funciones que
pueden modificar las variables en la
función llamada
Mtl. Lourdes Cahuich 15
16. Mecanismos de paso de
parámetros
La sintaxis del paso de parámetros por
referencia se declara incluyendo el prefijo
& en la declaración del parámetro a recibir
Aquí el signo quot;" señala al compilador que
este parámetro debe ser pasado por
referencia
Mtl. Lourdes Cahuich 16
17. Mecanismos de paso de
parámetros
El paso de un parámetro por referencia
también se utiliza como mecanismo para
pasar objetos grandes a las funciones.
Cuando los objetos son grandes, el paso
por valor puede dar lugar a operaciones
que consumen demasiado tiempo al sacar
copias.
Mtl. Lourdes Cahuich 17
18. Mecanismos de paso de
parámetros
El paso por referencia es más eficiente
porque no implica el sacar copias.
Incluso cuando una función no intenta
modificar uno de sus parámetros, se debe
usar el paso por referencia cuando el
parámetro es un objeto grande
Mtl. Lourdes Cahuich 18
19. Mecanismos de paso de
parámetros
A veces es útil pasar un apuntador por
referencia.
Esto se hace cuando una función necesita
cambiar la posición almacenada de
memoria del apuntador.
Es decir se pasa un apuntador a una
función por referencia cuando la función
necesita reposicionar el apuntador.
Mtl. Lourdes Cahuich 19
20. Mecanismos de paso de
parámetros
Un error que comúnmente se comete al
regresar por referencia, es regresar una
referencia a una variable local.
El ciclo de vida de una variable local
termina cuando la función en la que la
variable esta definida se termina de
ejecutar.
Mtl. Lourdes Cahuich 20
21. Mecanismos de paso de
parámetros
Regresar una referencia a algo que ya no
existe puede generar resultados
inesperados.
Mtl. Lourdes Cahuich 21
22. Administración de la Memoria
Dinámica
El Almacén Libre
Cada programa de C++ tiene lo que se llama
el quot;almacén librequot;.
El almacén libre, que a veces se le llama
quot;heapquot; es un área de la memoria de un
programa que se utiliza dinámicamente
Mtl. Lourdes Cahuich 22
23. Administración de la Memoria
Dinámica
Usar memoria dinámicamente significa
que la cantidad de memoria requerida
para una tarea es especificada en el
tiempo de ejecución, en lugar del tiempo
de compilación
Las variables creadas en el almacén libre
tienen dimensión dinámica.
Mtl. Lourdes Cahuich 23
24. Administración de la Memoria
Dinámica
La dimensión de una variable describe
cuánto tiempo permanece la variable
alrededor en un programa
Las variables locales en funciones tienen
dimensión local, se crean cuando se llama
una función y se destruyen cuando la
función regresa.
Mtl. Lourdes Cahuich 24
25. Administración de la Memoria
Dinámica
Las variables globales, que tienen
dimensión estática son creadas y están
disponibles a través del ciclo de vida
completo de un programa.
Una variable con dimensión dinámica
tiene su ciclo de vida especificado
explícitamente por el programador.
Mtl. Lourdes Cahuich 25
26. Asignación de Memoria
El proceso de obtener memoria del
almacén libre es llamado asignación de
memoria.
El operador new es usado en C++ para
asignar memoria dinámicamente.
// Allocate a single integer
int* ptr = new int;
Mtl. Lourdes Cahuich 26
27. Asignación de Memoria
El operador new siempre regresa una
dirección de memoria.
Recuerda, los apuntadores almacenan
direcciones de memoria, por lo que
nosotros debemos guardar el valor de
regreso del operador new en el apuntador
El operador new sirve para todos los tipos
de datos
Mtl. Lourdes Cahuich 27
28. Asignación de Memoria
Usando un apuntador, podemos tener
acceso indirectamente y modificar la
variable que acabamos de crear.
// Allocate a single integer
int* ptr = new int;
*ptr = 10;cout << quot;Address: quot; << ptr << endl;
cout << quot;Value: quot; << *ptr << endl;
Mtl. Lourdes Cahuich 28
29. Asignación de Memoria
Los objetos también pueden ser
asignados dinámicamente.
El operador new, además de asignar la
memoria para un objeto, llamará un
constructor para el objeto
Mtl. Lourdes Cahuich 29
30. #include <iostream>
#include <cstdlib>
using namespace std;
class my_class {
private:
int x;
public:
my_class() : x(0) {}
my_class(int p) : x(p) {}
int value() { return x;}
};
int main(int argc, char* argv[]) {
// Allocate a single object
my_class* ptr1 = new my_class(4);
// Allocate an array of objects
my_class* ptr2 = new my_class[10];
cout << ptr1->value() << endl;
cout << ptr2->value() << endl;
return EXIT_SUCCESS;
} Mtl. Lourdes Cahuich 30
31. Liberación de Memoria
El operador delete libera la memoria
asignada usando el operador new.
Para liberar, o desasignar memoria, el
operador delete necesita conocer qué
posición en la memoria deseamos liberar.
Para este fin, le proporcionamos un
apuntador, que en realidad es sólo la
posición que obtuvimos del operador new
Mtl. Lourdes Cahuich 31
32. // Dynamically allocate a variable.
double* ptr1 = new double;
// ... use the variable ...
// The variable is no longer needed,
// so we return its memory to the Free Store.
delete ptr1;
Mtl. Lourdes Cahuich 32
33. Liberación de Memoria
Existe una sintaxis especial para ser
usada por el operador delete al liberar
arreglos.
La palabra reservada delete es seguida
por el operador ([]).
Esto señala al ambiente de ejecución que
lo que el operador delete debe liberar es
en realidad un arreglo, no solo una
variable.
Mtl. Lourdes Cahuich 33
34. Constructores Copy
Un constructor copy define las acciones
que necesitan tomarse para crear una
copia de un objeto.
A diferencia de los constructores
regulares, una clase puede contener solo
un constructor copy.
Mtl. Lourdes Cahuich 34
35. Constructores Copy
Si una clase de C++ no define un
constructor copy, el lenguaje provee a la
clase un constructor copy por default.
Este constructor copy por default hace
una copia byte por byte del objeto
Mtl. Lourdes Cahuich 35
36. Constructores Copy
Los constructores copy son invocados cuando
se requiere hacer una copia de un objeto.
Existen tres situaciones cuando se hacen
copias de objetos.
1. Durante una declaración que involucre inicialización
2. Cuando los objetos son transmitidos por valor
3. Cuando los objetos son regresados por valor
Mtl. Lourdes Cahuich 36
37. Constructores Copy
Usar el constructor copy default provisto
por el sistema puede ser peligroso en un
programa que usa le memoria dinámica.
Pueden surgir situaciones donde dos o
más objetos mantienen incorrectamente
apuntadores a los mismos datos.
Mtl. Lourdes Cahuich 37
38. Constructores Copy
El constructor por default, en el caso de
tener apuntadores, copiará los punteros,
no a lo que apuntan “copia superficial”
El opuesto de una copia superficial es una
copia profunda, o quot;deep copyquot;.
Mtl. Lourdes Cahuich 38
39. class Array {
private:
int *ptr;
public:
Array(void) : ptr(new int[10]) {}
Array(const Array& src) {
cout << quot;Copy Constructor Invoked!nquot;;
ptr = new int[10];
for (int i = 0; i < 10; i++) {
ptr[i] = src.ptr[i];
}
}
~Array(void) {delete ptr;}
Mtl. Lourdes Cahuich 39
40. Constructores Copy
Un constructor copy puede ser usado para
asegurar la creación de una copia
profunda en un objeto.
El listado anterior contiene un constructor
copy para la clase Array.
Este constructor copy asigna un nuevo
arreglo en la memoria y copia en ella los
valores del arreglo fuente.
Mtl. Lourdes Cahuich 40
41. Errores Comunes
La regla básica para tratar con la
asignación dinámica y la desasignación o
liberación del espacio es directa: para
cada llamada a new (que consume
memoria), debe haber una llamada
correspondiente a delete (que libera
memoria).
Mtl. Lourdes Cahuich 41
42. Errores Comunes
Es importante pensar cuidadosamente y
con anticipación donde y cuando la
memoria debe ser asignada y liberada.
No asignes memoria al azar a través del
código o dentro de funciones anidadas,
porque esto hace difícil mantener un
registro de esas asignaciones.
Mtl. Lourdes Cahuich 42
43. Errores Comunes
En general, los errores referentes a la
asignación de memoria dinámica son
difíciles de tratar, y es mejor evitarlos
desde el principio
Mtl. Lourdes Cahuich 43
44. Agotamiento de la Memoria
Si nunca se utiliza delete, y se continúa
haciendo llamadas a new, el sistema en
última instancia fallará cuando se quede
sin memoria en el almacén libre.
Los bugs o fallas de funcionamiento son
llamadas quot;memory leaksquot; o agotamiento
de la memoria y pueden ser muy difíciles
de descubrir.
Mtl. Lourdes Cahuich 44
45. Agotamiento de la Memoria
Una característica insidiosa de los
agotamientos de la memoria es que
conducen a caídas aparentemente al azar
y fallas irreproducibles.
Mtl. Lourdes Cahuich 45
46. Sobreescritura
Al tratar con los apuntadores en C++, es
fácil a menudo sobrescribir la memoria.
Las sobre escrituras son especialmente
molestas al usar memoria dinámica.
Esto es porque una sobre escritura en el
heap daña la información usada para
administrar la memoria dinámica.
Mtl. Lourdes Cahuich 46
47. Sobreescritura
Las sobreescritura también son difíciles de
detectar debido a que por lo general no
causan un error inmediatamente.
Una sobreescritura puede hacer que el
programa falle después, en alguna otra
sección del código
Mtl. Lourdes Cahuich 47
48. Sobreescritura
Peor aún, el programa puede no fallar del
todo, y continuar ejecutándose con ciertos
datos corruptos
Mtl. Lourdes Cahuich 48
49. Uso de la Memoria Liberada
Otro error común con respecto a la
memoria dinámica es referenciar la
memoria que ya ha sido liberada.
Nunca es seguro utilizar un apuntador
cuya posición de memoria almacenada se
ha liberado usando delete
Mtl. Lourdes Cahuich 49
50. Uso de la Memoria Liberada
El operador delete, por razones de
eficiencia, no limpia el contenido de la
memoria que libera.
Esto significa que existe la posibilidad de
que la memoria todavía pueda contener
datos válidos, tiempo después de que se
haya liberado espacio.
Mtl. Lourdes Cahuich 50
51. Uso de la Memoria Liberada
No hay garantía de esto, sin embargo, la
memoria en cuestión podría ser
reasignada por el sistema.
Una buena manera de evitar usar
accidentalmente la memoria liberada es
fijar el apuntador igual al apuntador nulo
después de solicitar delete
Mtl. Lourdes Cahuich 51
52. Liberación Duplicada de la
Memoria
Solicitar delete para una posición de
memoria que ya ha sido liberada es otro
error común con respecto a la
administración dinámica de la memoria en
C++.
Mtl. Lourdes Cahuich 52
53. Liberación Duplicada de la
Memoria
Esto puede parecer un error muy obvio
que es fácil de evitar, pero al tener un
programa que tenga asignaciones y
liberaciones de espacio repartidas a
través de una base grande de código, este
error es muy común.
Mtl. Lourdes Cahuich 53