Copiar constructor en C++

Prerrequisito: Fundamentos de Constructor 

Un constructor de copias es una función miembro que inicializa un objeto usando otro objeto de la misma clase. Un constructor de copias tiene el siguiente prototipo de función general: 

ClassName (const ClassName &old_obj); 
  •  El constructor de copias se utiliza para inicializar los miembros de un objeto recién creado copiando los miembros de un objeto ya existente.
  • El constructor de copias toma una referencia a un objeto de la misma clase como argumento.
              Muestra(Muestra &t)
              {
                         id=t.id;
              }
  •  El proceso de inicialización de miembros de un objeto a través de un constructor de copia se conoce como inicialización de copia.
  • También se denomina inicialización por miembro porque el constructor de copia inicializa un objeto con el objeto existente, ambos pertenecientes a la misma clase en una base de copia de miembro por miembro.
  • El constructor de copias puede ser definido explícitamente por el programador. Si el programador no define el constructor de la copia, el compilador lo hace por nosotros.
     

Por ejemplo: 

CPP

// C++ program to demonstrate the working
// of a COPY CONSTRUCTOR
#include <iostream>
using namespace std;
  
class Point {
private:
    int x, y;
  
public:
    Point(int x1, int y1)
    {
        x = x1;
        y = y1;
    }
  
    // Copy constructor
    Point(const Point& p1)
    {
        x = p1.x;
        y = p1.y;
    }
  
    int getX() { return x; }
    int getY() { return y; }
};
  
int main()
{
    Point p1(10, 15); // Normal constructor is called here
    Point p2 = p1; // Copy constructor is called here
  
    // Let us access values assigned by constructors
    cout << "p1.x = " << p1.getX()
         << ", p1.y = " << p1.getY();
    cout << "\np2.x = " << p2.getX()
         << ", p2.y = " << p2.getY();
  
    return 0;
}
Producción

p1.x = 10, p1.y = 15
p2.x = 10, p2.y = 15

 

C++

// Example: Implicit copy constructor
  
#include<iostream>
using namespace std;
  
class Sample
{          
      int id;
    public:
    void init(int x)
    {
        id=x;    
    }    
    void display()
    {
        cout<<endl<<"ID="<<id;
    }
};
  
int main()
{
    Sample obj1;
    obj1.init(10);
    obj1.display();
      
    Sample obj2(obj1); //or obj2=obj1; 
    obj2.display();
    return 0;
}
Producción

ID=10
ID=10

 

C++

// Example: Explicit copy constructor
  
#include<iostream>
using namespace std;
  
class Sample
{
    int id;
    public:
    void init(int x)
    {
        id=x;    
    }    
    Sample(){}  //default constructor with empty body
      
    Sample(Sample &t)   //copy constructor
    {
        id=t.id;
    }
    void display()
    {
        cout<<endl<<"ID="<<id;
    }
};
int main()
{
    Sample obj1;
    obj1.init(10);
    obj1.display();
      
    Sample obj2(obj1); //or obj2=obj1;    copy constructor called
    obj2.display();
    return 0;
}
Producción

ID=10
ID=10

 

GeeksforGeeks-CPP-Foundation-Course

C++

// Example:
  
#include<iostream>
#include<string.h>
using namespace std;
  
class student
{
    int rno;
    char name[50];
    double fee;
    public:
    student(int,char[],double);
    student(student &t)       //copy constructor
    {
        rno=t.rno;
        strcpy(name,t.name);
        fee=t.fee;
    }
    void display();
      
};
  
  
  
  
    student::student(int no,char n[],double f)
    {
        rno=no;
        strcpy(name,n);
        fee=f;
    }    
  
   void student::display()
    {
        cout<<endl<<rno<<"\t"<<name<<"\t"<<fee;
    }
      
int main()
{
    student s(1001,"Ram",10000);
    s.display();
      
    student ram(s);   //copy constructor called
    ram.display();
      
    return 0;
}

 

Producción

1001    Ram    10000
1001    Ram    10000

 

C++

// Example:
  
#include<iostream>
#include<string.h>
using namespace std;
  
