C + + smart pointer unique_ptr

Today, let's talk about class template unique_ptr.

Before that, first answer the reader's question: what should I do in C language? There are several suggestions:

  • When writing, try to follow the principle of application within the function and release within the function
  • Note that malloc and free are written in pairs
  • Use static scanning tools, such as< pclint check>
  • Use memory detection tools such as valgrind

Related reading< Common memory problems>.

unique_ptr

A unique_ptr has exclusive access to the object it points to. In other words, there is only one unique at the same time_ PTR points to the same object when this unique_ When PTR is destroyed, the object pointed to is also destroyed. To use it, you need to include the following header file

#include<memory>

Basic use

Common methods are:

std::unique_ptr<int> up;//Unique that can point to int_ PTR, but empty
up = std::unique_ptr<int>(new int(12));

At this time, it is an empty unique_ptr, that is, it does not point to any object.

//unique_ptr<T>
std::unique_ptr<string> up1(new string("bianchengzhuji"));
std::unique_ptr<int[]> up2(new int[10]);//Arrays require special attention

You can also point to a new object. You can also combine the above two methods, such as:

std::unique_ptr<int> up;//Declare an empty unique_ptr
int *p= new int(1111);
up.reset(p);//Make up point to the new object, and p is the built-in pointer

Generally speaking, delete is used to destroy objects, but it can also be destroyed in a specified way. For a simple example, if you open a connection and get a file descriptor, now you want to use unique_ptr to manage, hoping to use unique when not needed_ PTR helps close it.

//Source: official account [programming pearls]
#include<iostream>
#include<unistd.h>
#include<memory>
void myClose(int *fd)
{
    close(*fd);
}
int main()
{
    int socketFd = 10;//just for example
    std::unique_ptr<int,decltype(myClose)*> up(&socketFd,myClose);
    /*Here are two other ways to write, and the latter is to use lambda expressions*/
    //std::unique_ptr<int,void(*)(int*)> up(&socketFd,myClose);
    //std::unique_ptr<int,void(*)(int*)> ip(&socketFd,[](int *fd){close(*fd);});
    return 0;
}

Its usage is as follows:

std::unique_ptr<T,D> up(t,d);
std::unique_ptr<T,D> up(d);//Empty unique_ptr

The meanings are as follows:

  • T unique_ Object types managed by PTR
  • D delete type
  • t unique_ptr managed objects
  • d delete the function / function object, etc., which is used to release the object pointer

decltype(myClose) * is used here to obtain the type of myClose function, * indicates that it is a pointer type, that is, a function pointer, and its incoming parameter is int *. You can also use the method in the comment. For function pointers, refer to< Advanced pointer topic - function pointer >. Even if there is an exception in the later execution, the socket connection can be closed correctly. We can also see later that with shared_ptr is different, unique_ptr binds the remover at compile time to avoid runtime overhead.

Release the object pointed to

In general, unique_ When PTR is destroyed (such as leaving the scope), the object will be released automatically. You can also display the released object in other ways. For example:

up = nullptr;//Set to null to release the object pointed to by up
up.release();//Relinquish control, return the bare pointer, and set up to null
up.reset();//Release the object pointed to by up

You can see the difference between release and reset is that the former will release control and return the bare pointer, and you can continue to use it. The latter directly releases the pointing object.

unique_ptr does not support ordinary copy and assignment

It should be noted that due to unique_ptr is a "unique" feature, which does not allow ordinary copy or assignment, such as:

std::unique_ptr<int> up0;
std::unique_ptr<int> up1(new int(1111));
up0 = up1 //Error, non assignable
std::unique_ptr<int> up2(up1);//Error, copy not supported

Anyway, remember, since unique_ptr is an exclusive object, so any operation that may be shared is not allowed, but can be moved.

Move unique_ Object of PTR

Although unique_ptr exclusive objects, but can also be moved, that is, transfer control. For example:

std::unique_ptr<int> up1(new int(42));
std::unique_ptr<int> up2(up1.release());

up2 accepts the pointer after up1 release, or:

std::unique_ptr<int> up1(new int(42));
std::unique_ptr<int> up2;
up2.reset(up1.release());

Or use move:

std::unique_ptr<int> up1(new int(42));
std::unique_ptr<int> up2(std::move(up1));

Use in functions

Remember in< What's the difference between passing values and pointers? >Did you say that? Since unique_ptr exclusive objects cannot be directly used as parameters. What should I do?

As a parameter

If the function is unique_ptr as a parameter? If you put unique directly like this_ PTR as a parameter must report an error, because it is not allowed to be copied:

//Source: official account [programming pearls]
#include<iostream>
#include<memory>
void test(std::unique_ptr<int> p)
{
    *p = 10;
}
int main()
{
    std::unique_ptr<int> up(new int(42));
    test(up);//Attempt to pass in unique_ptr, compilation error
    std::cout<<*up<<std::endl;
    return 0;
}

The above code compilation will directly report an error. Of course, we can pass ordinary pointers to the function, and use the get function to obtain raw pointers, such as:

//Source: official account [programming pearls]
#include<iostream>
#include<memory>
void test(int *p)
{
    *p = 10;
}
int main()
{
    std::unique_ptr<int> up(new int(42));
    test(up.get());//Pass in a raw pointer as a parameter
    std::cout<<*up<<std::endl;//Output 10
    return 0;
}

Or use reference as parameter:

//Source: official account [programming pearls]
#include<iostream>
#include<memory>
void test(std::unique_ptr<int> &p)
{
    *p = 10;
}
int main()
{
    std::unique_ptr<int> up(new int(42));
    test(up);
    std::cout<<*up<<std::endl;//Output 10
    return 0;
}

Of course, if the external is no longer needed, you can transfer the object to the function you call to manage it. Here, you can use the move function:

//Source: official account [programming pearls]
#include<iostream>
#include<memory>
void test(std::unique_ptr<int> p)
{
    *p = 10;
}
int main()
{
    std::unique_ptr<int> up(new int(42));
    test(std::unique_ptr<int>(up.release()));
    //test(std::move(up));// This way is also OK
    return 0;
}

As return value

unique_ptr can be returned as a parameter:

//Source: official account [programming pearls]
#include<iostream>
#include<memory>
std::unique_ptr<int> test(int i)
{
    return std::unique_ptr<int>(new int(i));
}
int main()
{
    std::unique_ptr<int> up = test(10);
    //std::shared_ptr<int> up = test(10);
    std::cout<<*up<<std::endl;
    return 0;
}

You can also put unique_ Convert PTR to shared_ptr is used, as shown in the annotation line.

Why unique is preferred_ ptr

Back to the question of the title, what is the priority of unique_ptr.

  • Avoid memory leaks
  • Avoid greater overhead

First, I believe it is well understood that automatic management can release when it is not needed, and can even prevent the following situations:

int * p = new int(1111);
/*do something*/
delete p;

If an exception occurs while doing something and exits, there will never be a chance for delete to be executed, which will cause memory leakage. If unique is used_ PTR would not have such a problem. Second, why do you say that? Because compared with shared_ptr, which has less overhead, can even be said to be equivalent to bare pointers. It does not need to maintain atomic operations of reference counting and so on. Therefore, if possible, unique is preferred_ ptr.

summary

This article introduces unique_ The basic usage and usage scenarios of PTR. It can effectively avoid memory leakage and its efficiency is controllable. Therefore, if it can meet the requirements, unique is preferred_ ptr.

Keywords: C++

Added by rjs on Wed, 02 Feb 2022 06:25:32 +0200