punteros como parametros de funciones que no retornan nada... buena idea?

Iniciado por digimikeh, 23 Febrero 2019, 16:08 PM

0 Miembros y 1 Visitante están viendo este tema.

digimikeh

Según he venido entendiendo hasta ahora, si yo creo un puntero, éste intenta localizar memoria en el espacio dinamico.... estos espacios de memoria deben ser liberados, pero que pasa en el siguiente caso?

Código (cpp) [Seleccionar]

void Persona::IngresarNombre(const char * nombre){
    strcpy(this->nombre, nombre);
   
    delete nombre;   //Es esto necesario?
}



Que sucede con ese puntero a char que está como parámetro?, se elimina solo o yo debo eliminarlo también?

o es mejor idea esto?:

Código (cpp) [Seleccionar]

void Persona::IngresarNombre(const char nombre[16]){
    strcpy(this->nombre, nombre);
}

Gracias.
Dungeons & dragons;
dragons.Attack();

K-YreX

Las dos opciones que tienes son:
Código (cpp) [Seleccionar]

void Persona::IngresarNombre(const char *nombre);

void Persona::IngresarNombre(const char nombre[]);


Y en ambos casos estás haciendo lo mismo. Estás pasando como parámetro la dirección del elemento 0, es decir, el inicio del array en este caso (ya que doy por hecho que ese puntero contiene una cadena/array de caracteres).

No tienes que liberarlo; y te explico. Un puntero se libera con <delete> si se ha usado antes <new> sobre él. Es decir, sólo si estás usando memoria dinámica. En otro caso el puntero se libera solo cuando finaliza la ejecución del programa como cualquier otra variable.
En el caso de que SÍ estés usando memoria dinámica, debes liberar el puntero cuando ya no vayas a usarlo más. Y si has asignado el valor de ese puntero a otro puntero y borras el primero, también estarás borrando el segundo. Ya que lo que haces en realidad es liberar la memoria a la que está apuntando.

Mi recomendación es que no borres el puntero dentro de la función y así te servirá para pasar punteros dinámicos como no dinámicos. Si dentro de la función borras el puntero y en un caso determinado le pasas un puntero no dinámico (osea el comienzo de un array creado sin <new>) tendrás problemas.

Te dejo aquí un par de ejemplos simples para que veas las diferencias :-X:
Código (cpp) [Seleccionar]

int main(){
   char *p_dinamico;
   p_dinamico = new char [20]; // tendras que liberarlo antes de acabar la ejecucion del programa
   char p_estatico[20]; // se liberara solo al acabar

   Persona my_person;
   my_person.InsertarNombre(p_dinamico); // p_dinamico lo puedes liberar en el main mas adelante
   my_person.InsertarNombre(p_estatico); // p_estatico se liberara solo

    delete [] p_dinamico; // como hemos dicho antes
}
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

avesudra

[EDITO] : No había visto el post de YreX-DwX  :-(

Hola digimikeh,

Todo depende de cómo haya sido reservada la memoria a la que apunta el puntero "nombre". Tienes dos casos:


  • Que fuera reservada dinámicamente con new, en tal caso, debería liberarse en algún sitio, pero no necesariamente ahí.
  • Que fuera reservada estáticamente, en tal caso no es necesario que se libere. Un ejemplo podría ser un literal como:
Código (cpp) [Seleccionar]
const char* cadena = "hola"


En éste último caso la duración de esa memoria será hasta el fin de la ejecución de tu programa y no es necesario que la liberes.

Un ejemplo de ambos casos sería(fíjate que cadenaB se libera porque se reservó con new):
Código (cpp) [Seleccionar]

const char* cadenaA = "HOLA";
char* cadenaB = new char[5];
strncpy(cadenaB, cadenaA, 5);

Persona personaA = new Persona();
Persona personaB = new Persona();

personaA.IngresarNombre(cadenaA);

personaB.IngresarNombre(cadenaB);

delete[] cadenaB;


Saludos.
Regístrate en

K-YreX

Cita de: avesudra en 23 Febrero 2019, 16:34 PM
Código (cpp) [Seleccionar]

const char* cadenaA = "HOLA";
char* cadenaB = new char[5];
strncpy(cadenaB, cadenaA, 5);

Persona personaA = new Persona();
Persona personaB = new Persona();

personaA.IngresarNombre(cadenaA);

personaB.IngresarNombre(cadenaB);

delete[] cadenaB;


Un pequeño apunte. Los objetos de tipo persona los estás creando dinámicamente:
Código (cpp) [Seleccionar]

Persona *personaA = new Persona();
Persona *personaB = new Persona();
// ...
delete personaA;
delete personaB;


Si los creas estáticamente no necesitas usar <new>:
Código (cpp) [Seleccionar]

Persona personaA; // ya llama al constructor sin parametros sin especificarlo nosotros
// ...
// no es necesario liberar memoria ya que no se ha reservado memoria dinamicamente
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

avesudra

Cita de: YreX-DwX en 23 Febrero 2019, 16:58 PM
Un pequeño apunte. Los objetos de tipo persona los estás creando dinámicamente:
Código (cpp) [Seleccionar]

Persona *personaA = new Persona();
Persona *personaB = new Persona();
// ...
delete personaA;
delete personaB;


Si los creas estáticamente no necesitas usar <new>:
Código (cpp) [Seleccionar]

Persona personaA; // ya llama al constructor sin parametros sin especificarlo nosotros
// ...
// no es necesario liberar memoria ya que no se ha reservado memoria dinamicamente


Cierto, se me ha pasado, el copy-paste....

Muchas gracias y saludos.
Regístrate en

digimikeh

Dungeons & dragons;
dragons.Attack();

Loretz

Pregunta:
Es esto necesario?

Citarvoid Persona::IngresarNombre(const char * nombre){
     strcpy(this->nombre, nombre);

     delete nombre;   //Es esto necesario?
}

Respuesta:
¿Cómo saberlo?

Se podría intentar responder primero a algunas cuestiones preliminares, por ejemplo:

¿Está claro en la documentación de cada función que invoca a IngresarNombre() que este parámetro va a ser destruido (delete) (y que cualquiera que vaya a usarla jure primero no volver a usar ese puntero después del punto de llamada).

Este puntero "nombre" ¿fue creado con new? ¿Seguro? (¿no habrá sido con malloc?)

¿No convendría llamar a esta función de alguna manera que deje más claro su comportamiento, por ejemplo,  IngresarNombreYCuidadoQueAcaMuereElPunteroNombre() ?

Cuando una función recibe un puntero "final", se dice que esa función recibe el "ownership", la responsabilidad del "delete", y se la suele llamar "sink function".

Ejemplo:

void sink_f(const char* p) // ¡¡ATENCIÓN!! ESTA FUNCIÓN DESTRUIRÁ A p ¡¡CUIDADO!! ¡¡ALERTA!!
{
    // hacer algo con p, y después
    delete[] p;
}


Y una forma de llamarla sería:

int main()
{
    char* str = new char[12];
    strcpy(str, "hola mundo!");
    sink_f(str);
    // delete[] str; // QUÉ SUERTE QUE LEÍ EL COMENTARIO DE sink_f(), uffff!!

}



Francamente... ¿qué opinas?

Por suerte ya nadie hace cosas como esa, espero.