class student
{
    int rno;
    char name[50];
    double fee;
    public:
    student(int,char[],double);
    student(student &t)       //copy constructor (member wise initialization)
    {
        rno=t.rno;
        strcpy(name,t.name);
          
    }
    void display();
    void disp()
    {
        cout<<endl<<rno<<"\t"<<name;
    }
      
};
    student::student(int no, char n[],double f)
    {
        rno=no;
        strcpy(name,n);
        fee=f;
    }    
  
   void student::display()
    {
        cout<<endl<<rno<<"\t"<<name<<"\t"<<fee;
    }
      
  
  
int main()
{
    student s(1001,"Ram",10000);
    s.display();
      
    student ram(s);   //copy constructor called
    ram.disp();
      
    return 0;
}

 

Producción

1001    Ram    10000
1001    Ram

 

¿Cuándo se llama al constructor de copias? 

En C++, se puede llamar a un constructor de copias en los siguientes casos: 

  • Cuando un objeto de la clase se devuelve por valor. 
  • Cuando se pasa un objeto de la clase (a una función) por valor como argumento. 
  • Cuando un objeto se construye a partir de otro objeto de la misma clase. 
  • Cuando el compilador genera un objeto temporal.

Sin embargo, no se garantiza que se llamará a un constructor de copia en todos estos casos, porque el estándar C++ permite que el compilador optimice la copia en ciertos casos, un ejemplo es la optimización del valor de retorno (a veces denominada RVO).

Copiar elisión

En la elisión de copia , el compilador evita la realización de copias adicionales, lo que resulta en un ahorro de espacio y mejora la complejidad del programa (tanto en tiempo como en espacio); Por lo tanto, hacer que el código sea más optimizado.  

Por ejemplo:

C++

// C++ program to demonstrate the working of copy elision
#include <iostream>
using namespace std;
  
class GFG {
public:
    void print() { cout << " Hello Dear!"; }
};
  
int main()
{
    GFG G;
    for (int i = 0; i <= 2; i++) {
        G.print();
        cout<<"\n";
    }
    return 0;
}

Salida :

 Hello Dear!
 Hello Dear!
 Hello Dear!

Ahora depende del compilador decidir qué quiere imprimir, podría imprimir la salida anterior o podría imprimir el caso 1 o el caso 2 a continuación, y esto es lo que es la Optimización del valor de retorno . En palabras simples, RVO es una técnica que le da al compilador algún poder adicional para terminar el objeto temporal creado, lo que resulta en cambiar el comportamiento/características observables del programa final.

Caso 1:

Hello Dear!
Hello Dear!

Caso 2:

Hello Dear!

¿Cuándo se necesita un constructor de copias definido por el usuario? 

Si no definimos nuestro propio constructor de copia, el compilador de C++ crea un constructor de copia predeterminado para cada clase que hace una copia de miembros entre objetos. El constructor de copias creado por el compilador funciona bien en general. Necesitamos definir nuestro propio constructor de copia solo si un objeto tiene punteros o cualquier asignación de tiempo de ejecución del recurso, como un identificador de archivo , una conexión de red, etc.
 

El constructor predeterminado solo hace una copia superficial. 

shallow

La copia profunda solo es posible con un constructor de copia definido por el usuario. En un constructor de copias definido por el usuario, nos aseguramos de que los punteros (o referencias) de los objetos copiados apunten a nuevas ubicaciones de memoria.  

Deep Copy 

Copiar constructor vs Operador de asignación 

La principal diferencia entre el constructor de copia y el operador de asignación es que el constructor de copia crea un nuevo almacenamiento de memoria cada vez que se llama, mientras que el operador de asignación no crea un nuevo almacenamiento de memoria.

¿Cuál de las siguientes dos declaraciones llama al constructor de copia y cuál llama al operador de asignación? 

CPP

MyClass t1, t2;
MyClass t3 = t1;  // ----> (1)
t2 = t1;          // -----> (2) 

Se llama a un constructor de copias cuando se crea un nuevo objeto a partir de un objeto existente, como una copia del objeto existente. El operador de asignación se llama cuando a un objeto ya inicializado se le asigna un nuevo valor de otro objeto existente. En el ejemplo anterior (1) llama al constructor de copia y (2) llama al operador de asignación. Vea esto para más detalles.

¿Escriba una clase de ejemplo donde se necesita un constructor de copia? 

