Puntero inteligente - C++

1. ¿Por qué necesita punteros inteligentes?

Analicemos primero si el siguiente programa tiene algún problema de memoria.
Un recordatorio: preste atención al análisis de los problemas en la función MergeSort:

int div()
{
    
    
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");
    return a / b;
}
void Func()
{
    
    
    // 1、如果p1这里new 抛异常会如何?
    // 2、如果p2这里new 抛异常会如何?
    // 3、如果div调用这里又会抛异常会如何?
    int* p1 = new int;
    int* p2 = new int;
    cout << div() << endl;
    delete p1;
    delete p2;
 cout << "释放资源" << endl;
}
int main()
{
    
    
    try
    {
    
    
        Func();
    }
    catch (exception& e)
    {
    
    
        cout << e.what() << endl;
    }
    return 0;
}
  1. En términos generales, no hay problema con new y delete, pero si se lanza una excepción aquí, la llamada se cruzará y no se liberará.
    Saltar directamente de lanzamiento a atrapar atrapar
  2. Problema mayor: cuando se deben lanzar varias excepciones consecutivas, se deben liberar algunos recursos al frente
    inserte la descripción de la imagen aquí
    inserte la descripción de la imagen aquí

Como se muestra en la figura, no hay liberación en este momento

 

2. Pérdida de memoria

2.1 ¿Qué es una fuga de memoria y el daño de una fuga de memoria?

什么是内存泄漏:
Pérdida de memoria se refiere a la situación en la que el programa no puede liberar la memoria que ya no se usa debido a una negligencia o error. Una fuga de memoria no se refiere a la desaparición física de la memoria, sino a la pérdida de control sobre un determinado segmento de memoria debido a un error de diseño después de que la aplicación asigna un determinado segmento de memoria, lo que resulta en una pérdida de memoria.

内存泄漏的危害:
Las fugas de memoria ocurren en programas de ejecución prolongada, que tienen un gran impacto, como sistemas operativos, servicios en segundo plano, etc. Las fugas de memoria darán lugar a respuestas cada vez más lentas y, finalmente, se congelarán.

void MemoryLeaks()
{
    
    
// 1.内存申请了忘记释放
int* p1 = (int*)malloc(sizeof(int));
int* p2 = new int;
// 2.异常安全问题
int* p3 = new int[10];
Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
delete[] p3;
}

内存泄漏是指针丢了还是内存丢了?
¡Se ha perdido el puntero! Si el puntero todavía está allí, aún se puede liberar la memoria. La memoria todavía está allí y se liberará después de que el proceso finalice normalmente
1. Los procesos zombis tienen pérdidas de memoria
2. Programas de ejecución prolongada

 

2.2 Clasificación de las fugas de memoria (comprensión)

En los programas C/C++, generalmente nos preocupamos por dos aspectos de las fugas de memoria:

  • Fuga de memoria del montón (Heap leak)
    La memoria del montón se refiere a una parte de la memoria asignada desde el montón a través de malloc / calloc / realloc / new, etc. durante la ejecución del programa según sea necesario, y debe eliminarse llamando al correspondiente free o delete after use. Suponiendo que el error de diseño del programa hace que esta parte de la memoria no se libere, esta parte del espacio ya no se utilizará en el futuro y se producirá una fuga de almacenamiento.
  • La fuga de recursos del sistema
    se refiere a los recursos asignados por el sistema utilizado por el programa, como sockets, descriptores de archivos, conductos, etc., que no se liberan mediante las funciones correspondientes, lo que genera un desperdicio de recursos del sistema, lo que puede reducir seriamente el rendimiento del sistema. y causar una ejecución inestable del sistema.

 

