首页 > 其他分享 >智能指针详解

智能指针详解

时间:2024-04-06 18:03:11浏览次数:25  
标签:count weak 智能 详解 shared include ptr 指针

目录

自己管理指针会有什么潜在的问题

智能指针的作用

share_ptr(共享智能指针)

初始化的方式

shared_ptr 获取原始指针

weak_ptr (弱引用智能指针)

初始化的方式

use_count()

expired()

lock()

reset()

返回管理this的shared_ptr

循环引用问题

图释循环引用:

​编辑

解决方法:

unique_ptr(独占智能指针)

初始化

reset()

shared_ptr的实现

实现1:

实现2:


自己管理指针会有什么潜在的问题

  1. 内存泄漏:

    最常见的问题就是内存泄漏,如果动态分配的内存没有被正确的释放,就是说没有被正确的delete操作就会导致内存泄漏。会使程序消耗的内存越来越多,最终导致程序的崩溃和性能的下降。

  2. 悬挂指针:

    在手动管理内存的时候会出现悬挂指针的问题,就是指向的内存已经被释放,但是指针的本身仍然保留,当我们再次访问这个指针的时候就会导致未定义的行为,可能会引发程序的崩溃。

  3. 二次释放:

    如果同一个内存地址被释放两次(二次释放),会导致严重的运行时错误,破坏堆的内部结构,也可能导致程序崩溃。

  4. 资源泄漏:

    除了内存泄漏外,还可能存在其他资源泄漏,比如文件句柄、数据库连接等资源没有正确释放,导致系统资源的浪费。

智能指针的作用

C++11引入了智能指针存储指向动态分配对象指针的类,

用于生存期的控制,能够确保在离开指针所在作用域时,自动的销毁分配的对象,防止内存泄漏。智能指针通过封装指针、使用 RAII(资源获取即初始化)的原则,以及引用计数等机制来实现自动化的内存管理。

智能指针的核心实现技术是引用技术,每使用它一次内部引用计数加1,每析构一次内部引用计数减1,减为0时,删除原始指针指向的堆区内存,使用智能指针需要引用头文件

RAII的原理

RAII(Resource Acquisition Is Initialization)是一种C++编程中的重要原则,它基于栈对象的生命周期来管理资源的获取和释放。在RAII中,资源的获取和释放被绑定到对象的生命周期,当对象被创建时获取资源,当对象离开作用域时自动释放资源,从而确保资源的正确释放,避免资源泄漏。

share_ptr(共享智能指针)

共享智能指针允许多个智能指针共享同一资源,通过引用计数来管理资源的生命周期,适用于需要共享资源的场景。

共享智能指针对象初始化完毕之后就指向了要管理的堆区内存,可以使用共享智能指针的成员函数 use_count 来查看当前有多少指针共同管理这这块内存

初始化的方式

  • 通过构造函数初始化

    #include<iostream>
    #include<memory>
    using namespace std;
    
    int main() {
        // 使用智能指针管理一块 int 型的堆内存
        shared_ptr<int> ptr1(new int(520));
        cout << "ptr1管理的内存引用计数:" << ptr1.use_count() << endl;
        //使用智能指针管理一块字符数组对应的堆内存
        shared_ptr<char> ptr2(new char[520]);
        cout << "ptr2管理的内存引用计数:" << ptr2.use_count() << endl;
        shared_ptr<int> ptr3;
        cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl;
        // 创建智能指针对象, 初始化为空
        shared_ptr<int> ptr4(nullptr);
        cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl;
    
        /*打印结果如下:
            ptr1管理的内存引用计数 : 1
            ptr2管理的内存引用计数 : 1
            ptr3管理的内存引用计数 : 0
            ptr4管理的内存引用计数 : 0*/
    return 0;
    }

从上面的程序我们能看出来,如果智能指针初始化了一块有效的内存,那么这块内存的引用计数+1,而如果没有被初始化或者是被初始化为一个空的指针,那么这个智能指针的引用计数还是0。

注意

不要用一个原始指针初始化多个shared_ptr

这是因为,shared_ptr通过引用计数来管理资源,多个shared_ptr实例共享相同的资源,并且会增加引用计数。但是多个shared_ptr实例指向同一个原始指针,这些shared_ptr指针是不知道他们的其他shared_ptr共享共同的指针。这样既可能出现悬挂指针的问题和指针的多次释放问题。

