Punteros fantasmas... (asi le puse yo)..

Iniciado por digimikeh, 10 Febrero 2019, 22:43 PM

0 Miembros y 2 Visitantes están viendo este tema.

Loretz

Citartengo que imaginar al delete como un interruptor que desactiva el espacio de memoria y al new como que lo activa..
Casí que es así, no vas a encontrar "activar" pero sí "reclamar", e incluso "allocar" (puaj), la palabra apropiada en el lenguaje C++ es el verbo inglés "allocate".

Si te fijas en la documentación (por ejemplo, en un sitio que no es el del comité estándar pero que todo el mundo tiene a mano: https://en.cppreference.com/w/cpp/memory/new/operator_delete
Verás que el operator delete es toda una familia de funciones, dedicadas a liberar (deallocate) la memoria que se haya pedido con su correspondiente new.

Como ves, delete necesita el nombre de una variable para saber qué liberar, si no hay variable no habrá delete. Por ejemplo, una forma de no liberar nunca memoria asignada (capturada, allocated) con new puede ser:
void f() { new int; } Aquí new int; reserva un espacio sizeof(int) en la memoria libre (digamos... 4 bytes), que jamás podrá liberar ningún delete, porque ni nombre tiene el pobre.

Haciendo algo más razonable:
void g() { int* i = new int; }
Ahora, al salir de g(), la variable i va a morir (es una variable local que termina su vida acotada a su ámbito), pero una copia temporal de esa i queda para ser devuelta como un temporal por la llamada a g().

Por eso es que tiene sentido hacer:
int* p = g();
porque ahora p va a tener el valor de ese temporal que devolvió g().

Si se hubiera hecho sólo:
g(); una llamada a g() sin asignar su valor devuelto a ninguna variable de tipo int*, se hubieran perdido esos 4 bytes irremediablemente.

pero después de
int* i = new int;
se podría hacer perfectamente
int* j = i;
int* k = j;

de este modo los tres punteros apuntan al mismo espacio de memoria. E invocando un delete en cualquiera de ellos se liberarán esos 4 bytes tomados por new. Puedes hacer, por ejemplo:
delete k;

Lo que no puedes hacer (bueno, puedes, pero el sistema operativo sabrá tratarte como te mereces) es invocar a delete en más de uno de esos, porque estarías tratando de devolver dos o tres veces la memoria que se reclamó una sola vez. Un new y tres deletes, no te atrevas.

Como este tipo de errores siempre fue frecuente y catastrófico, el C++ actual evita el uso de new / delete, y sanseacabó. unique_ptr y shared_ptr son tus amigos.








digimikeh

Perfecto, y esos dos últimos que mencionaste (unique_ptr y shared_ptr) serian punteros inteligentes no ?..
Dungeons & dragons;
dragons.Attack();

CalgaryCorpus

Hay un error en lo especificado antes:

En este codigo,

Código (cpp) [Seleccionar]


int * sumar(int a, int b){
    int * psuma = new int;
    *psuma = a + b;
    return psuma;

}

int main (){
    int a = 5;
    int b = 10;

    int * punteroSuma = new int;    //modificado el nombre
    punteroSuma = sumar(a, b);
   
    delete punteroSuma;      //aqui se elimina psuma, punteroSuma o ambos?
}


Aqui mi perfil en LinkedIn, invitame un cafe aqui

CalgaryCorpus

Hay un error en lo especificado antes:

En el codigo que preguntas

Código (cpp) [Seleccionar]


int * sumar(int a, int b){
    int * psuma = new int;
    *psuma = a + b;
    return psuma;

}

int main (){
    int a = 5;
    int b = 10;

    int * punteroSuma = new int;    //modificado el nombre
    punteroSuma = sumar(a, b);
   
    delete punteroSuma;      //aqui se elimina psuma, punteroSuma o ambos?
}



al asignar punteroSuma el resultado de sumar(), se pierde el valor que habias asignado antes.
Esa memoria no la estas liberando, ni se liberara en el delete del final.
Aqui mi perfil en LinkedIn, invitame un cafe aqui

digimikeh

#14
No te entendi, cual es el valor que se pierde que había asignado antes?..

EDIT:

Ah!, te refieres a que no debí haber escrito :

Código (cpp) [Seleccionar]
int * punteroSuma = new int;

sino:

Código (cpp) [Seleccionar]
int * punteroSuma = sumar(a, b);

directamente, verdad?...

Es como que cuando solicité memoria al declarar punteroSuma, esa memoria la dejé botada y pasé el puntero al valor de retorno de Suma.. creo que a eso te refieres...

Dungeons & dragons;
dragons.Attack();

K-YreX

Exacto, sobra el reservar memoria en el <main>. Imagina que cuando usas <new> compras un almacén para guardar "datos" y cuando ya no lo necesitas usas <delete> para venderlo. Un puntero guarda la dirección del almacén.

En tu caso la función <sumar()> compra un almacén, guarda la suma en él y te envía la dirección donde tienes tu almacén. Entonces en el <main> lo que haces es guardar la dirección que te envía la función <sumar()> en el puntero nuevo. No necesitas comprar otro almacén. Porque si compras otro almacén en el <main> y donde tienes guardada la dirección del nuevo almacén, guardas la dirección del almacén que compraste en la función <sumar()>, ya no sabes donde está el último almacén que has comprado. Y si no sabes donde lo tienes, no puedes venderlo... :-\

No sé si queda claro con esto pero es el mejor símil que se me ha ocurrido :-X
Código (cpp) [Seleccionar]

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

digimikeh

buen punto... lo tendré en cuenta la siguiente vez..  :)
Dungeons & dragons;
dragons.Attack();