2.3 Cómo evitar pérdidas de memoria

  1. Buenas especificaciones de diseño en la etapa inicial del proyecto, desarrolle buenos estándares de codificación y recuerde liberar el espacio de memoria que coincida. pd: Este estado ideal. Pero si encuentra una excepción, incluso si presta atención a la liberación, aún puede haber problemas. Debe ser administrado por el próximo puntero inteligente para estar garantizado.
  2. Use ideas RAII o punteros inteligentes para administrar recursos
  3. Algunas especificaciones internas de la empresa utilizan bibliotecas de administración de memoria privada implementadas internamente. Esta biblioteca viene con opciones para la detección de fugas de memoria.
  4. Algo salió mal usando una herramienta de pérdida de memoria para detectar. pd: Sin embargo, muchas herramientas no son lo suficientemente confiables o las tarifas son caras.

检测工具内部原理:
La memoria de la aplicación se registra en un contenedor
. Cuando se libera la memoria, se elimina del contenedor.
Antes de que finalice el programa o cuando no hay ninguna tarea en ejecución, los recursos en el contenedor pueden tener pérdidas de memoria.

总结一下:
Las fugas de memoria son muy comunes y existen dos soluciones:
1. Tipo de prevención previa. Como punteros inteligentes, etc.
2. Tipo de comprobación de errores posterior al evento. Como herramientas de detección de fugas.

 

 

3. El uso y principio de los punteros inteligentes

3.1 RAII

——获取资源就马上初始化

RAII (Adquisición de recursos es inicialización) es una técnica simple que utiliza el ciclo de vida del objeto para controlar los recursos del programa (como la memoria, los identificadores de archivos, las conexiones de red, los mutex, etc.).
Adquiera recursos cuando se construye el objeto, luego controle el acceso a los recursos para que sigan siendo válidos durante la vida útil del objeto y, finalmente, libere los recursos cuando se destruya el objeto. De esta manera, en realidad confiamos la responsabilidad de administrar un recurso a un objeto.

Este enfoque tiene dos beneficios principales:
1. No hay necesidad de liberar recursos explícitamente.
2. De esta manera, los recursos requeridos por el objeto siempre se mantienen válidos durante su vida útil para utilizar el ciclo de vida de otras clases.

Use punteros inteligentes para resolver el problema anterior de pérdida de memoria:

//利用RAII思想设计delete资源的类
template<class T>
class SmartPtr
{
    
    
public:
    SmartPtr(T* ptr)
        :_ptr(ptr)
    {
    
    }


       ~SmartPtr()
       {
    
    
           cout << "delete:" << _ptr << endl;
           delete _ptr;
       }
private:
    T* _ptr;
};

int div()
{
    
    
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");
    return a / b;
}


void Func()
{
    
    
    // 1、如果p1这里new 抛异常会如何?
    // 2、如果p2这里new 抛异常会如何?
    // 3、如果div调用这里又会抛异常会如何?
    SmartPtr<int> sp1(new int);
    SmartPtr<int> sp2(new int);
    cout << div() << endl;
    
    cout << "释放资源" << endl;
}
int main()
{
    
    
    try
    {
    
    
        Func();
    }
    catch (exception& e)
    {
    
    
        cout << e.what() << endl;
    }
    return 0;
}

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

Así que parece que ambos liberan recursos.
Aquí, no importa si la función normal termina o se lanza una excepción, sp1 y sp2 llamarán al destructor para liberar recursos, lo que resuelve muy bien el problema.

 

Lo anterior es solo el uso básico de los punteros inteligentes.
Hay otros usos:

3.2 El principio del puntero inteligente

El SmartPtr antes mencionado aún no puede llamarse puntero inteligente, porque aún no tiene el comportamiento de un puntero. El puntero se puede desreferenciar y también puede usar -> para acceder al contenido en el espacio apuntado. Por lo tanto: en la clase de plantilla AutoPtr, debe sobrecargar * y -> para que funcione como un puntero.

// 2、像指针一样的行为——重载运算符
template<class T>
class SmartPtr
{
    
    
public:
    SmartPtr(T* ptr)
        :_ptr(ptr)
    {
    
    }

    ~SmartPtr()
    {
    
    
        cout << "delete:" << _ptr << endl;
        delete _ptr;
    }

    T& operator*()
    {
    
    
        return *_ptr;
    }

    T* operator->()
    {
    
    
        return _ptr;
    }

private:
    T* _ptr;
};

