es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Es posible evitar la llamada al constructor de copia en C++?

Estoy escribiendo una función de plantilla que acepta una clase personalizada (que puede ser cualquier clase o tipo primitivo) como argumento de plantilla, lee algunos datos (de ese tipo) desde un flujo de entrada, y luego los almacena en un mapa no ordenado similar a este:

std::unordered_map<CustomClass, std::vector> mapa;

He implementado una clase personalizada para probar el comportamiento. He sobrecargado std::hash para que esta clase pueda ser almacenada en un mapa no ordenado como clave y he sobrecargado todos los operadores y constructores para que cada vez que son llamados, recibo un mensaje en la consola (por ejemplo, cuando se llama a un constructor de copia, recibo un mensaje “constructor de copia […datos…]”).

EDICIÓN: Como se solicitó en los comentarios, aquí está la definición e implementación de la clase personalizada (tenga en cuenta que la clase aquí es solo un marcador de posición para que podamos discutir la idea general detrás de esta pregunta. Soy muy consciente de que es tonto y no se debería implementar de esta manera. El código de los operadores >> y << no está aquí, para evitar el desorden)

class CustomClass {
public:
CustomClass(int a=0) {
std::cout << “constructor predeterminado” << std::endl;
m_data = a;
}

CustomClass(const CustomClass& other) {
    std::cout << "constructor de copia " ;//<< std::endl;
    m_data = other.m_data;
    std::cout << "[" << m_data << "]" << std::endl;
}

CustomClass(CustomClass&& other) {
    std::cout << "constructor de movimiento" << std::endl;
    m_data = other.m_data;
}

CustomClass& operator=(const CustomClass& other) {
    std::cout << "operador de asignación de copia" << std::endl;
    if(this != &other){
       m_data = other.m_data;
    }
    return *this;
}

CustomClass& operator=(CustomClass&& other) {
    std::cout << "operador de asignación de movimiento" << std::endl;
    if(this != &other){
        m_data = other.m_data;
    }
    return *this;
}

~CustomClass() {
    std::cout << "destructor" << std::endl;
}

int m_data;

};

Ahora mi pregunta es esta: ¿Es posible leer datos desde el flujo de entrada y construirlos en el lugar donde se necesitan sin llamar al constructor de copia?

Ejemplo de algunos códigos:

CustomClass x1; // llamada al constructor predeterminado
CustomClass x2; // llamada al constructor predeterminado
std::cout << “—-” << std::endl;
std::cin >> x1 >> x2; // mi entrada
std::cout << “—-” << std::endl;
map[x1].emplace_back(x2); // 2 llamadas al constructor de copia
std::cout << “—-” << std::endl;
std::cout << map[x1][0] << std::endl; // llamada al operador ==
std::cout << “—-” << std::endl;

Y aquí hay un ejemplo de salida de ese código:

constructor predeterminado

constructor predeterminado

[1]

[2]

constructor de copia [1]

constructor de copia [2]

operador ==

[2]

destructor
destructor
destructor
destructor

Me gustaría que cada objeto de esta clase se construyera solo una vez.

¿Es posible evitar estos constructores de copia? Si no ambos, al menos el que se llama durante la llamada a emplace_back(). ¿Es posible construir el objeto en el vector exactamente donde necesita estar en la memoria pero que esta llamada funcione para cada tipo?

Si necesito ampliar mi pregunta, por favor díganme en los comentarios, estaré encantado de hacerlo.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Sí, es posible construir el objeto en el lugar donde se necesita sin llamar al constructor de copia utilizando la función miembro emplace() del contenedor en lugar de emplace_back().

    Aquí hay un ejemplo de cómo puede modificar su código para lograr esto:

    CustomClass x1;                        // llamada al constructor por defecto
    CustomClass x2;                        // llamada al constructor por defecto
    std::cout << "----" << std::endl;
    std::cin >> x1 >> x2;                  // mi entrada
    std::cout << "----" << std::endl;
    map.emplace(x1, std::vector<customclass>{}).first->second.emplace_back(std::move(x2));              
    std::cout << "----" << std::endl;
    std::cout << map[x1][0] << std::endl;  // llamada al operador== 
    std::cout << "----" << std::endl;
    

    En lugar de construir un objeto temporal CustomClass con x2 al llamar a emplace_back(), este código utiliza std::move(x2), lo que permite que el objeto se construya en el lugar utilizando un constructor de movimiento, en lugar de un constructor de copia.

    La función emplace() se utiliza para construir el objeto std::vector en el mapa y devolver un iterador a su ubicación en el mapa, y luego se utiliza la función first->second.emplace_back() para insertar el objeto CustomClass dentro de ese vector en el lugar, sin llamar a ningún constructor de copia.

    Al utilizar emplace() y emplace_back() en lugar de intentar construir objetos y luego insertarlos o ubicarlos en el contenedor, se pueden evitar cualquier copia o movimiento innecesario y construir los objetos sólo una vez.

Comments are closed.