2009-06-17 21 views
7
class object 
{ 
    public: 
    void check() 
    { 
     std::cout<<"I am doing ok..."<<std::endl; 
    } 
}; 

int main() 
{ 
    object *p = new object; 
    p->check(); 
    delete p; 
    p->check(); 
    delete p; 
    p->check(); 
} 

EDITAR: gurúes, Estoy confundido por muchas de las declaraciones "que puede bloquearse o no" .. ¿por qué tampoco existe una norma que dicen, así como nos ocupamos de un bloque de memoria que se elimina usando 'eliminar operador' ...? ¿Alguna entrada?¿Por qué la pieza de código siguiente no se bloquea, aunque he eliminado el objeto?

+0

Gurus, estoy confundido por muchas de las declaraciones "puede bloquearse o no" ... ¿por qué no hay un estándar para decir, esto es como lidiar con un bloque de memoria que se borra usando 'eliminar operador' ... ? ¿Alguna entrada? – Warrior

+2

"Puede o no" bloquearse porque el comportamiento no está definido y depende del compilador, del sistema operativo y de lo que está sucediendo. Básicamente, la eliminación simplemente marca el puntero como disponible nuevamente. No se hace nada explícito en la memoria que se apunta, por lo que puede salirse con la suya si nada más ha cambiado. – ChrisF

Respuesta

10

Porque lo que en realidad parece después de que el compilador ha salido con la suya, es algo como esto:

object::check(object* this) 
{ 
    // do stuff without using this 
} 

int main() 
{   
    object *p = new object; 
    object::check(p); 
    delete p; 
    object::check(p); 
    delete p; 
    object::check(p); 
} 

Puesto que usted "No estás tocando" esto ", no tienes acceso a ningún mal recuerdo.

Aunque, eliminando p dos veces debería ser capaz de causar un accidente:

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.2

+1

En cuanto a la eliminación de 2x posiblemente causando un bloqueo ... sí, por eso normalmente configuro "p" a 0 después de eliminar ... a) para que sepa que ya no señala un objeto yb) porque es seguro llamada eliminar en un puntero nulo. – Dan

+0

Muchas bases de código que he visto han tenido algo así como una macro "#define SAFE_DELETE (x) delete x, x = NULL", o mejor aún una plantilla, por este motivo. Este es uno de los pocos lugares en los que me gusta el operador de coma: la asignación de eliminación y NULO debe "pertenecer" a la misma declaración en la mayoría de los casos, por lo que a mí respecta. – leander

7

Porque la función no está haciendo nada con los datos del miembro del objeto o el puntero this.

Es como llamar a una función

void check(object *self) 
{ 
    std::cout<<"I am doing ok..."<<std::endl; 
} 

con un puntero no válido como argumento self.

Sin embargo, hay una eliminación doble, que puede fallar en algunos entornos.

+1

Es incluso más divertido que eso. Esto compila y funciona muy bien: #include usando namespace std; objeto de clase { public: void check() { cout << "Estoy haciendo bien ..." << endl; } }; int main() { objeto * p = (objeto *) 0; p-> check(); return 0; } $ g ++ -o t tcc $ $ ./t Estoy bien ... $ :) ¡No necesita tener un objeto para llamar a este método! vítores, h. – haavee

+0

Ow querido, lay está en mal estado. Volveremos a publicar como respuesta independiente ... – haavee

+2

Sí porque los métodos no son parte del objeto. Los métodos son parte de la clase. Intente recordar los principios OO. Atributos y Comportamiento. Los atributos están asociados con los objetos y el comportamiento es de nivel de clase. Todos los objetos se comportan de la misma manera. Detrás de escena, todos los objetos se almacenan en una especie de tabla de funciones y una llamada a una función es solo un salto del puntero de instrucción a la dirección de inicio de la función. Para más detalles, ver mi respuesta a esta pregunta. – Aamir

0

Creo que depende del entorno (plataforma/compilador) ...
Esto al menos proporciona un comportamiento inesperado. I piensa que la suerte de que no se cuelga en su caso ;-)

7

eliminar sólo desasigna la memoria y hace que esté disponible de nuevo al montón.

El valor del puntero no está definido después de haber llamado a delete, por lo que puede bloquearse.

Una sugerencia de programación que utilizo para reducir errores de programación es después de una eliminación, establezca el puntero a NULL. De esta forma, sabrá que no está utilizando accidentalmente un puntero después de haber sido eliminado.

+0

Aún puede usar el puntero. Se debe hacer un control para NULL, pero supongo que eso es lo que querías decir. – ralphtheninja

+2

Una verdadera declaración, pero completamente inapelable para este "problema". – haavee