Resuma los principios de los punteros inteligentes:
1. Utilice el pensamiento RAII para diseñar clases de recursos de eliminación
2. Comportamiento como punteros: operadores sobrecargados
3. Problema de copia

 

3.3 estándar::auto_ptr

El puntero inteligente de auto_ptr se proporciona en la versión C++98 de la biblioteca. El uso y los problemas de auto_ptr se muestran a continuación. auto_ptr的实现原理: 管理权转移La idea, la siguiente simulación simplificada implementa un bit::auto_ptr para entender su principio

SmartPtr<A> sp1(new A);
    sp1->_a1++;
    sp1->_a2++;

    //致命问题:拷贝问题——直接报错
SmartPtr<A> sp2(sp1);

 

sp2 necesita llamar a la construcción de copia, pero no escribimos la construcción de copia, el compilador genera una copia superficial predeterminada del tipo incorporado, construcción de copia de tipo personalizado
Pero aquí hay solo un tipo incorporado:

private:
	T* _ptr;

En este momento, la copia superficial apunta al mismo espacio y el destructor se destruirá dos veces. ¿Cómo resolver?
Solución: ¿
solución de copia profunda? No, viola los requisitos funcionales; lo que se necesita es una copia superficial

 

Esta situación es muy similar a la de los iteradores, pero ¿por qué no hay problema con las copias superficiales de los iteradores?

list<int> lt;
auto it = lt.begin();

El iterador no administra la liberación de recursos, sino que solo accede, atraviesa y modifica los datos de manera unificada. La liberación es manejada por el destructor de la lista enlazada.

La copia de Curry no es problema:

auto_ptr<A> ap1(new A);//出了作用域自己会调析构函数
    ap1->_a1++;
    ap1->_a2++;

auto_ptr<A> ap2(ap1);

¿Cómo lo resolviste?
管理权转移——不负责的拷贝

El verdadero problema con auto_ptr:

auto_ptr<A> ap1(new A);//出了作用域自己会调析构函数
    ap1->_a1++;
    ap1->_a2++;

auto_ptr<A> ap2(ap1);
        ap1->_a1++;
    ap1->_a2++;
        ap2->_a1++;
    ap2->_a2++;

Se garantiza que se liberará cuando no se acceda a ap1:
inserte la descripción de la imagen aquí

Pero si vuelve a eliminar la referencia, se producirá un problema de puntero nulo y el objeto copiado quedará vacío.

inserte la descripción de la imagen aquí

 

class A
{
    
    
public:
    ~A()
    {
    
    
        cout << "~A()" << endl;
    }
    //private:
    int _a1 = 0;
    int _a2 = 0;
};

template<class T>
class SmartPtr {
    
    
public:
    SmartPtr(T* ptr = nullptr)
        : _ptr(ptr)
    {
    
    }

    ~SmartPtr()
    {
    
    
        if (_ptr)
        {
    
    
            cout << "Delete:" << _ptr << endl;
            delete _ptr;
        }
    }

    T& operator*()
    {
    
    
        return *_ptr;
    }

    T* operator->()
    {
    
    
        return _ptr;
    }

private:
    T* _ptr;
};

namespace haha
{
    
    
    // C++98  auto_ptr 管理权转移,被拷贝对象的出现悬空问题
    // 很多公司是明确的要求了不能使用它
    template<class T>
    class auto_ptr
    {
    
    
    public:
        auto_ptr(T* ptr = nullptr)
            : _ptr(ptr)
        {
    
    }

        auto_ptr(auto_ptr<T>& ap)
            :_ptr(ap._ptr)
        {
    
    
            ap._ptr = nullptr;
        }

        // ap1 = ap2;
        auto_ptr<T>& operator=(auto_ptr<T>& ap)
        {
    
    
            if (this != &ap)
            {
    
    
                if (_ptr)
                {
    
    
                    cout << "Delete:" << _ptr << endl;
                    delete _ptr;
                }

                _ptr = ap._ptr;
                ap._ptr = nullptr;
            }

            return *this;
        }

        ~auto_ptr()
        {
    
    
            if (_ptr)
            {
    
    
                cout << "Delete:" << _ptr << endl;
                delete _ptr;
            }
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }

    private:
        T* _ptr;
    };

void test_auto_ptr()
{
    
    
    std::auto_ptr<A> ap1(new A);
    ap1->_a1++;
    ap1->_a2++;

    std::auto_ptr<A> ap2(ap1);
    ap1->_a1++;
    ap1->_a2++;
    ap2->_a1++;
    ap2->_a2++;

    // 2 2
    cout << ap2->_a1 << endl;
    cout << ap2->_a2 << endl;

    std::auto_ptr<A> ap3(new A);
    ap2 = ap3;

    ap2->_a1++;
    ap2->_a2++;

    cout << ap2->_a1 << endl;
    cout << ap2->_a2 << endl;
}

inserte la descripción de la imagen aquí
 

namespace haha
{
    
    
    
    template<class T>
    class auto_ptr
    {
    
    
    public:
        auto_ptr(T* ptr = nullptr)
            : _ptr(ptr)
        {
    
    }

        auto_ptr(auto_ptr<T>& ap)
            :_ptr(ap._ptr)
        {
    
    
            ap._ptr = nullptr;
        }

        // ap1 = ap2;
        auto_ptr<T>& operator=(auto_ptr<T>& ap)
        {
    
    
            if (this != &ap)
            {
    
    
                if (_ptr)
                {
    
    
                    cout << "Delete:" << _ptr << endl;
                    delete _ptr;
                }

                _ptr = ap._ptr;
                ap._ptr = nullptr;
            }

            return *this;
        }

        ~auto_ptr()
        {
    
    
            if (_ptr)
            {
    
    
                cout << "Delete:" << _ptr << endl;
                delete _ptr;
            }
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }

    private:
        T* _ptr;
    };
}

void test_auto_ptr()
{
    
    
    haha::auto_ptr<A> ap1(new A);
    ap1->_a1++;
    ap1->_a2++;

    haha::auto_ptr<A> ap2(ap1);
    //ap1->_a1++;
    //ap1->_a2++;
    ap2->_a1++;
    ap2->_a2++;

    // 2 2
    cout << ap2->_a1 << endl;
    cout << ap2->_a2 << endl;

    haha::auto_ptr<A> ap3(new A);
    ap2 = ap3;

    ap2->_a1++;
    ap2->_a2++;

    cout << ap2->_a1 << endl;
    cout << ap2->_a2 << endl;
}

inserte la descripción de la imagen aquí

 
 

3.4 estándar::único_ptr

Puntero único
Ideas de diseño: simple y grosero, sin copiar

    // C++11库才更新智能指针实现
// C++11出来之前,boost搞除了更好用的scoped_ptr/shared_ptr/weak_ptr
// C++11将boost库中智能指针精华部分吸收了过来
// C++11->unique_ptr/shared_ptr/weak_ptr
// unique_ptr/scoped_ptr
// 原理:简单粗暴 -- 防拷贝
 template<class T>
    class unique_ptr
    {
    
    
    private:
        // 防拷贝 C++98
        // 只声明不实现 —— 声明成私有
        //unique_ptr(unique_ptr<T>& ap);
        //unique_ptr<T>& operator=(unique_ptr<T>& ap);
    public:
        unique_ptr(T* ptr = nullptr)
            : _ptr(ptr)
        {
    
    }

        // 防拷贝 C++11
        unique_ptr(unique_ptr<T>& ap) = delete;
        unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;

        ~unique_ptr()
        {
    
    
            if (_ptr)
            {
    
    
                cout << "Delete:" << _ptr << endl;
                delete _ptr;
            }
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }

    private:
        T* _ptr;
    };
}
    
// unique_ptr:简单粗暴,不让拷贝 只适用于不需要拷贝一些场景
void test_unique_ptr()
{
    
    
    haha::unique_ptr<A> up1(new A);
    //bit::unique_ptr<A> up2(up1);
    up1->_a1++;
    up1->_a2++;

    haha::unique_ptr<A> up3(new A);
    //up1 = up2;
}

inserte la descripción de la imagen aquí

