Resumen y disposición del conocimiento del puntero inteligente C ++ 11

1. El principio del puntero inteligente

        Un puntero inteligente es una clase que almacena punteros a objetos asignados dinámicamente (montón). Se utiliza para el control de la vida útil y puede garantizar que los objetos asignados dinámicamente se destruyan automática y correctamente al salir del alcance del puntero para evitar pérdidas de memoria. Una de sus técnicas comunes de implementación es usar el conteo de referencias. Cada vez que se usa, el conteo de referencias internas aumenta en 1. Cada vez que se destruye, el conteo de referencias internas disminuye en 1. Cuando se reduce a 0, el se elimina la memoria del montón puntiagudo.

        C++11 proporciona 3 tipos de punteros inteligentes: std::shared_ptr, std::unique_ptr, std::weak_ptr, agregue <memory.h> al usar.

Dos, puntero shared_ptr

        std::shared_ptr utiliza el recuento de referencias, cada copia de shared_ptr apunta a la misma memoria y la memoria no se liberará hasta que se destruya el último shared_ptr.

1. Inicialización

1) Inicializar por constructor

std::shared_ptr<int> p(nuevo int(1));

std::shared_ptr<int> p2 = p;

2) Inicializado por std::make_shared<T>

std::shared_ptr<int> p = std::make_shared<int>(10);

3) Inicializar por método de reinicio

std::shared_ptr<int>ptr;
ptr.reset(nuevo int(1));
if(ptr)
{     std::cout<< "ptr no es nulo"; }

2. Obtenga el puntero original

Cuando necesite obtener el puntero sin procesar, puede usar el método get para obtener el puntero sin procesar

std::shared_ptr<int> ptr(nuevo int(1));
int *p = ptr.get();

3. Especificar el eliminador

La inicialización del puntero inteligente puede especificar un eliminador, de la siguiente manera:

void DeleteInitPtr(int* p)
{     eliminar p; }

std::shared_ptr<int> p(new int, DeleteInitPtr);

Cuando el recuento de referencias de p es 0, se llama automáticamente al eliminador DeleteInitPtr para liberar la memoria del objeto. El eliminador también se puede escribir como una expresión lambda, por lo que se puede reescribir como:

std::shared_ptr<int> p(new int, [](int* p){delete p;});

(1) Cuando std::shared_ptr administra matrices dinámicas, también es necesario especificar el eliminador, ya que std::shared_ptr no admite objetos de matriz de forma predeterminada, el código es el siguiente: std::shared_ptr<int>
p
    (new int[10], [] (int* p){borrar[] p;});

(2) Administrar arreglos a través de std::default_delete como eliminador:
std::shared_ptr<int> p(new int[10], std::default_delete<int[]>);

(3)通过make_shared_array让std::shared_ptr支持数组:
template<typename T>
shared_ptr<T> make_shared_array(size_t size)
{     return std::shared_ptr<int> p(new T(size), std::default_delete<T []>()); }

测试代码:
std::shared_ptr<int> p = make_shared_array<int>(10);
std::shared_ptr<char> p = make_shared_array<char>(10);

4. Problemas que requieren atención al usar shared_ptr

Aunque el puntero inteligente puede administrar automáticamente la memoria del montón, todavía tiene muchos inconvenientes, por lo que debe prestarle atención cuando lo use.

1) No inicialice varios shared_ptr con un puntero sin procesar, por ejemplo, los siguientes son incorrectos:

int *ptr = nuevo int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); error lógico

2) No cree shared_ptr en los argumentos de la función. De la siguiente manera:
function(shared_ptr<int>(new int), g());

Esto se debe a las diferentes convenciones de llamadas de los diferentes compiladores, lo que puede causar pérdidas de memoria.La forma correcta de escribir:
shared_ptr<int> p(new int());
f(p, g());

3) Devuelve el puntero a través de shared_from_this()

No devuelva esto como shared_ptr, porque este puntero es esencialmente un puntero desnudo, por lo que esto puede conducir a una destrucción repetida, de la siguiente manera:

struct A
{     shared_ptr<A>GetSelf()     {             return shared_ptr<S>(this); //no hagas esto     } };




int main()
{     shared_ptr<A> sp1(nueva A);     shared_ptr<A> sp2 = sp1->GetSelf();     devolver 0; }



En este ejemplo, se construyen dos punteros inteligentes sp1 y sp2 con el mismo puntero (this), y no hay relación entre ellos, después de salir del alcance, este será destruido por los dos punteros inteligentes construidos respectivamente
. destructores
Cómo devolver correctamente shared_ptr de esto: deje que la clase de destino derive la clase std::enable_shared_from_this<T> y, a continuación, use la función miembro acumulada shared_from_this para
devolver shared_ptr de esto, de la siguiente manera:
clase A: public std::enable_shared_from_this <A>
{     std: :shared_ptr<A> GetSelf()     {         return shared_froml_this();     } };




std::shared_ptr<A> spy(nueva A);
std::shared_ptr<A> p = espía->GetSelf();

4) Para evitar referencias circulares:
struct A,
struct B;

estructura A
{     std::shared_ptr<B> bptr;     ~A() { cout << "¡A se eliminó!" << finl; }; };


estructura B
{     std::shared_ptr<A> aptr;     ~B() { cout << "¡B ha sido eliminada!" << finl; }; };


void testPtr()
{     std::shared_ptr<A> ap(nueva A);     std::shared_ptr<B> bp(nueva B);     ap->bptr = pb;     pb->aptr = ap; }




El resultado de la prueba es que los dos punteros A y B no se eliminarán y hay una pérdida de memoria. La referencia circular hace que los recuentos de referencia de ap y bp sean 2. Después de abandonar el alcance, los recuentos de referencia se reducen a 1. y no se reducirá a 0, lo que dará como resultado que los dos punteros no se destruyan, lo que provocará una pérdida de memoria. La solución es cambiar el tipo de puntero inteligente de cualquier miembro de A y B a std::weak_ptr.

Tres, puntero unique_ptr

        unique_ptr es un puntero inteligente exclusivo que no permite que otros punteros inteligentes compartan sus punteros internos.

Características:

1. No está permitido asignar un unique_ptr a otro unique_ptr por asignación. como:

unique_ptr<T> myPtr(new T);
unique_ptr<T> myOtherPtr = myPtr; // error, no se puede copiar

2. La propiedad de Unique_ptr se puede transferir a través de std::move

único_ptr<T> myPtr(nueva T);
unique_ptr<T> myOtherPtr = std::move(myPtr);

3. Unique_ptr no puede usar el método make_shared para crear un puntero inteligente. C11 actualmente no tiene un método make_unique

4. Unique_ptr puede apuntar a una matriz, como:

std::unique_ptr<int []> ptr(new int[10]);
ptr[9] = 9; //Establecer el último elemento en 9

5. Unique_ptr especifica el eliminador

Unique_ptr necesita determinar el tipo de eliminador al especificar el eliminador, por lo que no puede especificar directamente el eliminador como shared_ptr, puede escribirlo así:
std::unique_ptr<int, void(*)(int*)> ptr(nwe int( 1), [ ](int*p){delete p;}); Esta forma de escribir es correcta cuando la expresión lambdb no captura la variable, y si captura la variable, el compilador reportará un error.
Si desea que el eliminador de unique_ptr admita expresiones lambda, puede escribir:
std::unique_ptr<int, std::function<void(int*)>> ptr(new int(1), [&](int*p ) {delete p;});
Custom std::unique_ptr deleter:
#include <memoria>
#include <funcional>
usando el espacio de nombres std;

struct MyDeleter
{         void operator()(int*p)         {                 cout<<"delete"<<endl;                 eliminar p;         } };





int main()
{         std::unique_ptr<int, MyDeleter> p(new int(1));         devolver 0; }


Cuatro, puntero de referencia débil débil_ptr

