2009-09-07 11 views
5

he hecho la siguiente pequeño programa: (básicamente una clase que COUTS si se crea, copiado o destruida y una principal que hace un poco de ese)std C++ destrucción elemento contenedor y el comportamiento de inserción

class Foo 
{ 
public: 
Foo(string name): _name(name) 
{ 
    cout << "Instance " << _name << " of Foo created!" << std::endl; 
}; 
Foo(const Foo& other): _name(other._name) 
{ 
    cout << "Instance " << _name << " of Foo copied!" << std::endl; 
}; 

~Foo() 
{ 
    cout << "Instance " << _name << " of Foo destroyed!" << std::endl; 
} 
string _name; 
}; 



int main(int argc, char**argv) 
{ 
Foo albert("Albert"); 
Foo bert("Bert"); 
{ 
    vector<Foo> v1, v2; 
    system("PAUSE"); 

    v1.push_back(albert); 
    system("PAUSE"); 

    v2.push_back(bert); 
    system("PAUSE"); 

    v1 = v2; 
    system("PAUSE"); 
} 
    system("PAUSE"); 
} 

El resultado es el siguiente:

Instance Albert of class Foo created! 
Instance Bert of class Foo created! 
Press any key... 
Instance Albert of class Foo copied!  
Instance Albert of class Foo copied!  // why another copy? 
Instance Albert of class Foo destroyed! // and destruction? 
Press any key... 
Instance Bert of class Foo copied! 
Instance Bert of class Foo copied! 
Instance Bert of class Foo destroyed! 
Press any key...      // v1=v2 why did the albert instance not get destroyed? 
Press any key...      
Instance Bert of class A destroyed! 
Instance Bert of class A destroyed! 
Press any key...      // there's still an albert living in the void 

Esto me parece muy extraño. ¿Por qué me molesto en pasar algo como referencia si se copia dos veces de todos modos? ¿Por qué el v1.operator = (otro) no destruye los elementos que contiene? Encajaría muy bien con el comportamiento de shared_ptr. ¿Puede alguien decirme por qué?

ADEMÁS Pongo esto en un bucle sin fin y comprobado el uso de mem, no parece producir una fuga mem al menos.

ADICION Ok, la memoria no es un problema porque utiliza operator = en lugar del copy ctor, ok gracias. Cuando agrego

v1.reserve(10); 
v2.reserve(10); 

el número lógico de los ejemplares se realice. sin eso, reasigna y copia el vector completo para cada push_back, (que encuentro bastante retardado incluso para vectores pequeños). En cuanto a esto voy a considerar el uso de .reserve más y optimizar mis operadores de asignación igual que el infierno :)

ADEMÁS: RESUMEN

  1. Todas estas cuestiones parecen específico para VC++ 2005.
  2. Si el tamaño de los dos contenedores coincide, mi implementación utiliza operator = en los elementos en lugar de destruir los antiguos y copiar los nuevos, lo que parece práctica de sonido. SI los tamaños son diferentes, se usan destrucción y copia normal.
  3. Con la implementación de 2005, ¡uno tiene que usar la reserva! De lo contrario, un rendimiento abismal y no estándar.
  4. Estas cajas negras son mucho más negras de lo que pensaba.
+0

¿Has intentado compilar esto como una versión de lanzamiento? – jalf

+0

Sí. Mismo resultado. – AndreasT

+0

Es fácil probarse a sí mismo, solo copie y pegue en un proyecto vacío y, agregue incluye iostream, vector y cadena, y listo. – AndreasT

Respuesta

4

¿Por qué me molesto en pasar algo como referencia si se copia dos veces de todos modos?

usted debe considerar los tipos de contenedores STL como caja negra que puede copiar los objetos se almacenan con la frecuencia que lo necesitan. Por ejemplo, cada vez que se cambia el tamaño del contenedor, se copiarán todos los objetos.

Es posible que la implementación de su compilador de push_back() utilice una copia adicional temporal. En mi máquina (gcc en Mac OS X), no hay copias adicionales durante push_back() (según la salida de su programa).

Esta copia ocurre en algún lugar del código STL, no en el constructor de copia (ya que utiliza una referencia).

¿Por qué la v1.operator = (otra) no destruir los elementos que contiene?

Foo::operator= serán llamados para la instancia "Albert" con el ejemplo "Bert" como argumento. Por lo tanto, no hay una operación implícita de destrucción y copia aquí. Es posible que desee comprobar esto al proporcionar su propia implementación para el operador:

Foo& operator=(const Foo& other) { 
    cout << "Instance " << other._name << " of Foo assigned to " << _name << "!" << std::endl; 
    return *this; 
} 

Esto produce la siguiente salida en mi máquina:

Instancia Alberto de Foo creado!
Instancia Bert de Foo creado!
Instancia Albert of Foo copiado!
Instancia Bert de Foo copiado!
Instancia Bert de Foo asignado a Albert!
Instancia Bert de Foo destruido!
¡Instancia Albert of Foo destruida!
Instancia Bert de Foo destruido!
¡Instancia Albert of Foo destruida!

+1

Y tenga en cuenta que el simple hecho de hacer un seguimiento de las copias significa que su copiadora se vuelve más compleja hasta el punto en que el optimizador ya no puede eliminarla. ¡Así que estás contando llamadas de ctor que existen solo porque se cuentan! – MSalters

+0

El argumento de la caja negra es válido, sin embargo, siempre esperé que no fueran cajas negras estúpidas :). Bueno, otra desilusión M $. – AndreasT

+0

@MSalters: Esto es interesante. El principio de incertidumbre C++. – stribika

3

Hay un operador auto generated =. Cuando haces v1 = v2 ese operador es usado. En ese punto, una de las instancias de "albert" se convierte en "bert". Trate de añadir esta función a Foo:

Foo& operator = (const Foo& rval) { 
    cout << _name << " = " << rval._name << endl; 
    _name = rval._name; 
    return *this; 
} 

Esto es lo mismo que generó el automóvil, pero imprime un mensaje de depuración para que pueda ver lo que está sucediendo.

+0

y qué pasa con las copias sobrantes y la pérdida de memoria (que en realidad no está sucediendo, ver arriba) – AndreasT

+1

Si agrega esta función (o un contador estático), puede ver que no hay fugas. En cuanto a las copias adicionales, no hay garantía de que un contenedor STL solo copie las cosas una vez. Puede ser un pase por valor o alguna otra cosa dependiendo de su implementación de STL. – stribika

1

La "copia doble" no ocurre cuando se compila con GCC. Esto debe ser específico para la forma en que se implementa std :: vector en VC++.

+0

Parece específico para VS2005 – AndreasT

1

Visual Studio 2008 me da el siguiente resultado:

Instance Albert of Foo created! 
Instance Bert of Foo created! 
Press any key to continue . . . 
Instance Albert of Foo copied! 
Press any key to continue . . . 
Instance Bert of Foo copied! 
Press any key to continue . . . 
Press any key to continue . . . << here auto-generated operator= doing its job 
Instance Bert of Foo destroyed! 
Instance Bert of Foo destroyed! << this is Albert was originally 
Press any key to continue . . .

parece ser que std::vector implementación no es muy eficaz en VS2005.

+1

¡Eso es definitivamente cierto! – AndreasT

Cuestiones relacionadas