C++——智能指针——auto_ptr、shared_ptr、unique_ptr

1.4、智能指针

智能指针是行为类似于指针的类对象。

C++11 中提供了三种智能指针,使用这些智能指针时需要引用头文件 :

·shared_ptr;

·unique_ptr;

·auto_ptr;

这三个智能指针模板都定义了类似指针的对象,可意义将new获得(直接或者间接)的地址赋给这种对象。当智能指针过期时,其析构函数将使delete来释放内存。智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。

·weak_ptr;

·auto_ptr; (现在基本不用)

使用智能指针:

定义:

auto_ptr<double> pt(new double);
unique_ptr<double> ptt(new double);
share_ptr<string> pttt(new string);

使用智能指针需要包含头文件memory;

共享智能指针:参考爱编程的大丙
1.shared_ptr的初始化:

共享智能指针是指多个智能指针可以同时管理同一块有效的内存,共享智能指针 shared_ptr 是一个模板类,如果要进行初始化有三种方式:通过构造函数、std::make_shared 辅助函数以及 reset 方法。共享智能指针对象初始化完毕之后就指向了要管理的那块堆内存,如果想要查看当前有多少个智能指针同时管理着这块内存可以使用共享智能指针提供的一个成员函数 use_count,函数原型如下:

// 管理当前对象的 shared_ptr 实例数量,或若无被管理对象则为 0。
long use_count() const noexcept;

初始化方式:

std::shared_ptr<T> 智能指针名字(创建堆内存);
例如:share_ptr<string> pttt(new string);

测试案例:

#include 
#include 
using namespace std;

int main(int argc,char *argv[]){
    int *p=new int(2);
    shared_ptr<int> pt(p);
    
    shared_ptr<double> pr(new double(3.14159));
    cout << "pr.count: " << pr.use_count() << endl;
    shared_ptr<string> pp(new string);
    cout << "pr.count: " << pp.use_count() << endl;
    shared_ptr<double> pd(pr);
    cout << "pd.count: " << pd.use_count() << endl;
    
    cout<<"*p: "<<*p<<" *pt: "<<*pt<<endl;
    cout<<"*pr: "<<*pr<<" *pd: "<<*pd<<endl;
    cout<<"*pp: "<<*pp<<endl;
    (*p)++;
    cout<<"*p: "<<*p<<" *pt: "<<*pt<<endl;
    return 0;
}

C++——智能指针——auto_ptr、shared_ptr、unique_ptr_第1张图片

如果智能指针被初始化了一块有效内存,那么这块内存的引用计数 + 1,如果智能指针没有被初始化或者被初始化为 nullptr 空指针,引用计数不会 + 1。另外,不要使用一个原始指针初始化多个shared_ptr。

int *p = new int;
shared_ptr<int> p1(p);
shared_ptr<int> p2(p);		// error, 编译不会报错, 运行会出错

通过拷贝和移动构造函数初始化:

#include 
#include 
using namespace std;

int main()
{
    // 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1
    shared_ptr<int> ptr1(new int(520));
    cout << "ptr1.countptr1.count: " << ptr1.use_count() << endl;
    //调用拷贝构造函数
    shared_ptr<int> ptr2(ptr1);
    cout << "ptr2.count: " << ptr2.use_count() << endl;
    shared_ptr<int> ptr3 = ptr1;
    cout << "ptr3.count: " << ptr3.use_count() << endl;
    //调用移动构造函数
    shared_ptr<int> ptr4(std::move(ptr1));
    cout << "ptr4.count: " << ptr4.use_count() << endl;
    std::shared_ptr<int> ptr5 = std::move(ptr2);
    cout << "ptr5.count: " << ptr5.use_count() << endl;
	
   // cout<<"ptr1: "<<*ptr1<
   // cout<<"ptr2: "<<*ptr2<
    cout<<"ptr3: "<<*ptr3<<endl;
    cout<<"ptr4: "<<*ptr5<<endl;
    cout<<"ptr5: "<<*ptr4<<endl;
    return 0;
}

C++——智能指针——auto_ptr、shared_ptr、unique_ptr_第2张图片

如果使用拷贝的方式初始化共享智能指针对象,这两个对象会同时管理同一块堆内存,堆内存对应的引用计数也会增加;如果使用移动的方式初始智能指针对象,只是转让了内存的所有权,管理内存的对象并不会增加,因此内存的引用计数不会变化。

通过std::make_shared初始化
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );

示例:

 shared_ptr<int> ptr1 = make_shared<int>(520);
#include 
#include 
#include 
using namespace std;

