[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