What is the C ++ copy constructor for? How should I implement it?

8

I am programmer Java and I am currently studying C++ . In addition to the "normal" constructors (the default constructor and the parameterized constructor), C++ has a copy constructor. I would like to know what it is for and how I should implement it.

    
asked by anonymous 27.03.2014 / 18:01

2 answers

10

The concept may seem strange to a Java programmer, because in this language objects are always treated with "reference semantics". When you do something like:

Objeto obj1 = new Objeto();
Objeto obj2 = obj1;
obj2.alteraValor(4); //Alteração é refletida em obj1

Both variables refer to the same object, so changes made from any of them are reflected in both.

In C ++, the normal is called "value semantics", and the behavior of this expression is different:

Objeto obj1;
Objeto obj2 = obj1;
obj2.alteraValor(4); //Alteração só é feita em obj2, obj1 fica como estava

When an object is created from the assignment of another, it copies the attributes of the original, and each one follows its path after that. And this copy is done with the invocation of the copy constructor.

Most of the time you do not have to worry about it, the compiler generates a copy constructor for your class, which copies all its attributes one by one. You need to worry about this when your class does resource manipulation, to avoid having two objects inadvertently pointing to the same resource.

class Objeto {
    int *recurso;
public:
    Objeto() { //Construtor principal - aloca recurso
        recurso = new int;
    }
    Objeto(const Objeto &outro) { //Construtor de cópia - copia recurso
        recurso = new int;
        *recurso = *(outro.recurso);
    }
    ~Objeto() { //destrutor - libera recurso
        delete recurso;
    }                
};

In this example, without implementing the copy constructor, an assignment operation like that of the first example would cause both objects to point to the same resource, and so when their destructors are invoked it would be deleted twice, which can generate multiple run-time errors, such as heap corruption .

This class should still have overloaded the assignment operator, because if at any other time it is done obj1 = obj3 , we will have the same problem of 2 objects pointing to the same resource. This operator can be implemented like this:

class Objeto {
    ...

    Objeto &operator=(const Objeto &outro) {
        *recurso = *(outro.recurso);
        return *this;
    }

    ...
};

So we come to the concept known as the Rule of Three of C ++:

  

If your class has one of these three: destructor, copy constructor   or assignment operator, it probably needs all three.

In C ++ 11, in addition to creating objects from copies, it is also possible to "steal" the state of other objects, mainly temporary or that are about to be destroyed. For this operation the class needs to provide a move constructor (horrible translation for move constructor ) and the assignment operator moves assignment operator ).

I think it is beyond the scope of this answer to explain the concept behind them, but they would add two more elements to the rule of the three mentioned above, making it the Rule of Five. But implementing all of these methods to manage resources is almost never necessary since the language now provides classes like shared_ptr and unique_ptr that take care of it automatically. Using them for resource management leaves the semantics of what you want to make explicit, prevents some headaches, and eliminates the need to write destructors, builders, and special assignment operators. This is the principle of the Rule of Zero.

    
27.03.2014 / 19:27
1

Basically speaking, a copy constructor is for making a copy, that is, for you to construct an object that is a copy of another. Copy builders have the following signatures:

MyClass( const MyClass& other ); MyClass( MyClass& other ); MyClass( volatile const MyClass& other ); MyClass( volatile MyClass& other );

obtained from www.cplusplus.com

If you do not provide a copy constructor, the compiler will implicitly generate it, but when you need special handling when copying, you must write the constructor explicitly, such as when you do not want to simply copy a pointer , but if you allocate a new area of memory and copy its contents, for example.

Here is an example of a copy builder:

class MinhaClasse
{
public:
MinhaClasse()
{
    ...
    // construtor de MinhaClasse
    ...
}

// Construtor de cópia
MinhaClasse(const MinhaClasse& outra) :
    inteiro(outra.inteiro)
{
}
private:
int inteiro;
};
    
27.03.2014 / 18:51