可以使用下面的方法,共享同一个指针,下面的sharedPtr1 和 sharedPtr2 共享相同的引用计数和资源,不会出现引用计数管理不当的问题。

std::shared_ptr sharedPtr1 = std::make_shared(10);

std::shared_ptr sharedPtr2 = sharedPtr1; // 这是安全的,因为它们共享相同的源
  • 拷贝构造、移动构造初始化
  • #include<iostream>
    #include<memory>
    using namespace std; 
    int main() {
    //构造函数
    shared_ptr ptr1(new int(520));
    cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl; 
    //拷贝构造函数
    shared_ptr ptr2(ptr1);
    cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl;
    shared_ptr ptr3 = ptr1;
    cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl; 
    //移动构造函数
    shared_ptr ptr4(std::move(ptr1));
    cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl;
    std::shared_ptr ptr5 = std::move(ptr2);
    cout << "ptr5管理的内存引用计数: " << ptr5.use_count() << endl;
    /*打印结果如下:
    ptr1管理的内存引用计数 : 1
    ptr2管理的内存引用计数 : 2
    ptr3管理的内存引用计数 : 3
    ptr4管理的内存引用计数 : 3
    ptr5管理的内存引用计数 : 3*/ 
    return 0;
    }

    拷贝构造函数和赋值构造都会增加引用计数,这都是多个shared_ptr共享相同的资源。

    但是移动构造就是会将资源的所有权从一个shared_ptr转移到另一个,被移动的shared_ptr会指向空,所以引用计数不增加。

  • std::make_shared辅助函数

    通过c++11提供的std::make_shared()函数就可以完成内存对象的创建并将其初始化给智能指针

    #include <iostream>
    #include <string>
    #include <memory>
    using namespace std;
    
    class Test
    {
    public:
        Test()
        {
            cout << "无参构造函数" << endl;
        }
        Test(int x)
        {
            cout << "int类型构造函数 " << x << endl;
        }
        Test(string str)
        {
            cout << "string类型的构造函数" << str << endl;
        }
        ~Test()
        {
            cout << "析构函数" << endl;
        }
    };
    
    int main()
    {
        // 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1
        shared_ptr<int> ptr1 = make_shared<int>(520);
        cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl;
    
        shared_ptr<Test> ptr2 = make_shared<Test>();
        cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl;
    
        shared_ptr<Test> ptr3 = make_shared<Test>(520);
        cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl;
    
        shared_ptr<Test> ptr4 = make_shared<Test>("QQQQ");
        cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl;
        return 0;
    }
    打印结果如下:
            ptr1管理的内存引用计数: 1
            无参构造函数
            ptr2管理的内存引用计数: 1
            int类型构造函数 520
            ptr3管理的内存引用计数: 1
            string类型的构造函数QQQQ
            ptr4管理的内存引用计数: 1
            析构函数
            析构函数
            析构函数

make_shared()函数原型

std::shared_ptr<T> make_shared< T >( Args&&... args );

T 表示指向的类型,Args 是类型 T 的构造函数所需的参数列表,在调用时需要传递给构造函数。

  • reset方法
#include <iostream>
#include <string>
#include <memory>
using namespace std;

int main()
{
    // 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1
    shared_ptr<int> ptr1 = make_shared<int>(520);
    shared_ptr<int> ptr2 = ptr1;
    shared_ptr<int> ptr3 = ptr1;
    shared_ptr<int> ptr4 = ptr1;
    cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl;
    cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl;
    cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl;
    cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl;

    ptr4.reset();
    cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl;
    cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl;
    cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl;
    cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl;

    shared_ptr<int> ptr5;
    ptr5.reset(new int(250));
    cout << "ptr5管理的内存引用计数: " << ptr5.use_count() << endl;
    return 0;
}
打印结果如下:
    ptr1管理的内存引用计数: 4
    ptr2管理的内存引用计数: 4
    ptr3管理的内存引用计数: 4
    ptr4管理的内存引用计数: 4

    ptr1管理的内存引用计数: 3
    ptr2管理的内存引用计数: 3
    ptr3管理的内存引用计数: 3
    ptr4管理的内存引用计数: 0

    ptr5管理的内存引用计数: 1

