2012-10-05 14 views
6

Tengo un problema con un destructor llamado para una clase al final de una subrutina, aunque debería definirse fuera del alcance de la subrutina.C++: ¿Se ha llamado al destructor antes de que salga del alcance?

Aquí es la más pequeña pieza de código que tengo que muestra mi problema:

#include <iostream> 
using namespace std; 

class Foo { 
private: 

    double *array; 

public: 

Foo(int N) { 
    array = new double[N]; 
    for (int i=0; i<N; i++) { 
     array[i]=0; 
    } 
} 

~Foo() { 
    delete[] array; 
} 
}; 

void subroutine(Foo x) { 
    cout << "Hello!" << endl; 
} 

int main() { 
    Foo bar(10); 
    subroutine(bar); 
    subroutine(bar); 
} 

Ahora el destructor para la barra de objetos que aquí se vuelve a llamar después de la primera subrutina termina pesar de que es alcance debe ser el conjunto de la ¿función principal? Esto significa que cuando llamo a la segunda subrutina se llama al destructor nuevamente y me sale una pérdida de memoria.

He encontrado que puedo solucionar esto llamando por referencia en la subrutina, pero no estoy muy satisfecho con esta corrección ya que no entiendo por qué no funcionó en primer lugar. ¿Alguien puede arrojar algo de luz sobre esto para mí?

Gracias.

+2

Dado su destructor, debe definir o eliminar el constructor de copias y el operador de asignación de copias de 'Foo'. Busque la "regla de tres". –

+5

"se llama al destructor para una clase": verás que, con el tiempo, las cosas son mucho más claras si siempre distingues entre una ** clase ** y un ** objeto **. Los destructores son llamados sobre ** objetos ** que no están en las clases. –

Respuesta

21

Usted está pasando un Foo por valor a la función subroutine. Esto significa que tiene su propia copia, que se destruye al salir de su alcance.

void subroutine(Foo x) { 
    // x is a new Foo here. It will get destroyed on exiting scope, 
    // resulting in a destructor call 
} 

Su principal problema aquí es que no se ha implementado un constructor de copia, por lo que la matriz asignada dinámicamente no se copia (sólo el puntero que apunta a que es). Entonces, cuando copie objetos Foo, tiene cada copia haciendo referencia a la misma matriz. Y cada copia intentará destruirlo.

Debe seguir el rule of three e implementar un operador de asignación y un constructor de copia que hacen una "copia profunda" de la matriz, de modo que cada objeto Foo posea su propia matriz.

+0

¡Ah, gracias por la respuesta rápida! Sí, me di cuenta de que llamar por referencia lo solucionó, pero no sabía por qué, gracias por la explicación. – Plog

+1

@ user1722882: En realidad, sería mejor que utilizaras un 'std :: vector' y eliminaras tu destructor por completo. Reimplementar los contenedores de la Biblioteca estándar puede ser bueno para el deporte, pero cuando no es el objetivo principal del ejercicio/trabajo ... simplemente está matando su productividad. –

1

El problema que tiene es que estás pasando a su objeto por valor:

void subroutine(Foo x) { 

Esto está creando un objeto temporal e invocando el constructor de copia/destructor de su objeto cada vez que la llame.

3

Cuando llama al void subroutine(Foo x) {, se copia su objeto bar (por lo tanto, se llama al destructor después de que termina la función).

Intente usar: void subroutine(Foo &x) {, debería funcionar bien.

6

Está pasando una barra por valor a la subrutina para que se cree una copia. Para evitar hacer una copia pasarlo por referencia:

void subroutine(Foo& x) 
{ 
    cout << "Hello!" << endl; 
} 

Puede evitar las copias accidentales de su clase declarando el constructor de copia y copiar la asignación privada operador de la siguiente manera:

class Foo { 
private: 

    double *array; 

    Foo(const Foo&); 
    Foo& operator=(const foo&); 

public: 
    ... 
}; 

, entonces obtendrá una compilación error en su lugar. Si realmente necesita poder hacer una copia de su clase, entonces necesitará implementar estas funciones para realizar una "copia profunda" (o mejor aún, use std::vector<float> y deje que eso administre la memoria, incluida la copia segura).

+0

con C++ 11 es mejor declararlos como 'eliminados' en su lugar –

Cuestiones relacionadas