class Test
{
public:
    Test() 
    {
        cout << "construct Test..." << endl;
    }
    Test(int x) 
    {
        cout << "construct Test, x = " << x << endl;
    }
    Test(string str) 
    {
        cout << "construct Test, str = " << str << endl;
    }
    ~Test()
    {
        cout << "destruct Test ..." << endl;
    }
};

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

    shared_ptr<Test> ptr2 = make_shared<Test>();
    cout << "ptr2.count: " << ptr2.use_count() << endl;

    shared_ptr<Test> ptr3 = make_shared<Test>(520);
    cout << "ptr3.count: " << ptr3.use_count() << endl;

    shared_ptr<Test> ptr4 = make_shared<Test>("string");
    cout << "ptr4.count: " << ptr4.use_count() << endl;
    
    cout<<"ptr1"<<*ptr1<<endl;
    return 0;
}

C++——智能指针——auto_ptr、shared_ptr、unique_ptr_第3张图片

使用 std::make_shared() 模板函数可以完成内存地址的创建,并将最终得到的内存地址传递给共享智能指针对象管理。如果申请的内存是普通类型,通过函数的()可完成地址的初始化,如果要创建一个类对象,函数的()内部需要指定构造对象需要的参数,也就是类构造函数的参数。

通过 reset 方法初始化

共享智能指针类提供的 std::shared_ptr::reset 方法函数原型如下:

void reset() noexcept;

template< class Y >
void reset( Y* ptr );

template< class Y, class Deleter >
void reset( Y* ptr, Deleter d );

template< class Y, class Deleter, class Alloc >
void reset( Y* ptr, Deleter d, Alloc alloc );

ptr:指向要取得所有权的对象的指针
d:指向要取得所有权的对象的指针
aloc:内部存储所用的分配器
测试代码如下:

#include 
#include 
#include 
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。

获取原始指针

对应基础数据类型来说,通过操作智能指针和操作智能指针管理的内存效果是一样的,可以直接完成数据的读写。但是如果共享智能指针管理的是一个对象,那么就需要取出原始内存的地址再操作,可以调用共享智能指针类提供的 get () 方法得到原始地址,其函数原型如下:

T* get() const noexcept;

测试代码如下:

#include 
#include 
#include 
#include 
#include 
using namespace std;

int main()
{
    int len = 128;
    shared_ptr<char> ptr(new char[len]);
    // 得到指针的原始地址
    char* add = ptr.get();
    memset(add, 0, len);
    strcpy(add, "string4");
    cout << "string: " << add << endl;
    

    shared_ptr<int> p(new int);
    *p = 100;
    cout << p.get() << "  " << *p << endl;

    return 0;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oNm5DuIW-1619675049258)(C:\Users\wei\AppData\Roaming\Typora\typora-user-images\image-20210429132031649.png)]

指定删除器

当智能指针管理的内存对应的引用计数变为 0 的时候,这块内存就会被智能指针析构掉了。另外,我们在初始化智能指针的时候也可以自己指定删除动作,这个删除操作对应的函数被称之为删除器,这个删除器函数本质是一个回调函数,我们只需要进行实现,其调用是由智能指针完成的。

#include 
#include 
using namespace std;

// 自定义删除器函数,释放int型内存
void deleteIntPtr(int* p)
{
    delete p;
    cout << "int 型内存被释放了...";
}

int main()
{
    shared_ptr<int> ptr(new int(250), deleteIntPtr);
    return 0;
}

删除器函数也可以是 lambda 表达式,因此代码也可以写成下面这样:

int main()
{
    shared_ptr<int> ptr(new int(250), [](int* p) {delete p; });
    return 0;
}

在上面的代码中,lambda表达式的参数就是智能指针管理的内存的地址,有了这个地址之后函数体内部就可以完成删除操作了。

在 C++11 中使用 shared_ptr 管理动态数组时,需要指定删除器,因为 std::shared_ptr的默认删除器不支持数组对象,具体的处理代码如下:

int main()
{
    shared_ptr<int> ptr(new int[10], default_delete<int[]>());
    return 0;
}

另外,我们还可以自己封装一个 make_shared_array 方法来让 shared_ptr 支持数组,代码如下:

#include 
#include 
using namespace std;

template <typename T>
shared_ptr<T> make_share_array(size_t size)
{
    // 返回匿名对象
    return shared_ptr<T>(new T[size], default_delete<T[]>());
}

int main()
{
    shared_ptr<int> ptr1 = make_share_array<int>(10);
    cout << ptr1.use_count() << endl;
    shared_ptr<char> ptr2 = make_share_array<char>(128);
    cout << ptr2.use_count() << endl;
    return 0;
}

unique_ptr看我另一篇文章C++——auto_ptr与unique_ptr

你可能感兴趣的:(C++,c++)