reset()函数就是让智能指针初始化,如果这个智能指针当中存的值,那么调用reset()的时候就会释放原来的空间,使引用计数-1

shared_ptr 获取原始指针

使用get()函数返回原始指针

int main()
{
    shared_ptr<int>  p(new int);
    *p = 100;
    cout << *p.get() << "  " << *p << endl;
    return 0;
}
//100  100

weak_ptr (弱引用智能指针)

弱引用类型的指针就是一个特殊的智能指针,它可以允许你观察和访问有强引用智能指针管理的对象,但不会增加对象的引用计数。

我们可以把weak_ptr看作是shared_ptr指针的助手,他不管理shared_ptr内部的指针。不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数。它的主要作用就是作为旁观者监视shared_ptr指针管理的资源是否存在。

初始化的方式

构造函数、拷贝构造

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> sp(new int);

        //weak_ptr<int> wp1; 构造了一个空 weak_ptr 对象
    weak_ptr<int> wp1; 

        // weak_ptr<int> wp2(wp1); 通过一个空 weak_ptr 对象构造了另一个空 weak_ptr 对象
        weak_ptr<int> wp2(wp1);

        //weak_ptr<int> wp3(sp); 通过一个 shared_ptr 对象构造了一个可用的 weak_ptr 实例对象
    weak_ptr<int> wp3(sp);

        //wp4 = sp; 通过一个 shared_ptr 对象构造了一个可用的 weak_ptr 实例对象(这是一个隐式类型转换)
    weak_ptr<int> wp4;
    wp4 = sp;

        //wp5 = wp3; 通过一个 weak_ptr 对象构造了一个可用的 weak_ptr 实例对象
    weak_ptr<int> wp5;
    wp5 = wp3;

    return 0;
}

use_count()

use_count的方法可以获得当前所观测的引用计数。和shared_ptr的count计数作用一样。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> sp(new int);

    weak_ptr<int> wp1;
    weak_ptr<int> wp2(wp1);
    weak_ptr<int> wp3(sp);
    weak_ptr<int> wp4;
    wp4 = sp;
    weak_ptr<int> wp5;
    wp5 = wp3;

    cout << "use_count: " << endl;
    cout << "wp1: " << wp1.use_count() << endl;
    cout << "wp2: " << wp2.use_count() << endl;
    cout << "wp3: " << wp3.use_count() << endl;
    cout << "wp4: " << wp4.use_count() << endl;
    cout << "wp5: " << wp5.use_count() << endl;
    return 0;
}
   /* use_count:
    wp1: 0
    wp2: 0
    wp3: 1
    wp4: 1
    wp5: 1*/

通过上面的代码看出,虽然弱引用智能指针wp3,wp4,wp5都是检测的同一个资源,但是没有像shared_ptr一样,随着检测增多,而增加引用计数。这也说shared_ptr只是监测资源,而不是管理资源的。

expired()

expired()方法可以判断所监测的资源是否已经被释放。当shared_ptr管理的资源被释放或者是调用了reset()方法,就会返回 true 表示所监测的资源不存在了。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> shared(new int(10));
    weak_ptr<int> weak(shared);
    cout << "1. weak " << (weak.expired() ? "is" : "is not") << " expired" << endl;

    shared.reset();
    cout << "2. weak " << (weak.expired() ? "is" : "is not") << " expired" << endl;

    return 0;
}

//1. weak is not expired
//2. weak is expired

lock()

通过调用 std::weak_ptr 类提供的 lock() 方法来获取管理所监测资源的 shared_ptr 对象

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> sp1, sp2;
    weak_ptr<int> wp;

    sp1 = std::make_shared<int>(520);
    wp = sp1;
    sp2 = wp.lock();
    cout << "use_count: " << wp.use_count() << endl;

    sp1.reset();
    cout << "use_count: " << wp.use_count() << endl;

    sp1 = wp.lock();
    cout << "use_count: " << wp.use_count() << endl;

    cout << "*sp1: " << *sp1 << endl;
    cout << "*sp2: " << *sp2 << endl;

    return 0;
}

//use_count: 2
//    use_count : 1
//    use_count : 2
//    * sp1 : 520
//    * sp2 : 520

