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]


Sobrecarga de operadores unarios: ejemplo

§1  Sinopsis

El ejemplo muestra la utilización de la indirección ( 4.9.16) y la sobrecarga de operadores sobre los miembros de una clase, para simular la sobrecarga de los operadores ++ y -- sobre punteros; algo que no es posible en el lenguaje.

§2  Presentación del problema:

El acceso a elementos de matrices mediante punteros (o subíndices) presenta el riesgo de que intenten acceder áreas de memoria equivocadas, ya que el lenguaje no incluye ningún mecanismo estándar que garantice que los punteros (o índices) se mantengan dentro del ámbito adecuado. La situación puede ser esquematizada así:

char matriz[dimension];
char* puntero = &matriz[0];
...
--puntero;
*puntero = 'x';                  // L.1: Error!! puntero fuera de rango
cout << matriz[dimension + 10];  // L.2: Error!! índice fuera de rango

En estas circunstancias el acceso, y lo que es peor, la escritura de zonas erróneas, puede resultar en errores catastróficos y a menudo irrecuperables.

El problema puede evidenciarse mediante un sencillo programa (que posteriormente modificaremos) en el que se muestra una matriz alfanumérica m a partir de la posición señalada por un puntero. La posición del puntero (y por tanto el origen del texto que se muestra) puede ser desplazado a voluntad mediante las teclas de movimiento de cursor izquierda y derecha. En la primera versión que presentamos, la pulsación repetida de estas teclas puede provocar que el puntero se salga del ámbito adecuado, señalando posiciones de memoria anteriores a m[0] o posteriores al último elemento m[59].

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
using namespace std;

void showm (char* ptr) { printf("%s\n", ptr); }  // funcion auxiliar
char m[60] = "En un lugar de la mancha de cuyo nombre no quiero acordarme\0";
char* cpt = &m[30];

void main() {    // ====================
  cout << "Pulse una tecla: [<-] [->] ([Esc] salir) " << endl;
  int key, ext;
  while ( true ) {
    key = getch();
    if (key == '\x1B') break;   // ESC pulsada: Salida!!
    if (key == 0) {
      ext = getch();
      if (ext == 77)  showm(++cpt);      // -> pulsada
      else if (ext == 75) showm(--cpt);  // <- pulsada
      else cout << "\nSolo teclas [<-] [->] [Esc] !!";
    }
    else cout << "\nSolo teclas [<-] [->] [Esc] !!";
  }
  cout << "\nMensaje completo: " << endl;
  printf("%s\n", m);
}

Salida:

Sugerimos compilarlo y jugar con él.

Comentario

El programa no requiere apenas comentario.  Se ha utilizado la modificación de un modelo contenido en la librería de ejemplos ( 9.2). Aparte de main, se utiliza solo la función showm, que muestra la matriz a partir de la posición indicada por el puntero. Para esto se utiliza la función de Librería Estándar printf  ( <stdio.h> ) que aunque no es muy "C++" viene que ni pintada para el caso, pues muestra el contenido de la matriz señalada desde la posición indicada por el puntero hasta el primer carácter nulo (que se considera fin de cadena).

§3  La solución

Una solución del problema presentado en L.1 (salirse de los límites de la matriz) consistiría en sobrecargar los preoperadores incremento ++ y decremento -- para los punteros cpt, de forma que incluyeran un control de "fuera de ámbito", pero sabemos ( 4.9.18), que esto no es posible. Para simular la sobrecarga utilizamos un objeto auxiliar p que se comporta "como un puntero" a la matriz m. Este objeto no es desde luego un puntero; en realidad pertenece a una clase Pam (puntero-a-matriz) para la que sí podemos redefinir el comportamiento de ambos operadores. El esquema utilizado es el siguiente:

class Pam { /* incluye mecanismo de control */ };
char matriz[dimension];
Pam p = Pam(&matriz, dimension);
...
--p;
*p = 'x';      // Aviso !! puntero fuera de rango

Nota:  en el epígrafe dedicado a la sobrecarga del operador subíndice [ ] se incluye una versión prácticamente idéntica del caso que presentamos aquí ( 4.9.18dw1). Con la diferencia de que la solución allí presentada, utilizada la sobrecarga del operador subíndice de matriz.

§3.1  Versión ejecutable

#include <iostream.h>
#include <stdio.h>
#include <conio.h>
using namespace std;

char m[60] = "En un lugar de la mancha de cuyo nombre no quiero acordarme\0";
class Pam {      // definicion de clase auxiliar
  const int size;
  public:
  char* pti; char* ptr;
  Pam& operator++ () {            // L.11 preincremento
    if (ptr == pti+size-1 ) cout << "\a";
    else ++ptr;                   // L.13
    return *this;
  }
  Pam& operator-- () {            // L.16 predecremento
    if (ptr == pti) cout << "\a";
    else --ptr;                   // L.18
    return *this;
  }
  Pam (char* punt = NULL, int z = 0) : size(z) { // L.21 constructor
    ptr = pti = punt;
  }
};
void showm (Pam& p) { printf("%s\n", p.ptr); }   // funcion auxiliar

void main() {     // ==============
  Pam p = Pam(m, 60);             // M.1 invocación al constructor
  cout << "Pulse una tecla [<-] [->] ([Esc] salir): " << endl;
  int key, ext;
  while ( true ) {
    key = getch();
    if (key == '\x1B') break;     // ESC pulsada: Salida!!
    if (key == 0) {
      ext = getch();
      if (ext == 77) showm(++p);        // M.9  -> pulsada
      else if (ext == 75) showm(--p);   // M.10 <- pulsada
      else cout << "\nSolo teclas [<-] [->] [Esc] !!";
    }
    else  cout << "\nSolo teclas [<-] [->] [Esc] !!";
  }
  cout << "\nMensaje completo: " << endl;
  printf("%s\n", m);
}

Salida:

Aconsejamos compilar y ejecutar probando el comportamiento en los casos extremos.

Comentario

Como puede verse, los cambios efectuados en el cuerpo de la función main son insignificantes; se limitan a la creación e inicialización del objeto p en M.1. Este objeto ejerce la misma función que el puntero  cpt de la primera versión del programa. Lo ha sustituido en las sentencias M.9/10 como argumento de la función showm que ha sido convenientemente modificada para reflejar el cambio (L.25/26).

Los cambios más importantes se refieren al espacio global, donde se ha introducido la clase Pam (puntero-a-matriz); los objetos de esta clase pueden ser utilizados como punteros a matrices de caracteres.

La clase incluye solo tres propiedades:  un entero size, que almacena el tamaño de la matriz referenciada, y dos punteros a carácter (pti y ptr). El primero almacena la dirección del primer elemento de la matriz referenciada, el segundo la dirección del elemento actual (este último es el que ejerce en realidad la función del puntero cpt de la versión primitiva).

La clase incluye un constructor por defecto que se encarga de inicializar el objeto, asociándolo con la matriz correspondiente (se ha utilizado en M.1), y sendas funciones-operador que sobrecargan los operadores preincremento (L.11) y predecremento (L.16) para los objetos de la clase. El comportamiento de ambas resulta evidente. Si el puntero actual ptr, intenta pasar de los límites se produce una señal acústica ('\a' 3.2.3e) y se devuelve su valor actual sin modificación (en este momento señala al primero o al último elemento de la matriz asociada). En caso contrario, el puntero es incrementado o decrementado utilizando las versiones globales de los operadores correspondientes (L.13 y L.18).

Observe que si fuese preciso utilizar los operadores suma + o resta - binaria con los objetos Pam, por ejemplo, expresiones del tipo p+10 o p-5, habría que definir también las correspondientes funciones-operador ( 4.9.18b).