Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


4.2.1  Puntero a objeto

Deberías hacer el bien y no el mal.
Deberías encontrar el perdón para ti y perdonar a los demás.
Deberías compartir libremente, no tomando nunca más de lo que das.

D. Richard Hipp    www.sqlite.org en sustitución de la nota copyright de su obra. 

§1  Sinopsis:

Un "puntero-a-tipoX" (puntero a un objeto de tipoX), almacena la dirección de un objeto del tipo señalado; es decir: señala a un objeto tipoX. Puesto que los punteros son objetos, puede haber un puntero apuntando a un puntero (así sucesivamente). Los objetos a los que se apunta suelen ser matrices, estructuras, uniones y clases.

Nota: aquí consideramos que un "objeto" es una región específica de memoria que puede contener un valor (o conjunto de valores) fijo o variable. Esta acepción del vocablo es diferente (más amplia) que cuando designa la instancia de una clase.


Las explicaciones que siguen son válidas para punteros objetos de cualquier tipo (que no sean funciones), pero cuando se trata específicamente de punteros a clases e instancias de clases, existen ciertas particularidades en la notación, así que les hemos dedicado un epígrafe específico con algunos comentarios ( 4.2.1f).

Nota: tradicionalmente se considera que punteros y matrices están estrechamente relacionados [1], hasta el punto de ser estudiados simultáneamente. De hecho, cualquier operación que puede efectuarse entre subíndices de matrices puede efectuarse con punteros, en general de forma más rápida (aunque algo más difícil de entender por el principiante). Además, el operador de elemento de matriz [  ] se define en términos de ser un puntero ( 4.9.16).


Los punteros a objeto tiene su propia aritmética (rudimentaria), de forma que pueden ser incrementados, disminuidos y comparados cuando se exploran matrices u otras estructuras complejas.

§2  Puntero nulo

Un puntero nulo es un puntero que apunta a una dirección que no corresponde a ningún objeto del programa. Asignando la constante entera 0 a un puntero se le asigna el valor nulo. Para mejor ligibilidad puede utilizarse la constante manifiesta NULL (definida en la librería de cabeceras estándar <stdio.h>). Ejemplo:

int* punt1;          // L.1: Declara punt1
punt1 = 0;           // L.2: define punt1 como puntero nulo
punt1 = NULL;        // Equivalente a la anterior


Nota: la cuestión de la inicialización del puntero nulo es uno de los puntos de controversia para los teóricos del lenguaje. En rigor, la sentencia L.2 anterior es errónea, ya que el tipo de punt1 es puntero-a-int, mientras que 0 es una constante numérica (int por defecto); a pesar de lo cual es aceptada sin rechistar por el compilador [2]. Lo sintácticamente correcto sería realizar un modelado adecuado ( 4.9.9):

punt1 = static_cast<int*> (0);


Es interesante observar que los punteros nulos de tipos distintos son distintos entre si. Por ejemplo, un puntero nulo-a-int es de tipo distinto de un puntero nulo-a-char (aunque ambos coincidan en que su Rvalue es 0), por tanto no pueden ser comparados. La comparación de dos punteros nulos del mismo tipo conduce a que son iguales. Ejemplo:

int* pint = NULL;
char* pchar = NULL;
if (pint == pchar) cout << "Iguales";   // Error!!

La última sentencia produce un error de compilación: Error:  Nonportable pointer comparison in...


Es un error deferenciar ( 4.9.11) un puntero nulo, además, el intento de utilizar el valor obtenido puede dar lugar a un error de ejecución. Por ejemplo, refiriéndonos al caso anterior:

int x = *pint      // Error:

Recordar que cuando falla el operador de modelado dynamic_cast ( 4.9.9c) sobre un puntero, el resultado es un puntero nulo. También que algunas funciones de la Librería Estándar devuelven punteros; incluso el operador new devuelve un puntero, y en algunas implementaciones antiguas, el aviso de error en la operación de este operador se realizaba devolviendo un puntero nulo.

§3  Inicializar punteros

En general los punteros pueden ser inicializados como otra variable cualquiera; desde luego solo tiene sentido que el valor sea la dirección de inicio del almacenamiento de un objeto del tipo adecuado. En otras palabras: un valor que sea una dirección de memoria. Como existe un operador que precisamente proporciona la "dirección" de una variable (operador de referencia & 4.9.11), la forma usual de inicializar un puntero suele ser asignándole el valor devuelto por dicho operador. Ejemplo:

int x = 100;
int* ptr;        // declaración de ptr como puntero-a-int
ptr = &x;        // se le asigna la "dirección" de una variable


También es muy frecuente iniciarlos con el valor devuelto por el operador new ( 4.9.20) que precisamente devuelve un puntero a objeto. Ejemplo:

class C { .... };     // definición de una clase C
C* cptr;              // declara cptr como puntero-a-C
cptr  = new C(...)    // new crea una instancia de la clase y devuelve un
                  // puntero al objeto, cuyo valor que es asignado a cptr

Nota: los enteros y los punteros no son intercambiables, con excepción del cero. El cero es un valor que nunca puede adoptar un puntero en condiciones normales, por esta razón se utiliza para significar "no asignado" u otra circunstancia especial. Recordar que la aritmética de punteros permite comparar dos punteros, para igualdad o desigualdad, con NULL.

§4  Punteros como argumentos

En muchas ocasiones los punteros se utilizan como argumentos a funciones. De hecho, la mayoría de las veces en que las matrices o las cadenas de caracteres pasan como argumentos, lo que en realidad pasa es un puntero al primer elemento. Por ejemplo:

char* str = "La Tacita de Plata";
printf("La ciudad es: \"%s\"\n", str);

En este caso, la función printf recibe como segundo argumento str, que es un puntero a la posición de memoria donde comienza el almacenamiento de la cadena "La Tacita de Plata\0".


  En cualquier caso, es necesario declarar esta circunstancia en el prototipo y en la definición de la función para que el compilador conozca que el argumento pasado es una variable tipo puntero. Ejemplo:

# include <iostream.h>
 
void func(int *);     // prototipo: argumento definido como puntero-a-int
 
void main () {        // =============
  int x = 35;
  int * ptr = &x;
  func (ptr);         // se pasa puntero-a-int como argumento
  cout << "El valor es ahora: " << x << endl;
}
 
void func (int * p) { // el argumento se define como puntero-a-int
  *p += 10;           // el argumento es tratado como puntero
}

Salida:

El valor es ahora: 45


  Inicio.


[1]  Algunos autores afirman directamente y sin rodeos, que las matrices son punteros. Deepak P. "Pointers In C - A Boon Or A Bane?" www.devarticles.com

[2]  En realidad no se produce error porque el compilador realiza una conversión automática de tipo ( 2.2.5).