此代码中,sp2 = wp.lock(); 通过调用 lock() 方法得到一个用于管理 weak_ptr 对象所监测的资源的共享智能指针对象,使用这个对象初始化 sp2,此时所监测资源的引用计数为 2。

sp1.reset(); 共享智能指针 sp1 被重置,weak_ptr 对象所监测的资源的引用计数减 1。

sp1 = wp.lock(); sp1 重新被初始化,并且管理的还是 weak_ptr 对象所监测的资源,因此引用计数加 1。

共享智能指针对象 sp1 和 sp2 管理的是同一块内存,因此最终打印的内存中的结果是相同的,都是 520。

reset()

用来清空weak_ptr对象,使其不监测任何对象。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> sp(new int(10));
    weak_ptr<int> wp(sp);
    cout << "1. wp " << (wp.expired() ? "is" : "is not") << " expired" << endl;

    wp.reset();
    cout << "2. wp " << (wp.expired() ? "is" : "is not") << " expired" << endl;

    return 0;
}

1. wp is not expired
2. wp is expired

返回管理this的shared_ptr

getSharedPtr() 函数中,你直接返回了一个 shared_ptr<Test>,并传入了指向当前对象的裸指针 this。然而,当使用 shared_ptr 构造函数直接接受一个裸指针时,这个 shared_ptr 并不知道它是否已经被其他 shared_ptr 管理。这可能导致资源释放的不确定性和重复释放的问题。

#include <iostream>
#include <memory>
using namespace std;

struct Test
{
    shared_ptr<Test> getSharedPtr()
    {
        return shared_ptr<Test>(this);
    }

    ~Test()
    {
        cout << "析构函数" << endl;
    }
};

int main()
{
    shared_ptr<Test> sp1(new Test);
    cout << "引用个数 " << sp1.use_count() << endl;
    shared_ptr<Test> sp2 = sp1->getSharedPtr();
    cout << "引用个数: " << sp1.use_count() << endl;
    return 0;
}
//引用计数: 1
//引用计数: 1
//析构函数
//析构函数

通过输出的结果可以看到一个对象被析构了两次,其原因是这样的:在这个例子中使用同一个指针 this 构造了两个智能指针对象 sp1 和 sp2,这二者之间是没有任何关系的,因为 sp2 并不是通过 sp1 初始化得到的实例对象。在离开作用域之后 this 将被构造的两个智能指针各自析构,导致重复析构的错误。

循环引用问题

#include <iostream>
#include <memory>
using namespace std;

class A;
class B;

class A
{
public:
    shared_ptr<B> bptr;
    ~A()
    {
        cout << "class TA is disstruct ..." << endl;
    }
};

class B
{
public:
    shared_ptr<A> aptr;
    ~B()
    {
        cout << "class TB is disstruct ..." << endl;
    }
};

void testPtr()
{
    shared_ptr<A> ap(new A);
    shared_ptr<B> bp(new B);
    cout << "A 的 引用计数: " << ap.use_count() << endl;
    cout << "B 的 引用计数: " << bp.use_count() << endl;

    ap->bptr = bp;
    bp->aptr = ap;
    cout << "A 的 引用计数: " << ap.use_count() << endl;
    cout << "B 的 引用计数: " << bp.use_count() << endl;
}

int main()
{
    testPtr();
    return 0;
}

A 的 引用计数: 1
B 的 引用计数: 1
A 的 引用计数: 2
B 的 引用计数: 2

共享智能指针 ap、bp 对 A、B 实例对象的引用计数变为 2,在共享智能指针离开作用域之后引用计数只能减为1,这种情况下不会去删除智能指针管理的内存,导致类 A、B 的实例对象不能被析构,最终造成内存泄露。

图释循环引用:

用图片解释就是这样:

当释放的时候:

解决方法:

通过使用 weak_ptr 可以解决这个问题,只需要将类 A 或者 B 的任意一个成员改为 weak_ptr.

由于weak_ptr不会增加shared_ptr的引用计数,所以A和B 中有一个的引用计数为1,在pa和pb析构时,会正确地释放掉内存

下面程序中,在对类 A 成员赋值时 ap->bptr = bp; 由于 bptr 是 weak_ptr 类型,这个赋值操作并不会增加引用计数,所以 bp 的引用计数仍然为 1,在离开作用域之后 bp 的引用计数减为 0,类 B 的实例对象被析构。

