2010-03-27 11 views
6

Quería ejecutar 1,000 iteraciones de un programa, así que establezca un contador para 1000 en main. Necesitaba reinicializar varias variables después de cada iteración, y como el constructor de la clase tenía todas las inicializaciones ya escritas, decidí llamar a eso después de cada iteración, con el resultado de cada iteración almacenada en una variable en main.Llamar a un constructor para reinicializar variables no parece funcionar?

Sin embargo, cuando llamé al constructor, no tuvo ningún efecto ... me tomó un tiempo darme cuenta, ¡pero no reinició nada!

Creé una función exactamente igual que el constructor, por lo que el objeto tendría su propia versión. Cuando llamé eso, reinició todo como esperaba.

int main() 
{ 
Class MyClass() 

int counter = 0; 

while (counter < 1000) 
{ stuff happens } 

Class(); // This is how I tried to call the constructor initially. 
      // After doing some reading here, I tried: 
      // Class::Class(); 
      // - but that didn't work either 
/* Later I used... 
MyClass.function_like_my_constructor; // this worked perfectly 
*/ 
} 

... Podría alguien intentar explicar por qué lo que hice estuvo mal, o no trabajar, o era tonto o lo que sea? Es decir, mentalmente, pensé, mierda, puedo llamar a este constructor y reiniciar todo esto. ¿Los constructores (idealmente) SOLO se llaman cuando se crea un objeto?

+0

Parece posible con * placement new *. http://stackoverflow.com/questions/6868363/how-to-recall-a-constructor-of-an-initialised-object – Eonil

+0

'MyClass = Class();'? –

Respuesta

8

Su línea Class(); llama al constructor de la clase Class, pero la llama para crear un "objeto temporal". Como no usa ese objeto temporal, la línea no tiene ningún efecto útil.

Los objetos temporales (generalmente) desaparecen al final de la expresión en la que aparecen. Son útiles para pasar como parámetros de función o inicializar otros objetos. Casi nunca es útil crear uno solo en una declaración. El lenguaje lo permite como una expresión válida, es solo que para la mayoría de las clases no hace mucho.

En C++ no hay forma de llamar a un constructor sobre un objeto que ya se ha construido. El ciclo de vida de un objeto C++ es una construcción y una destrucción. Así es como funciona. Si desea restablecer un objeto durante su ciclo de vida, ha hecho lo correcto, que es llamar a una función para restablecerlo. Dependiendo de su clase, es posible que no necesite escribir uno: el operador de asignación predeterminado puede hacer exactamente lo que necesita. Fue entonces cuando un temporal puede ser útil:

Class myObject; 
// ... do some stuff to myObject ... 

myObject = Class(); 

Esto actualiza myObject con los valores de la temporal recién construido. No es necesariamente el código más eficiente posible, ya que crea un temporal, luego copia, luego destruye el temporal, en lugar de simplemente configurar los campos a sus valores iniciales. Pero a menos que su clase sea grande, es poco probable que hacer todo eso 1000 veces tome una cantidad de tiempo notable.

Otra opción es utilizar un nuevo objeto de marca para cada iteración:

int main() { 
    int counter = 0; 
    while (counter < 1000) { 
     Class myObject; 
     // stuff happens, each iteration has a brand new object 
    } 
} 

Tenga en cuenta que Class MyClass(); hace no definen un objeto de tipo de clase, llamada MiClase, y construirlo sin parámetros. Declara una función llamada MyClass, que no toma parámetros y devuelve un objeto de tipo Class. Presumiblemente en su código real, el constructor tiene uno o más parámetros.

+0

Uso constructores, pero rara vez paso parámetros, aunque sé que puedo. La mayoría de las veces siento que debería ponerlos dentro del constructor en el código fuente. ¿Por qué pasar parámetros para hacerlo a través de main? – Azoreo

+0

En ese caso, estoy muy sorprendido de que compile su código, si no está usando parámetros de constructor, pero tampoco usa la sintaxis correcta para constructores sin parámetros. De todos modos, la razón para usar parámetros de constructor es si los objetos de tu clase no son todos iguales. Eche un vistazo a la biblioteca estándar para ver ejemplos: puede especificar el tamaño inicial y el contenido de un vector, etc. –

+0

Usando Microsoft Visual Studio C++ y su compilador incorporado ... Si declaro un constructor como KnightsTour :: KnightsTour (por ejemplo) y luego pongo KnightsTour; en mi archivo de cabecera, arroja errores, diciendo que parece una función, pero que no hay parámetros. Tan ... Agregué una lista de parámetros vacía, es decir, "KnightsTour :: KnightsTour()" y luego "KnightsTour();" en el archivo de cabecera: ¡compila y ejecuta! – Azoreo

1

Sí, este uso no es típico. Cree una función que restablezca sus variables y llame al método cada vez que lo necesite.

0

Cae presa de una mala interpretación común de C++. El nuevo C++ 0x hace las cosas un poco más claras.

El problema es que la sintaxis de construcciones se parece a una llamada de función.

void foo(int i) { } 
class Foo { }; 

Foo(10); // construct a temporary object of type foo 
foo(10); // call function foo 
Foo{10}; // construct a temporary object of type foo in c++0x syntax 

Creo que la sintaxis de C++ 0x es más clara.

Puede hacer lo que quiera con esta sintaxis. Pero tenga en cuenta que es muy avanzado y debe no hacerlo.

MyClass.~Class(); // destruct MyClass 
new(&MyClass) Class; 
+0

La principal razón práctica para no hacer esto es que si el constructor de 'Clase' lanza, entonces el objeto' MyClass' está en un estado incoherente. El desenrollado de la pila intentará destruirlo nuevamente, produciendo un comportamiento indefinido. Eso, y no gana nada con una función de miembro bien escrita 'operator =' o 'reset'. Entonces, si controlas 'MyClass' no hay necesidad de hacer esto, y si no controlas' MyClass' entonces está pidiendo problemas ;-) –

5

¿Qué ocurre en la línea de que la lectura ...

Class(); 

es que usted, de hecho, llamar al constructor - para un objeto temporal que se está construyendo desde cero, y que luego se destruye inmediatamente ya que no estás haciendo nada con eso. Es muy parecido a enviar contenido a Class, que crea un valor usando una llamada de constructor, excepto que en este caso no hay valor para lanzar, por lo que se usa el constructor predeterminado.

Es posible que el compilador optimice este temporal, por lo que no hay ningún constructor en absoluto. No estoy seguro de si eso está permitido o no.

Si desea reinicializar miembros, llamar al constructor no es la forma de hacerlo. Mueva todo su código de inicialización a otro método y llámelo desde su constructor, y cuando quiera reinicializar, en su lugar.

+2

"No estoy seguro de si eso está permitido o no" - lo es, pero solo si ni el constructor ni el destructor tienen ningún efecto sobre el comportamiento observable del programa. Entonces, si pones un poco de rastreo para ver si se omiten, entonces no se pueden omitir. –

0

Con tales requisitos, generalmente escribo un método clear() (público). Lo llamo desde constructor, destructor. El código de usuario puede llamarlo cuando quiera.

class Foo 
{ 
    public: 

    Foo() { clear(); } 

    ~Foo() { clear(); } 

    void clear(); // (re)initialize the private members 

    private: 

    // private members 
}; 

Para responder a la pregunta aquí, el método clear() se puede llamar siempre que sea necesario para volver a inicializar la clase ya que estaba justo después de la construcción inicial.

Cuestiones relacionadas