On the use of smart pointers

2

Suppose we have the hypothetical case where pointers are used for all types of variables. More exactly, we have the following procedure to read the elements of a one-dimensional array:

void leer_arreglo(int *&v)
{
    int *i = new int;
    for (*i = 0; *i < dim_v; (*i)++)
    {
        cout << "Elemento " << *i + 1 << ": ";
        cin >> v[*i];
    }
    delete i;       
}

As you can see, even the variable i , which is only used to traverse the elements of the array, is declared as a pointer to int , and as such, at the end of the procedure the memory must be manually released .

What would be the most reasonable solution for the variable i to remain within the scope of the loop for and at the same time avoid manually releasing the memory allocated for that pointer?

My first attempt was the one that follows:

void leer_vector(int *&v)
{
    for (int *i = new int(0); *i < dim_v; (*i)++)
    {
        cout << "Elemento " << *i + 1 << ": ";
        cin >> v[*i];
    }
    delete i;       
}

The problem is that an error is generated at the moment of arriving at the delete i; instruction since said variable no longer exists outside the scope of for .

Then I came up with the following:

void leer_vector(int *&v)
{
    for (int *i = new int(0); *i < dim_v; (*i)++)
    {
        cout << "Elemento " << *i + 1 << ": ";
        cin >> v[*i];
        delete i;
    }       
}

However, I have serious doubts that the above is a good idea because in each iteration I am releasing the assigned memory.

I did not really know what to do until I discovered the so-called smart pointers ( smart pointers , thanks Paula_plus_plus) and then I came to the following code:

void leer_vector(int *&v)
{
    for (auto i = make_unique<int>(); *i < dim_v; (*i)++)
    {
        cout << "Elemento " << *i + 1 << ": ";
        cin >> v[*i];
    }       
}

How nice, right? The variable i is within the scope of for and we do not have to worry about manually releasing the allocated memory. So, my question comes up: Is this a valid use of smart pointers?

I ask this question because despite reviewing some SO links where they are analyzed in which cases should be used this type of pointers, I still do not quite clear if in my particular case the last piece of code that I put is a correct and acceptable solution.

Thanks in advance for your comments and / or answers.

    
asked by Xam 13.05.2018 в 17:34
source

1 answer

3
  

Is this a valid use of smart pointers?

Sure: the code compiles, executes and does not produce errors at runtime or memory leaks; so it is a valid use.

Another thing is that the code you have shown as an example is an smart use of smart pointers, which in my opinion: it is not; for example:

  

What would be the most reasonable solution for the variable i to remain within the scope of the loop for and at the same time avoid freeing the memory allocated for that pointer?

The reason for the intelligent pointers is precisely to free up the memory assigned to them, so if you wanted the assigned memory not to be released: an intelligent pointer should not be your choice.

Intelligent pointers were introduced in the C ++ 11 standard, although in C ++ there was already an attempt to standardize the RAII 1 idiom on pointers 2 before the standard of 2011 that attempt was very limited and not very flexible so it was decided to deprecate it and develop three types of smart pointers:

Single pointer ( std::unique_ptr ).

Envelopes a pointer that will conceptually have a unique owner, when the pointer is stopped or reassigned, the resources that it was managing are freed; it is possible to move the pointer between different areas:

std::unique_ptr<int> dame_dato() {
    std::unique_ptr<int> result;
    // Obtenemos dato, de una fuente externa, el dato necesita ser borrado
    // cuando se deje de usar.
    int *puntero_crudo = factoria_de_datos();
    // Asignamos el puntero
    result.reset(puntero_crudo);
    // Devolvemos el puntero inteligente
    return result;
}

The std::unique_ptr created within dame_dato does not release the pointer assigned when leaving scope, but it does change the owner, the previous owner being the function dame_dato and the new owner the caller of the same.

Shared pointer ( std::shared_ptr ).

Envelops a pointer that conceptually will have multiple owners, the number of owners of the pointer is remembered as an atomic counter managed by the intelligent pointer itself; growing by winning an owner and decreasing by losing it. When the owner counter reaches zero, the resources that the pointer was handling will be freed, it is also possible to move the pointer between different areas and if instead of moving it the owner counter is updated, this can be expensive at the level of process because managing said meter involves several security checks. The following code:

struct S {};

void propietarios(const std::shared_ptr<S> &sp)
{
    std::cout << "Propietarios de " << sp.get() << ": " << sp.use_count() << '\n';
}

void f(std::shared_ptr<S> sp) { propietarios(sp); }

int main()
{
    auto sp = std::make_shared<S>();
    propietarios(sp); // Referencia: no aumenta propietarios
    f(sp);            // Copia: aumenta propietarios
    propietarios(sp); // Referencia: no aumenta propietarios
    return 0;
}

It would produce the following output:

Propietarios de 0x22ea170: 1
Propietarios de 0x22ea170: 2
Propietarios de 0x22ea170: 1

Weak pointer ( std::weak_ptr ).

It wraps around a pointer that conceptually will not own, any use of the pointer requires to block it which will return a shared pointer, it lets you know if the object it points to exists using the function std::weak_ptr::expired .

When to use smart pointers?

The unique pointer will be used when we know that the pointed resource will have a unique owner, the shared pointer will be used when the pointed resource has several owners and the weak pointer will be used to claim the use of shared pointers without appropriating them (more that temporarily).

  • Thanks for mentioning Trauma.
  • The std::auto_ptr , deprecated in C ++ 11 and removed from the standard in C ++ 17.
  • answered by 15.05.2018 / 12:34
    source