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.11.2d5  Observaciones y errores más frecuentes

§1  Presentación

Muchos de los problemas que se presentan en casos prácticos programados en C++ se derivan del diseño inadecuado de constructores y destructores, en especial algunos de esos errores inexplicables que pueden acabar con los nervios del más templado. A continuación se muestra una selección con algunos de los que considero más probables, o que he tenido que sufrir en mis propias carnes.

§2  Errores en el Constructor
§2.1  Constructor inadecuado

class C {
  ...
  char* string;
  C(char*);        // constructor explícito
};

C::C(char* st) {   // definición
  ...
  strcpy(string, st);    // L1:
}

Se pretende que la clase acepte sentencias en las que el miembro string pueda ser iniciado con una cadena de caracteres constantes estilo C mediante sentencias como:

C c1("Contiene el path-name de un fichero");

El programa compila sin dificultad pero genera un error de runtime; concretamente el consabido: Este programa ha ejecutado una operación no válida y será interrumpido....

El problema aquí es que nos hemos pasado de listos. Hemos pretendido copiar el "contenido" de la cadena en lugar del puntero (nuestro miembro es un puntero).  El diseño correcto en este caso sería:

class C {
  ...
  char* string;
  C(char*);        // constructor explícito
};
C::C(char* st) {   // definición
  ...
  string = st;     // L1:
}

Sin embargo este diseño tiende a presentar problemas, ya que la información (en este caso una cadena alfanumérica) está fuera del cuerpo del objeto y por tanto fuera de su control. Es mejor un diseño alternativo en el que los datos pertenezcan al cuerpo de la clase:

#define LSTRING 250
 
class C {
  ...
  char string[LSTRING];
  C(char*);        // constructor explícito
};
C::C(char* st) {   // definición
  ...
  strcpy(string, st);    // L1:
}

Esta forma tiene el pequeño inconveniente de que debemos prever de antemano el tamaño máximo LSTRING, que debemos reservar para la matriz, y que la cadena de inicio suministrada no desbordará el tamaño de nuestro buffer si pretendemos itroducir una cadena demasiado larga (en principio el usuario de nuestro programa quizás no esté advertido de este límite interno). Esto es fácil insertando una sentencia de control en el constructor:

C::C(char* st) {   // definición
  ...
  if (strlen(st) < LSTRING)
      strcpy(string, st);    // L1:
  else
     // medidas de control
}

Esta última forma tiene la ventaja de que nuestra aplicación no será susceptible de un ataque de desbordamiento de buffer.

§3  Errores en el Constructor-copia
§3.1  Constructor-copia inadecuado

class C {
  ...
  X* xptr;
  C(C&);    // constructor-copia
};
C::C(C& c1) {
  xptr = c1.xptr;    // L1:
}

Cuando la clase contiene miembros que son punteros, hay que meditar muy detenidamente si al copiar un objeto, los punteros del nuevo deben apuntar al mismo objeto que en el original o no. La omisión o la inclusión de una sentencia como L1 supone que los punteros de ambos objetos señalan a la misma entidad, lo que puede derivar en innumerables problemas.

§3.2

class C {
  ...
  char* string;
  C(C&);          // constructor-copia
};
C::C(C& c1) {     // definición
  ...
  strcpy(string, c1.string);    // L1:
}

Es una versión del error cometido antes en el constructor explícito (§2.1 ), pero aplicado al constructor-copia. El origen; el resultado y la solución son los mismos que en aquel caso. La versión correcta seria:

class C {
  ...
  char* string;
  C(C&);          // constructor-copia
};
C::C(C& c1) {     // definición
  ...
  string = c1.string;    // L1:
}

Observe que en este caso podría haberse omitido la sentencia L1, ya que el compilador proporciona por su cuenta una versión por defecto.