Solo es
adecuado para algunos escenarios que no necesitan ser copiados

 
 

3.5 estándar::shared_ptr

C ++ 11 comenzó a proporcionar un shared_ptr más confiable que admite la copia

El principio de shared_ptr es compartir recursos entre varios objetos shared_ptr mediante el recuento de referencias.

  1. shared_ptr mantiene un recuento de cada recurso que contiene, que se utiliza para registrar que el recurso es compartido por varios objetos.
  2. Cuando se destruye el objeto (es decir, se llama al destructor), significa que el recurso ya no se usa y el recuento de referencias del objeto se reduce en uno.
  3. Si el conteo de referencia es 0, significa que es el último objeto en usar el recurso y debe liberar el recurso;
  4. Si no es 0, significa que hay otros objetos que usan este recurso además de usted, y el recurso no se puede liberar, de lo contrario, otros objetos se convertirán en punteros salvajes.

Los siguientes son los escenarios que deben copiarse

// shared_ptr 需要拷贝的场景
void test_shared_ptr1()
{
    
    
    std::shared_ptr<A> sp1(new A);
    std::shared_ptr<A> sp2(sp1);
    std::shared_ptr<A> sp3(sp1);

    sp1->_a1++;
    sp1->_a2++;
    cout << sp2->_a1 << ":" << sp2->_a2 << endl;
    sp2->_a1++;
    sp2->_a2++;
    cout << sp1->_a1 << ":" << sp1->_a2 << endl;

    std::shared_ptr<A> sp5(new A);
    std::shared_ptr<A> sp6(sp5);

    sp1 = sp5;
    sp2 = sp5;
    sp3 = sp5;

    // 自己给自己赋值
    std::shared_ptr<int> sp4(new int);
    sp4 = sp4;
    sp1 = sp5;
}

inserte la descripción de la imagen aquí

La idea factible de la gestión conjunta es:
cuando se libera cada objeto, – contar
el último objeto destruido y liberar recursos

 
 

//这种写法是不可行的
    //template<class T>
    //class shared_ptr
    //{
    
    
    //public:
    //    shared_ptr(T* ptr = nullptr)
    //        : _ptr(ptr)
    //    {
    
    
    //        ++_count;
    //    }

    //    ~shared_ptr()
    //    {
    
    
    //        if (--_count == 0 && _ptr)
    //        {
    
    
    //            cout << "Delete:" << _ptr << endl;
    //            delete _ptr;
    //        }
    //    }

    //    shared_ptr(shared_ptr<T>& sp)
    //        : _ptr(sp._ptr)
    //    {
    
    
    //        ++_count;
    //    }

    //    T& operator*()
    //    {
    
    
    //        return *_ptr;
    //    }

    //    T* operator->()
    //    {
    
    
    //        return _ptr;
    //    }

    //private:
    //    T* _ptr;

    //    static int _count; // 引用计数
    //};

    //template<class T>
    //int shared_ptr<T>::_count = 0;
template<class T>
    class shared_ptr
    {
    
    
    public:
        shared_ptr(T* ptr = nullptr)
            : _ptr(ptr)
            , _pCount(new int(1))
        {
    
    }

        void Release()
        {
    
    
            if (--(*_pCount) == 0)
            {
    
    
                cout << "Delete:" << _ptr << endl;
                delete _ptr;
                delete _pCount;
            }
        }

        ~shared_ptr()
        {
    
    
            Release();
        }

        // sp1(sp2)
        shared_ptr(const shared_ptr<T>& sp)
            : _ptr(sp._ptr)
            , _pCount(sp._pCount)
        {
    
    
            (*_pCount)++;
        }

        // sp1 = sp5
        // sp1 = sp1
        
        shared_ptr<T>& operator=(const shared_ptr<T>& sp)
        {
    
    
            //if (this == &sp)
            if (_ptr == sp._ptr)
            {
    
    
                return *this;
            }

            // 减减被赋值对象的计数,如果是最后一个对象,要释放资源
            /*if (--(*_pCount) == 0)
            {
            delete _ptr;
            delete _pCount;
            }*/
            Release();

            // 共管新资源,++计数
            _ptr = sp._ptr;
            _pCount = sp._pCount;

            (*_pCount)++;

            return *this;
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }

    private:
        T* _ptr;

        // 引用计数
        int* _pCount;
    };
 
 
 // shared_ptr 需要拷贝的场景
void test_shared_ptr1()
{
    
    
    haha::shared_ptr<A> sp1(new A);
    haha::shared_ptr<A> sp2(sp1);
    haha::shared_ptr<A> sp3(sp1);

    sp1->_a1++;
    sp1->_a2++;
    cout << sp2->_a1 << ":" << sp2->_a2 << endl;
    sp2->_a1++;
    sp2->_a2++;
    cout << sp1->_a1 << ":" << sp1->_a2 << endl;

    haha::shared_ptr<A> sp5(new A);
    haha::shared_ptr<A> sp6(sp5);

    sp1 = sp5;
    sp2 = sp5;
    sp3 = sp5;

    // 自己给自己赋值
    haha::shared_ptr<int> sp4(new int);
    sp4 = sp4;
    sp1 = sp5;
}

inserte la descripción de la imagen aquí

No se permiten objetos de conteo estáticos.
Un recurso se asigna con un conteo. Varios objetos de puntero inteligente administran objetos de conteo estáticos. Todos los recursos tienen solo un conteo, porque los miembros estáticos pertenecen a toda la clase y todos los objetos que pertenecen a la clase deben ser cuando cada recurso necesita ser administrado
Para el constructor, construya un nuevo recuento.

3.5.1 Análisis de referencia circular:

  1. Los dos objetos de puntero inteligente, nodo1 y nodo2, apuntan a dos nodos, el recuento de referencias se convierte en 1 y no es necesario eliminarlos manualmente
    .
  2. El _next del nodo1 apunta al nodo2, el _prev del nodo2 apunta al nodo1 y el recuento de referencias se convierte en 2.
  3. El nodo 1 y el nodo 2 se destruyen y el recuento de referencias se reduce a 1, pero _next sigue apuntando al siguiente nodo. Pero _prev todavía apunta al
    nodo anterior.
  4. Es decir, se destruye _next y se libera el nodo2.
  5. Es decir, se destruye _prev y se libera el nodo1.
  6. Pero _next es miembro del nodo, el nodo1 se libera, _next se destruirá y el nodo1 es administrado por _prev, _prev es miembro del nodo2, por lo que se denomina referencia circular y nadie la liberará.

循环引用:

struct Node
{
    
    
    int _val;
    std::shared_ptr<Node> _next;
    std::shared_ptr<Node> _prev;



    ~Node()
    {
    
    
        cout << "~Node" << endl;
    }
};


void test_shared_ptr2()
{
    
    
    std::shared_ptr<Node> n1(new Node);
    std::shared_ptr<Node> n2(new Node);

    n1->_next = n2;//不能赋值过去,自定义对象不能赋值给原生指针
    n2->_prev = n1;

    
}

Después de que finalice esta función, primero se destruirá n2 y luego se destruirá n1
_next administra el bloque de memoria del nodo a la derecha
_prev administra el bloque de memoria del nodo a la izquierda

_next se destruye, el nodo derecho se elimina, ¿cuándo se lanzará _next? El
nodo izquierdo se elimina, se llama al destructor y _next se destruirá como miembro

_prev se destruye, el nodo izquierdo se elimina, ¿cuándo se liberará _prev? El
nodo derecho se elimina, se llama al destructor y _prev se destruirá como miembro

 
 

解决方法:

struct Node
{
    
    
    int _val;    

std::weak_ptr<Node> _next;
std::weak_ptr<Node> _prev;

    ~Node()
    {
    
    
        cout << "~Node" << endl;
    }
};