在类 B 的实例对象被析构的时候,内部的 aptr 也被析构,其对 A 对象的管理解除,内存的引用计数减为 1,当共享智能指针 ap 离开作用域之后,对 A 对象的管理也解除了,内存的引用计数减为 0,类 A 的实例对象被析构。

#include <iostream>
#include <memory>
using namespace std;

class A;
class B;

class A
{
public:
    weak_ptr<B> bptr;
    ~A()
    {
        cout << "A 的 析构函数" << endl;
    }
};

class B
{
public:
    shared_ptr<A> aptr;
    ~B()
    {
        cout << "B 的 析构函数" << endl;
    }
};

void testPtr()
{
    shared_ptr<A> ap(new A);
    shared_ptr<B> bp(new B);
    cout << "A 的 引用计数: " << ap.use_count() << endl;
    cout << "B 的 引用计数: " << bp.use_count() << endl;

    ap->bptr = bp;
    bp->aptr = ap;
    cout << "A  的 引用计数: " << ap.use_count() << endl;
    cout << "B  的 引用计数: " << bp.use_count() << endl;
}

int main()
{
    testPtr();
    return 0;
}

A 的 引用计数: 1
B 的 引用计数: 1
A  的 引用计数: 2
B  的 引用计数: 1
B 的 析构函数
A 的 析构函数

unique_ptr(独占智能指针)

初始化

std::unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针,但是不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。

#include <iostream>
#include <string>
#include <memory>
using namespace std;

int main()
{
    // 通过构造函数初始化对象
    unique_ptr<int> ptr1(new int(10));
    //  报错
    unique_ptr<int> ptr2 = ptr1;
    return 0;
}

unique_ptr 是一种独占所有权的智能指针,它确保在任何时候只有一个指针可以拥有对所指向对象的所有权。因此,它不允许被复制,但可以通过函数返回给其他的 unique_ptr 或者通过 std::move() 转移给其他的 unique_ptr

这样发生的是所有权的转移。也就是说,函数中的 unique_ptr 将其所指向的对象所有权转移给了调用该函数的地方。这意味着函数结束后,原始的 unique_ptr 将不再拥有对象的所有权,因为所有权已经转移到了函数外的另一个 unique_ptr 中。

当使用 std::move() 将一个 unique_ptr 转移给另一个 unique_ptr 时,也发生了所有权的转移。通过 std::move(),告诉编译器你要移动对象的所有权,而不是复制它。这样做不会创建对象的拷贝,而是将原始 unique_ptr 指向的对象所有权转移到新的 unique_ptr 中。原始的 unique_ptr 将不再拥有对象的所有权,而新的 unique_ptr 则成为对象的唯一所有者。

reset()

使用 reset 方法可以让 unique_ptr 解除对原始内存的管理,也可以用来初始化一个独占的智能指针。

#include <iostream>
#include <memory>
using namespace std;
int main()
{
    unique_ptr<int> ptr1(new int(10));
    unique_ptr<int> ptr2 ;
    ptr1.reset(); //解除对原始内存的管理
    ptr2.reset(new int(250)); //重新指定智能指针管理的原始内存

    return 0;
}

如果想要获取独占智能指针管理的原始地址,可以调用 get () 方法

#include <iostream>
#include <memory>
using namespace std;
int main()
{
    unique_ptr<int> ptr1(new int(10));
    unique_ptr<int> ptr2 = move(ptr1);

    ptr2.reset(new int(251));
    cout << *ptr2.get() << endl;    // 得到内存地址中存储的实际数值 250
    return 0;
}
    251

shared_ptr的实现

实现1:

#pragma once
#include<iostream>
using namespace std;

//实现方案一
//使用辅助类Ref管理引用计数
template<class T>
class Ref {
private:
    //计数变量
    int r_count = 0;
    //存放原始指针
    T* object;
public:
    //构造函数
    Ref(T* target) :object(target) {
        r_count++;
    }

    //频繁调用使用inline函数,减少函数调用的开销
    //引用计数+1
    inline void increase() {
        r_count++;
    }
    //引用计数-1并且要判断是否要释放原始堆区内存
    inline void reduce() {
        r_count--;
        //如果引用计数为0
        //释放管理的堆区内存和自己
        if (r_count == 0) {
            delete object;
            delete this;
        }
    }

    T* get() {
        return object;
    }

    int getCount() {
        return r_count;
    }
};
//共享智能指针需要的方法:
/*
        无参构造,传递指针构造,拷贝构造,移动构造,拷贝赋值,移动赋值
        reset()替换对象 reset()销毁对象
        operator*()  operator->()
        get()获取原始指针
        use_count 获得引用计数
*/
template <class T>
class Share_ptr1
{
private:
    Ref<T>* ref = nullptr;
public:
    //默认构造函数默认初始化
    Share_ptr1() = default;
    ~Share_ptr1()
    {
        if (ref)ref->reduce();//引用计数-1
    }
    Share_ptr1(T* newP) {
        cout << "调用构造函数" << endl;
        ref = new Ref<T>(newP);
    }

    Share_ptr1(const Share_ptr1& other) {
        cout << "调用拷贝构造函数" << endl;
        this->ref = other.ref;
        if (ref)ref->increase();
    }
    Share_ptr1(Share_ptr1&& other) {
        cout << "调用移动构造函数" << endl;
        ref = other.ref;
        other.ref = nullptr;
    }
    Share_ptr1& operator = (const Share_ptr1 & other) {
        cout << "调用赋值函数" << endl;
        //因为要进行赋值操作,所以之前如果指向一个地址的话,
        //那个指针的引用计数要-1
        if (ref)ref->reduce();
        ref = other.ref;
        if (ref)ref->increase();
        return *this;
    }
    Share_ptr1& operator =(Share_ptr1 && other) {
        cout << "调用移动赋值函数" << endl;
        if (ref)ref->reduce();
        ref = other.ref;
        other.ref = nullptr;
        return *this;
    }
    void reset(T* newP) {
        if (ref)ref->reduce();

        ref = new Ref<T>(newP);
    }
    void reset() {
        if (ref)ref->reduce();
        ref = nullptr;
    }
    //*要返回对象类型的
    T& operator *() {
        if (ref) return *ref->get();
    }
    //->是对指针操作,要返回指针类型
    T* operator ->() {
        if (ref) return ref->get();
    }
    int use_count() {
        if (ref)return ref->getCount();
        return 0;
    }

};

实现2:

#pragma once
#include<iostream>
using namespace std;

template<class T>
class Share_ptr2 {
private:
    T* data_ = nullptr;
    size_t* count_ = nullptr;
private:
    //释放资源
    void release() {
        if (count_) {
            --(*count_);
            if (*count_ == 0) {
                delete count_;
                delete data_;
            }
            count_ = nullptr;
            data_ = nullptr;
        }
    }
public:
    //构造函数
    Share_ptr2(T* ptr = nullptr) {
        if (ptr) {
            count_ = new size_t(1);
            data_ = ptr;
        }
    }
    //拷贝构造
    Share_ptr2(const Share_ptr2<T>& other) {
        count_ = other.count_;
        data_ = other.data_;
        if (count_) {
            ++(*count_);
        }
    }
    //移动构造
    Share_ptr2(Share_ptr2<T>&& other) {
        count_ = other.count_;
        data_ = other.data_;
        other.count_ = nullptr;
        other.data_ = nullptr;
    }
    ~Share_ptr2()
    {
        release();
    }
    //赋值运算符
    Share_ptr2<T>& operator=(const Share_ptr2<T>&other){
        if (this != &other) {
            this->release();
            count_ = other.count_;
            data_ = other.data_;
            if (count_) {
                ++(*count_);
            }
        }
        return *this;
    }
    T& operator*() const {
        return *data_;
    }
    T* operator->() const {
        return data_;
    }
    size_t use_count() const {
        return count_ ? *count_ : 0;
    }
    void reset(T* ptr = nullptr) {
        this->release();
        if (ptr) {
            count_ = new size_t(1);
            data_ = ptr;
        }
    }
};

标签:count,weak,智能,详解,shared,include,ptr,指针
From: https://blog.csdn.net/2201_75839679/article/details/137235127

相关文章

  • 7、双指针-接雨水
     按列求+辅助数组只关注每一列当前能够留下几滴雨水。0和末尾位置不用考虑,盛不了雨水。现在有个0<i<length-1位置,那么他能盛多少雨水呢?取决于左边最大值和右边最大值,Math.min(leftArr[i-1],rightArr[i+1])。再减去i位置的高度就是可以盛的雨水,如果本身高度大于左右边最......
  • 阿里巴巴拍立淘API新功能揭秘:图片秒搜商品,实现智能化个性化购物新体验
    在数字化快速发展的今天,智能化和个性化已经成为购物体验中不可或缺的元素。为了满足消费者日益增长的购物需求,阿里巴巴中国站不断推陈出新,其中拍立淘API的新功能——图片秒搜商品,无疑为智能化个性化购物体验开创了新的篇章。一、拍立淘API新功能概述拍立淘API是阿里巴巴中国......
  • YOLOv8 深度详解!一文看懂,快速上手
    https://zhuanlan.zhihu.com/p/598566644?utm_id=0&wd=&eqid=a1d56281000fe8920000000464910f3a YOLOv8是ultralytics公司在2023年1月10号开源的YOLOv5的下一个重大更新版本,目前支持图像分类、物体检测和实例分割任务,在还没有开源时就收到了用户的广泛关注。考虑到......
  • C++ this指针的概念和使用
    this指针的概念:在C++中成员变量和成员函数是分开存储的。每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码。那么问题是:这一块代码是如何区分哪个对象调用自己的呢?c++通过提供特殊的对象指针,this指针,解决上述问题。关键:this指针指向......
  • Golang中的强大Web框架Fiber详解
    Golang 取消首页编程手机软件硬件安卓苹果手游教程平面服务器首页 > 脚本专栏 > Golang >Golang Web框架FiberGolang中的强大Web框架Fiber详解2023-10-2410:31:51 作者:技术的游戏在不断发展的Web开发领域中,选择正确的框架可以极大地影响项目的效......
  • OccNet 栅格占据网络:重建智能驾驶场景表征
    随着高阶智能驾驶的发展,长尾障碍物感知成为智驾发力的关键点。驾驶场景中常见的行人、车、障碍物,能够通过3D物体检测等方式实现其位置、大小的估计。而现实世界城区的交通路况中,还存在海量长尾场景问题:如异形车辆、路上的石子、掉落的树叶等障碍物,以3D检测框、点云等传统表......
  • socket编程——C++实现基于UDP协议的简单通信(含详解)
    文章后面有代码,可以直接复制在VisualStudio2022中运行(注意:必须是两个项目,客户端服务端各一个,连接在同一网络中,先运行服务端,并且客户端数据发送的目标IP要改为你服务端的IP)目录前言帮助文档一、UDP通信框架1.服务端2.客户端二、服务端实现1.加载库(WSAStartup函数)......
  • 腾讯云2核4G轻量服务器应用场景详解:2024年5M带宽服务器测评与优惠活动大放送
     随着云计算技术的日益成熟,腾讯云推出的2核4G5M轻量应用服务器已成为众多用户的首选。那么,这款服务器究竟能干什么?适用哪些场景呢?腾讯云2核4G5M轻量应用服务器,凭借其出色的性能与合理的价格,已成为性价比极高的云服务器产品。对于大部分个人使用需求,如网页浏览、在线视频播放......
  • lsblk命令参数详解
    lsblk命令用于列出块设备的信息,包括磁盘、分区和挂载点等。下面是lsblk命令的一些常用用法和示例讲解: lsblk:简单地运行lsblk命令会列出所有块设备的基本信息,包括设备名、大小和挂载点等。lsblk [设备名称]:显示指定设备的信息,可以是磁盘或分区的设备名称。lsblk -a:显示所......
  • 基于深度学习的智能监考系统(网页版+YOLOv8/v7/v6/v5代码+训练数据集)
    摘要:本文深入研究了基于YOLOv8/v7/v6/v5的智能监考系统,核心采用YOLOv8并整合了YOLOv7、YOLOv6、YOLOv5算法,进行性能指标对比;详述了国内外研究现状、数据集处理、算法原理、模型构建与训练代码,及基于Streamlit的交互式Web应用界面设计。在Web网页中可以支持图像、视频和实时......