2010-06-06 5 views
6

He encontrado un hecho muy perjudicial sobre los mapas stl. Por alguna razón, no puedo insertar objetos en el mapa para construirlos/destruirlos una sola vez.cómo obtener el mapa stl para construir/destruir objeto insertado una sola vez

Ejemplo:

struct MyObject{ 
    MyObject(){ 
     cout << "constructor" << endl; 
    } 
    ~MyObject(){ 
     cout << "destructor" << endl; 
    } 
}; 
int main() { 
    std::map<int, MyObject> myObjectsMap; 
    myObjectsMap[0] = MyObject(); 
    return 0; 
} 

devuelve:

constructor 
destructor 
destructor 
constructor 
destructor 

Si hago:

typedef std::pair<int, MyObject> MyObjectPair; 
myObjectsMap.insert(MyObjectPair(0,MyObject())); 

devuelve:

constructor 
destructor 
destructor 
destructor 

Estoy insertando Objetos responsables de su propia asignación de memoria, por lo que cuando se destruyan se limpiarán ellos mismos, ser destruido varias veces me está causando algunos problemas.

+1

aparentemente, 'std :: map ' no ofrece una interfaz sin copia para crear nuevas entradas. Recomiendo los punteros compartidos aquí - 'std :: map >' –

Respuesta

1

Así es como funcionan map y los otros contenedores, no se puede evitar. Es por eso que std::auto_ptr no se puede usar en una colección, por ejemplo.

+0

Creo que cambiaré a un vector y mantendré los punteros en el mapa. –

6

Le sugiero que agregue un constructor de copia, eso es lo que se está utilizando para las construcciones 'faltantes', creo.

Código:

#include <iostream> 
#include <map> 

using namespace std; 

struct MyObject{ 
    MyObject(){ 
     cout << "no-arg constructor" << endl; 
    } 
    MyObject(const MyObject&) { 
    cout << "const copy constructor" << endl; 
    } 
    ~MyObject(){ 
     cout << "destructor" << endl; 
    } 
}; 
int main() { 
    std::map<int, MyObject> myObjectsMap; 
    myObjectsMap[0] = MyObject(); 
    return 0; 
} 

Salida:

no-arg constructor 
const copy constructor 
const copy constructor 
destructor 
destructor 
no-arg constructor 
destructor 
destructor 
4

std::map se le permite hacer tantas copias de los objetos como lo desee. Esta es la implementación definida y usted no tiene control sobre esto. Las construcciones "faltantes" que advierta, por cierto, pueden ser para llamar al constructor de copias, que no definió.

Lo que puede hacer, sin embargo, es usar un flyweight para que la construcción de un objeto, de hecho, obtenga un objeto existente de un grupo de objetos preexistentes, y la destrucción de un objeto no haga nada. La agrupación nunca libera sus objetos, pero siempre mantiene un control sobre todos ellos. De esta forma, el uso de la memoria es grande de principio a fin, pero no cambia mucho a lo largo de la vida del programa.

2

Para poder ser utilizado en un contenedor estándar, sus objetos deben poder copiarse y asignarse. Si sus objetos no se ajustan a esto, es probable que tenga problemas.

Dicho esto, si (como su código de ejemplo indica) sólo tiene un objeto predeterminado construida insertada en el mapa sólo se puede usar el operador [] por su efecto secundario:

// Insert default constructed MyObject at key 0 
myObjectsMap[0]; 

Editar

No estoy del todo claro por su pregunta, pero si no tiene claro el número de objetos construidos y cree que hay una discrepancia de constructor/destructor, tenga en cuenta que el compilador proporcionará un constructor de copia que no se conecta al std::cout ya que no proporciona un usuario declarado.

+0

Tenía la esperanza de poder tener una sola construcción y una destrucción, como si estuviera usando un vector. vector v; v.push_back (MyObject()); // produce solo una construcción y destrucción. –

+0

¿Has probado lo que 'myObjectsMap [0];' realmente hace? –

+0

Bueno, realmente no, en realidad estaba usando el método de inserción, tiene menos sobrecarga pero aún produce más copias de las que esperaba. De cualquier manera, creo que me quedaré con el vector y usaré el mapa solo para los punteros. La publicación anterior fue bastante clara de lo que realmente está sucediendo. Bueno saber. –

2

Cuando dice myObjectsMap [0], está llamando al constructor predeterminado para MyObject. Esto se debe a que todavía no hay nada en [0] y acaba de acceder a él. It's in the manual.

Al presionar MyObject(); está creando una instancia temporal de MyObject utilizando el constructor predeterminado.

Dado que permitió que el compilador definiera su constructor de copia, verá más mensajes de destructor que de constructor. (Solo puede haber un destructor, pero muchos constructores, a diferencia de la construcción de una casa.) Si el objeto nunca se debe copiar de esta manera, es probable que desee declarar un constructor de copias privado y un operador de asignación de copias.

Estás llamando al constructor por defecto y el constructor de copia dos veces cada con este código:

myObjectsMap[0] = MyObject(); 

Al hacer esto:

myObjectsMap.insert(MyObjectPair(0,MyObject())); 

se llama al constructor por defecto una vez y el copia el constructor 3 veces

Probablemente debería utilizar punteros como valores de mapa en lugar de los objetos en sí, en particular, sugiero mirar shared_ptr.

note: tests were done using GCC 3.4.5 on a Windows NT 5.1 machine. 
Cuestiones relacionadas