+0

+1 por mencionar el puntero de configuración a NULL. Excelente para la depuración porque los punteros borrados no parecen ser válidos. – markh44

2

Aunque la llamada al método utilizara este puntero, no se garantizaría su bloqueo. El puntero p cuando se borra no se está poniendo a cero, por lo que sigue apuntando a la misma dirección en la memoria. Dependiendo de la implementación de new/delete y del heap manager, es posible que esta memoria no se reutilice, por lo que el uso de p puede seguir funcionando aunque la memoria se haya liberado.

0

Para agregar un poco a lo que otros han dicho, intente usar una variable miembro de su clase dentro del método check() y luego vea qué sucede.

La función de llamada en el caso de las clases es similar a la función normal llamando a excepción de una pequeña diferencia. Los argumentos se insertan en la pila en ambos casos, pero en el caso de una llamada a función de un objeto, 'this' se coloca en la pila como primer argumento. Este 'esto' se usa dentro de la función para acceder a las variables miembro. Como no está accediendo a ninguna variable miembro, está funcionando. Y sí, puede funcionar incluso si accede a las variables miembro pero obtendrá un comportamiento inesperado porque el compilador puede usar libremente la memoria asignada a 'p'. Tan pronto como el compilador utilizará esa memoria, comenzará a tener bloqueos. Por ejemplo, si declara más variables después de eliminar antes de reutilizar 'p', podría colapsar.

Editar: Además, si su método no está accediendo a ninguna variable miembro, entonces es un candidato definitivo para ser un método estático.

+0

Bro he cambiado el código de esta manera, todavía no se cuelga: clase objeto { int p; público: objeto() { p = 100; } void check() { std :: cout << "p =" << p << std :: endl; std :: cout << "Lo estoy haciendo bien ..." << std :: endl; } }; >> ./test p = 100 que estoy haciendo bien ... p = 100 que estoy haciendo bien ... p = 100 que estoy haciendo bien ... – Warrior

+0

Como he explicado anteriormente, la eliminación 'p' simplemente significa que la memoria es libre de ser utilizada para el compilador. Internamente, el lugar al que apunta el puntero 'p' todavía tiene una dirección válida. Intenta declarar algunas variables de puntero antes de reutilizar 'p' y podría colapsar. Esto te dará un comportamiento inesperado. Nadie puede decirte eso cuando exactamente se bloqueará. – Aamir

5

Usted puede hacer lo mismo con los punteros nulos, con tal de que nunca se accede al estado de la instancia de clase:

class object 
{ 
    public: 
    void check() 
    { 
     std::cout<<"I am doing ok..."<<std::endl; 
    } 
}; 

int main() 
{ 
    object *p = 0; 
    p->check(); 
} 
5

Es aún más divertido que eso. Este compila & corre muy bien:

#include <iostream> 
using namespace std; 
class object { 
    public: 
     void check() { 
      cout << "I am doing ok..." << endl; 
     } 
}; 

int main() { 
    object *p = (object*)0; 
    p->check(); 
    return 0; 
} 

En la shell:

 
$ g++ -o t t.cc 
$ ./t 
I am doing ok... 
$ 

:) En realidad no tiene que tener un objeto para llamar a este método! aplausos, h

1

1) depende del compilador, en Mac OS con gcc 4.0.1:

g++ -Wall -g -c main.cpp -o main.o 
g++ -o x main.o 
./x 
I am doing ok ... 
I am doing ok ... 
x(5857) malloc: *** error for object 0x100150: double free 
*** set a breakpoint in malloc_error_break to debug 
I am doing ok ... 

problemas gratuitas está causando dobles.

2) Generalmente no se define borrar el puntero que ya se ha eliminado, siempre se debe establecer el puntero en 0 después de eliminar, se permite llamar a eliminar en el puntero con el valor 0.

3) La razón por la que todavía se puede imprimir la cadena es que el puntero aún apunta a la memoria que se liberó, pero supongo que solo se recupera el puntero, p. devuelto al pool libre para su reutilización, la memoria no se sobrescribe o anula, por lo que si desreferencia el puntero, aún obtendrá los valores originales, a menos que la memoria se reutilice mientras tanto.

1

borrar no establece p a nulo por lo que aún apunta a la ubicación de la memoria. Depende de la implementación de new/delete, pero generalmente elimina solo las marcas de esa parte de la memoria disponible para cualquier asignación nueva.

Es como cuando elimina un archivo de su disco duro. El sistema de archivos simplemente marca esa área como disponible. Los datos del archivo existen y aún se pueden recuperar siempre que no realice nuevas escrituras.

Cuestiones relacionadas