A continuación se muestra un programa completo en C++ para demostrar el uso del constructor Copy. En la siguiente clase String, debemos escribir un constructor de copias. 

CPP

// C++ program to demonstrate the 
// Working of Copy constructor
#include <cstring>
#include <iostream>
using namespace std;
  
class String {
private:
    char* s;
    int size;
  
public:
    String(const char* str = NULL); // constructor
    ~String() { delete[] s; } // destructor
    String(const String&); // copy constructor
    void print()
    {
        cout << s << endl;
    } // Function to print string
    void change(const char*); // Function to change
};
  
// In this the pointer returns the CHAR ARRAY
// in the same sequence of string object but
// with an additional null pointer '\0' 
String::String(const char* str)
{
    size = strlen(str);
    s = new char[size + 1];
    strcpy(s, str);
}
  
  
void String::change(const char* str)
{
    delete[] s;
    size = strlen(str);
    s = new char[size + 1];
    strcpy(s, str);
}
  
String::String(const String& old_str)
{
    size = old_str.size;
    s = new char[size + 1];
    strcpy(s, old_str.s);
}
  
int main()
{
    String str1("GeeksQuiz");
    String str2 = str1;
  
    str1.print(); // what is printed ?
    str2.print();
  
    str2.change("GeeksforGeeks");
  
    str1.print(); // what is printed now ?
    str2.print();
    return 0;
}
Producción

GeeksQuiz
GeeksQuiz
GeeksQuiz
GeeksforGeeks

¿Cuál sería el problema si eliminamos el constructor de copias del código anterior? 

Si eliminamos el constructor de copias del programa anterior, no obtendremos el resultado esperado. Los cambios realizados en str2 también se reflejan en str1, lo que nunca se espera. 
 

CPP

#include<iostream>
#include<cstring>
using namespace std;
  
class String
{
private:
    char *s;
    int size;
public:
    String(const char *str = NULL); // constructor
    ~String() { delete [] s;  }// destructor
    void print() { cout << s << endl; }
    void change(const char *);  // Function to change
};
  
String::String(const char *str)
{
    size = strlen(str);
    s = new char[size+1];
    strcpy(s, str);
}
  
// In this the pointer returns the CHAR ARRAY
// in the same sequence of string object but
// with an additional null pointer '\0' 
void String::change(const char *str)
{
    delete [] s;
    size = strlen(str);
    s = new char[size+1];
    strcpy(s, str);
}
  
int main()
{
    String str1("GeeksQuiz");
    String str2 = str1;
  
    str1.print(); // what is printed ?
    str2.print();
  
    str2.change("GeeksforGeeks");
  
    str1.print(); // what is printed now ?
    str2.print();
    return 0;
}

Producción: 

GeeksQuiz
GeeksQuiz
GeeksforGeeks
GeeksforGeeks

¿Podemos hacer que el constructor de copias sea privado? 

Sí, un constructor de copias se puede hacer privado. Cuando hacemos que un constructor de copia sea privado en una clase, los objetos de esa clase se vuelven no copiables. Esto es particularmente útil cuando nuestra clase tiene punteros o recursos asignados dinámicamente. En tales situaciones, podemos escribir nuestro propio constructor de copia como el ejemplo anterior de String o crear un constructor de copia privada para que los usuarios obtengan errores del compilador en lugar de sorpresas en tiempo de ejecución. 

¿Por qué se debe pasar el argumento a un constructor de copia como referencia? 

Se llama a un constructor de copias cuando se pasa un objeto por valor. El constructor de copias en sí mismo es una función. Entonces, si pasamos un argumento por valor en un constructor de copia, se haría una llamada al constructor de copia para llamar al constructor de copia que se convierte en una string de llamadas sin terminación. Por lo tanto, el compilador no permite que los parámetros se pasen por valor.

¿Por qué el argumento de un constructor de copia debe ser constante?

Una razón para pasar la referencia a const es que deberíamos usar const en C++ siempre que sea posible para que los objetos no se modifiquen accidentalmente. Esta es una buena razón para pasar la referencia como const, pero hay más que ‘ ¿Por qué el argumento de un constructor de copia debe ser const?’

Este artículo es una contribución de Shubham Agrawal . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir tu artículo en write.geeksforgeeks.org . Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks. Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *