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 del operador subíndice [] para controlar el acceso a miembros dentro de los límites de una matriz [1].

§2  Presentación del problema:

Nota: el ejemplo es casi idéntico al incluido al presentar la sobrecarga de operadores unarios ( 4.9.18c), y trata de la solución del mismo problema, aunque en aquel caso, se utiliza la sobrecarga de los operadores pre y post-incremento en una clase auxiliar, mientras que en el que presentamos aquí, la solución se consigue sobrecargando el operador subíndice de matriz.

El acceso a elementos de matrices mediante subíndices (o punteros) 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 subíndices (o punteros) se mantengan dentro del ámbito adecuado. La situación puede ser esquematizada así:

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

En estas circunstancias el acceso, y lo que es peor, la escritura de zonas erróneas puede originar errores catastróficos.

El problema puede evidenciarse mediante un sencillo programa (que posteriormente modificamos) en el que se muestra una matriz alfanumérica m a partir de la posición señalada por un subíndice.  El subíndice (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 esta primera versión, la pulsación repetida de estas teclas puede provocar que el índice 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;

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

void main() {       // =============
  int pos = 30;     // posición inicial del índice
  cout << "Pulse una tecla: [<-] [->] ([Esc] salir) " << endl;
  int key, ext;
  while ( true ) {
    key = getch();
    if (key == '\x1B') break;                // ESC pulsada. Salir!!
    if (key == 0) {
      ext = getch();
      if (ext == 77)      showm(&m[++pos]);  // M.9  -> pulsada
      else if (ext == 75) showm(&m[--pos]);  // M.10 <- pulsada
      else cout << "\nSolo teclas [<-] [->] [Esc] !!";
    }
    else cout << "\nSolo teclas [<-] [->] [Esc] !!";
  }
  cout << "\nMensaje completo: " << endl;
  showm(m);     // M.16
}

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 un puntero. Para esto se utiliza la función de Librería Estándar printf ( <stdio.h> ) que aunque no es muy "C++" es muy adecuada para el caso, ya que muestra el contenido de la matriz desde la posición indicada por el puntero hasta el primer carácter nulo (que se considera fin de cadena).

Para obtener el argumento que se pasará a esta función en M.9 y M.10 utilizamos la notación de subíndices. El objeto m[pos] es transformado en un puntero aplicándole el operador de referencia & ( 4.9.11). Observe la utilización del identificador m (M.16) como sinónimo de puntero al primer elemento de la matriz ( 4.3.2).

§3  La solución

Una solución del problema esquematizado en §2 sería sobrecargar el operador subíndice [ ] de la matriz m, de forma que incluyan un control de "fuera de ámbito", pero esto no es posible porque este objeto es tipo char[ ] y los operadores no pueden ser sobrecargados para los tipos básicos.

Para simular la sobrecarga utilizamos un objeto auxiliar cm que se comporta como un puntero a la matriz m. Este objeto pertenece a una clase CMat (Matriz-de-caracteres) para la que si podemos redefinir el comportamiento del operador. El esquema utilizado es el siguiente:

class CMat { /* incluye control de límites para índices */ };
char matriz[dimension];
CMat cm = CMat(&matriz, dimension);
...
cout << cm[dimension + 10];    // Aviso: subíndice fuera de rango!!

§4  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 CMat {          // L.7 Clase auxiliar
  const int size;
  int pos;
  public:
  friend void showm (CMat&);  // L.11
  char* m;
  CMat& operator[](int i) {   // L.13
    if (i > size-1 ) { cout << "\a"; pos = size-1; }
    else if (i < 0) { cout << "\a"; pos = 0; }
    else pos = i;
    return *this;
  }
  CMat (char* punt = NULL, int z = 0) : size(z) {      // L.19 constructor
    pos = 0; m = punt;
  }
};
void showm (CMat& p) { printf("%s\n", &p.m[p.pos]); }  // L.23

void main() {         // =========================
  CMat cm = CMat(cptr, 60);   // M.1
  int pos = 30;
  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(cm[++pos]);            // M.10 -> pulsada
      else if (ext == 75) showm(cm[--pos]);            // M.11 <- pulsada
      else cout << "\nSolo teclas [<-] [->] [Esc] !!";
    }
    else  cout << "\nSolo teclas [<-] [->] [Esc] !!";
  }
  cout << "\nMensaje completo: " << endl;
  showm(cm[0]);
}

Salida:

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

Comentario

Los cambios efectuados en el cuerpo de main son mínimos; se limitan a la creación e inicialización del objeto cm en M.1. Este objeto ejerce la misma función que la matriz m. Lo ha sustituido en las sentencias M.10/11 como argumento de la función showm que ha sido también convenientemente modificada para reflejar el cambio (L.23).

Los cambios más importantes se refieren al espacio global, donde se ha introducido la clase CMat (matriz de caracteres); los objetos de esta clase pueden ser utilizados como manejadores ("handlers") de matrices de caracteres.

La clase incluye solo tres propiedades: un entero size, que almacena el tamaño de la matriz referenciada; un entero pos, que sirve de puntero interno, y un punteros-a-carácter m, que almacena la dirección de la matriz referenciada desde la clase.

La clase incluye también un constructor por defecto que se encarga de inicializar el objeto, asociándolo con la matriz correspondiente (se ha utilizado explícitamente en M.1) y una función, operator[ ], que define el comportamiento del operador subíndice para los objetos de la clase (L.13).

Su comportamiento resulta evidente: si el índice intenta pasar de los límites superior o inferior, se produce una señal acústica ('\a' 3.2.3e) y el puntero interno es situado en el valor máximo o mínimo respectivamente. En caso contrario este puntero adopta el valor del índice.

Observe que la función showm se ha declarado friend de la clase ( 4.11.2a1), al objeto de que pueda acceder a sus miembros (L.11), y que si fuese preciso utilizar otras operaciones con los objetos CMat, sería necesario definir las correspondientes funciones-operador.

  Inicio.


[1]  El ejemplo es parecido y complementario al que se presentó al tratar de la sobrecarga de operadores unarios ( 4.9.18c).