What is the C++ copy builder for? How do I implement it?

Asked

Viewed 6,412 times

10

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

2 answers

12


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, and therefore changes made from either of them are reflected in both.

In C++, 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 follows its path after that. And this copy is made by invoking the copy builder.


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

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 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 various runtime errors such as corruption of heap.

This class should still have overloaded the allocation operator, as if at some 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 thus:

class Objeto {
    ...

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

    ...
};

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

If your class has one of these three: destructor, copy builder or assignment operator, she 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, mostly temporary or that are about to be destroyed. For this operation to work the class needs to provide a drive builder (horrible translation to constructor) and the operator of handling assignment (move 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 before, turning it into the Rule of Five. But implementing all these methods to manage resources is almost never necessary as the language now provides classes like shared_ptr and unique_ptr who automatically take care of it. Using them for resource management leaves the semantics of what one wants to do explicit, prevents some headaches, and eliminates the need to write special destructors, constructors, and assignment operators. This is the principle of the Rule of Zero.

  • 3

    Note: A complete reference to the 3 rule in the present day, would make reference the 5 rule and the 0 rule.

  • 1

    I agree with @pepper_chico, talk about the old rule of 3 and not say that currently the preferred rule is the rule of 0, and in some cases the rule of 5, is one of the few things that make me not give upvote.

  • 1

    I started to put an explanation of the rules of 5 and 0, but then I was already talking about moves constructs and moves Semantics and the answer was getting to triple the required size. I tried to give a short version.

  • Thanks for the answers and the comments. I am researching more on the subject and for sure everything that was discussed will add me much.

1

Basically speaking, a copy constructor serves to make a copy, that is, for you to build 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.

If you do not provide a copy constructor the compiler will generate one implicitly, but when you need some special treatment when copying it is necessary to write the constructor explicitly, as when you do not want to simply copy a pointer, but allocate a new memory area and copy its content, for example.

Below is an example copy builder:

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

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

Browser other questions tagged

You are not signed in. Login or sign up in order to post.