// 循环引用 -- weak_ptr不是常规智能指针,没有RAII,不支持直接管理资源
// weak_ptr主要用shared_ptr构造,用来解决shared_ptr循环引用问题
void test_shared_ptr2()
{
    
    
    

    std::shared_ptr<Node> n1(new Node);//不支持隐式类型转换
    std::shared_ptr<Node> n2(new Node);

    cout << n1.use_count() << endl;
    cout << n2.use_count() << endl;

    n1->_next = n2;
    n2->_prev = n1;

    cout << n1.use_count() << endl;
    cout << n2.use_count() << endl;
}

inserte la descripción de la imagen aquí

特点:
Cuando _next y _prev son débil_ptr, no participa en la gestión de liberación de recursos, puede acceder y modificar recursos, pero no aumenta la cuenta, por lo que no hay problema de referencias circulares.

 

模拟实现weak_ptr

   //辅助型智能指针,配合解决shared_ptr的循环引用问题
    template<class T>
    class weak_ptr
    {
    
    
    public:
        weak_ptr()
            :_ptr(nullptr)
        {
    
    }

        weak_ptr(const shared_ptr<T>& sp)
            :_ptr(sp.get())
        {
    
    }

        weak_ptr(const weak_ptr<T>& wp)
            :_ptr(wp._ptr)
        {
    
    }

        weak_ptr<T>& operator=(const shared_ptr<T>& sp)
        {
    
    
            _ptr = sp.get();
            return *this;
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }
    public:
        T* _ptr;
    };

 
 

Si no es un objeto nuevo, ¿cómo puede ser manejado por un puntero inteligente? De hecho, shared_ptr diseñó un eliminador para resolver este problema.
El tipo incorporado new[] no tiene ningún problema, y ​​el tipo personalizado informará un error.

template<class T>
struct DeleteArray
{
    
    
    void operator()(T* ptr)
    {
    
    
        cout << "delete[]" << ptr << endl;
        delete[] ptr;
    }
};

template<class T>
struct Free
{
    
    
    void operator()(T* ptr)
    {
    
    
        cout << "free" << ptr << endl;
        free(ptr);
    }
};

// 定制删除器
//void test_shared_ptr3()
//{
    
    
//    // 仿函数对象
//    /*std::shared_ptr<Node> n1(new Node[5], DeleteArray<Node>());
//    std::shared_ptr<Node> n2(new Node);
//
//    std::shared_ptr<int> n3(new int[5], DeleteArray<int>());
//
//    std::shared_ptr<int> n4((int*)malloc(sizeof(12)), Free<int>());*/
//
//    // lambda
//    //std::shared_ptr<Node> n1(new Node[5], [](Node* ptr){delete[] ptr; });
//    //std::shared_ptr<Node> n2(new Node);
//
//    //std::shared_ptr<int> n3(new int[5], [](int* ptr){delete[] ptr; });
//
//    //std::shared_ptr<int> n4((int*)malloc(sizeof(12)), [](int* ptr){free(ptr); });
//    //std::shared_ptr<FILE> n5(fopen("test.txt", "w"), [](FILE* ptr){fclose(ptr); });
//
//    //std::unique_ptr<Node, DeleteArray<Node>> up(new Node[5]);
//}

void test_shared_ptr3()
{
    
    
    haha::shared_ptr<Node, DeleteArray<Node>> n1(new Node[5]);
    haha::shared_ptr<Node> n2(new Node);
    haha::shared_ptr<int, DeleteArray<int>> n3(new int[5]);
    haha::shared_ptr<int, Free<int>> n4((int*)malloc(sizeof(12)));
}

 
 

4. La relación entre los punteros inteligentes en C++11 y boost

  1. El primer puntero inteligente auto_ptr se creó en C++98.
  2. C++ boost proporciona scoped_ptr, shared_ptr y débil_ptr más prácticos.
  3. C++ TR1, introdujo shared_ptr, etc. Pero tenga en cuenta que TR1 no es una versión estándar.
  4. C ++ 11, introdujo unique_ptr y shared_ptr y débil_ptr. Cabe señalar que unique_ptr corresponde a scoped_ptr de boost. Y los principios de implementación de estos punteros inteligentes se refieren a la implementación en impulso.

Supongo que te gusta

Origin blog.csdn.net/Ll_R_lL/article/details/128905227
Recomendado
Clasificación