        El puntero de referencia débil débil_ptr se usa para monitorear shared_ptr y no aumentará el recuento de referencias en 1. No administra el puntero interno de shared_ptr, principalmente para monitorear el ciclo de vida de shared_ptr, más como un asistente de shared_ptr. Weak_ptr no tiene operadores sobrecargados * y ->, porque no comparte solo odio, y no puede operar recursos, principalmente a través del derecho de monitoreo de recursos exhalados shared_ptr, su construcción y destrucción no causará el aumento y disminución del recuento de referencia. El propósito es observar la existencia de recursos punteros compartidos.

1. Utilice el método use_count() para obtener el recuento de referencia del recurso de observación actual, de la siguiente manera:

shared_ptr<int> sp(nuevo int(10));
débil_ptr<int> wp(sp);

cout<<wp.use_count()<<endl;

2. Use el método expired() para determinar si el recurso observado ha sido liberado

shared_ptr<int> sp(nuevo int(10));
débil_ptr<int> wp(sp);

if(wp.expired())
{     cout<<"weak_ptr no es válido, ¡el recurso monitoreado ha sido liberado!\n"<<endl; } else {     cout<<"weak_ptr es válido"<<endl; }





3. Obtenga el shared_ptr monitoreado a través del método de bloqueo, de la siguiente manera:

estándar::débil_ptr<int> gw;
void f()
{     if(gw.caducado())     {         std::cout<<"gw ha caducado\n";     }     más     {         auto spt = gw.lock();         estándar::cout <<*spt << "\n";     } }









int main()
{     {         auto sp = std::make_shared_ptr<int>(42);         gw = sp;         f();     }     f(); } El resultado es el siguiente: 42 gw ha caducado









4. débil_ptr devuelve este puntero

Como se mencionó anteriormente, el puntero this no se puede devolver directamente como shared_ptr. Debe devolver el puntero inteligente a través de la clase std::enable_shared_from_this y su método shared_from_this. La razón es que hay un puntero débil_ptr en la clase std::enable_shared_from_this. Este punto débil se usa para observar. Para este puntero inteligente, cuando se llama al método shared_from_this(), se llamará al método lock() del punto débil interno y se devolverá el punto compartido observado.

Revisa el ejemplo anterior:

clase A: public std::enable_shared_from_this<A>
{     std::shared_ptr<A> GetSelf()     {         return shared_froml_this();     } };




std::shared_ptr<A> spy(nueva A);
std::shared_ptr<A> p = espía->GetSelf();

Es seguro crear el puntero inteligente del objeto A externo y devolver el puntero inteligente de este a través del objeto, porque shared_from_this() es el puntero inteligente devuelto después de que débil_ptr llame al método lock().

5. débil_ptr resuelve el problema de referencia circular

estructura A;
estructura B;

estructura A
{     std::shared_ptr<B> bptr;     ~A() { cout << "¡A se eliminó!" << finl; }; };


estructura B
{     std::shared_ptr<A> aptr;     ~B() { cout << "¡B ha sido eliminada!" << finl; }; };


void testPtr()
{     std::shared_ptr<A> ap(nueva A);     std::shared_ptr<B> bp(nueva B);     ap->bptr = pb;     pb->aptr = ap; }




En el ejemplo anterior, debido a las referencias circulares, los recuentos de referencia de ap y bp son ambos 2. Después de abandonar el alcance, los recuentos de referencia se reducen a 1 y el puntero no se eliminará, lo que provocará pérdidas de memoria. Este problema se puede resolver con débil_ptr, simplemente cambie cualquier miembro de A o B a débil_ptr.

estructura A;
estructura B;

estructura A
{     std::shared_ptr<B> bptr;     ~A() { cout << "¡A se eliminó!" << finl; }; };


estructura B
{     std::weak_ptr<A> aptr;     ~B() { cout << "¡B ha sido eliminada!" << finl; }; };


void testPtr()
{     std::shared_ptr<A> ap(nueva A);     std::shared_ptr<B> bp(nueva B);     ap->bptr = pb;     pb->aptr = ap; }




De esta forma, al asignar un valor a un miembro de B, es decir, al ejecutar bp->aptr=ap;, dado que aptr es débil_ptr, no aumentará la cuenta de referencia, por lo que la cuenta de referencia de ap seguirá siendo 1 Después de dejar el alcance, ap El recuento de referencia del objeto se reducirá a 0 y se destruirá el puntero A. Después de la destrucción, el recuento de referencia del bptr dentro de él se reducirá a 1, y luego la referencia bp el conteo se reducirá de 1 a 0 después de abandonar el alcance, y el objeto B también se destruirá, no se producirán pérdidas de memoria.

Resumen: Los punteros inteligentes son una herramienta poderosa para resolver posibles problemas de pérdida de memoria para lenguajes sin mecanismos de recolección de basura, pero hay algunos puntos a los que se debe prestar atención en el uso real. Afortunadamente, estos problemas se pueden resolver.

1) Cómo elegir entre shared_ptr y unique_ptr: si desea que solo un puntero administre recursos o matrices, puede usar unique_ptr; si desea que varios punteros inteligentes administren el mismo recurso, puede usar shared_ptr.

2) débil_ptr es un asistente de shared_ptr, solo supervisa si los recursos administrados por shared_ptr se liberan y no opera ni administra los recursos por sí mismo. Se utiliza para resolver el problema de referencia circular de shared_ptr y el problema de devolver este puntero.

Supongo que te gusta

Origin blog.csdn.net/leiyang2014/article/details/128370105
Recomendado
Clasificación