[C con Clase] Dudas sobre clases

Programante programante en gmail.com
Jue Dic 13 14:34:35 CET 2007


Patricio González Sevilla escribió:
> En el Curso de C++.
>
> Capítulo 29 - CONSTRUCTORES tengo algunas dudas:
>
> - ¿Qué ventajas posee ubicar la definición de las funciones de las 
> clases fuera de la declaración de la clase? ¿Es lo habitual?
Separar los prototipos de la implementación. Así es mucho más fácil por 
ejemplo ver qué funciones hay sin necesidad de bucear por la función 
entre todo el código.
Pero lo que lo hace realmente útil es que esta separación también afecta 
al compilador. Si tienes por un lado los prototipos en el .h y por otro 
las implementación en el .cpp para usar dicha clase en 100 archivos es 
suficiente con incluir en todos ellos la cabecera. Todos ellos usarán el 
mismo código. De lo contrario tendría que compilar el código en línea en 
cada uno de los archivos (y aunque en unos pocos casos puede ser 
deseable, por lo general no), con la consiguiente redundancia y pérdida 
de tiempo.

> Capítulo 30 - DESTRUCTORES tengo algunas dudas:
>
> - Si cad está declarada como miembro privado, ¿Es correcto acceder a 
> ella de esta forma al crear el constructor copia?:
> cadena::cadena(const cadena &Cad) {
>    // Reservamos memoria para la nueva y la almacenamos
>    cad = new char[strlen(Cad.cad)+1];
>    // Reserva memoria para cadena
>    strcpy(cad, Cad.cad);             // Almacena la cadena
> 	// ¿No debería accederse a Cad.cad con el método [char *cadena::Leer(char *c)]?
> } 
Es correcto. Ten en cuenta que acceder al miembro privado puede que sea 
la única forma de acceder a cad. Hay quien prefiere usar siempre que sea 
posible las funciones que exporta la clase y quien no. En tu ejemplo 
también podríamos hacer strcpy(Leer(), Cad.Leer()); pero se pierde la 
relación entre Leer y cad. Tratándose de otro objeto puede ser buena 
idea usarlo con los métodos que provee.
PD: Seguramente Leer debería devolver un const char *

> - Otra duda en este apartado es el siguiente. ¿Este método:
>
> char *cadena::Leer(char *c) {
>    strcpy(c, cad);
>    return c;
> }
> no podría sustituirse por:
>
> char *cadena::Leer() {
>    char *c;
>    strcpy(c, cad);
>    return c;
> }
> ?.
No. En el primer caso el usuario tiene que proporcionar la memoria (por 
lo que sería mala idea usarlo en el destructor). En el segundo estás 
copiando la cadena al lugar adonde apunta el putnero. Y como apunta a 
cualquier sitio... lo más probable es que te produzca un fallo de 
segmentación.

En el primer caso:
char Buffer[50];
Cadena1.Leer(Buffer);

char *cadena::Leer(char *c) { //c apunta a Buffer
   strcpy(c, cad); //Copiamos la cadena a Buffer
   return c;//Le devolvemos la dirección de Buffer
}

Funciona sin problemas... Siempre y cuando Cadena1 no contenga más de 49 
caracteres ya que sino se producirá un desbordamiento de Buffer. Habría 
que pasarle a Leer la longitude del buffer para que compruebe si es 
seguro hacer la copia.


En el segundo caso
Cadena1.Leer();

char *cadena::Leer() {
   char *c; //c apunta a cualquier dirección de memoria (donde no podremos escribir)
   strcpy(c, cad); //Intentamos escribir en ella. El sistema operativo nos cierra el programa
   return c;
}


Una tercera opción sería devolver la memoria que usa la clase 
internamente, pero el puntero puede dejar de ser válido cuando añadamos 
texto a la cadena:

const char *cadena::Leer() {
   return cad;
}



Otra opción es hacer una copia y dejar que el usuario la libere:

char *cadena::Leer() {
   return strdup(cad);
}



char* Texto;
Texto = Cadena1.Leer();
//trabajar con Texto
free(Texto); //Si queremos liberar con delete, reservar con new




Más información sobre la lista de distribución Cconclase