智能指针shared_ptr

shared_ptr是通过指针保持对象共享所有权的智能指针。shared_ptr保存所管理对象的引用计数,当引用计数变为0时,对象将被删除。shared_ptr可以管理单个对象,也可以管理动态分配的对象数组。shared_ptr满足可复制构造、可复制赋值、可移动构造以及可移动赋值的要求。

代码库:

https://gitee.com/gamestorm577/CppStd

创建shared_ptr

一般通过std::make_shared来创建一个shared_ptr,例如:

struct MyStruct
{
    MyStruct()
    {
        printf("MyStruct1, {X: %d, Y: %d}\n", X, Y);
    }

    MyStruct(int x, int y)
        : X(x)
        , Y(y)
    {
        printf("MyStruct2, {X: %d, Y: %d}\n", X, Y);
    }

    int X = 0;
    int Y = 0;
};

std::cout << "construct a shared_ptr of single object: " << std::endl;
std::shared_ptr ptr1 = std::make_shared();

std::cout << "construct a shared_ptr of single object: " << std::endl;
std::shared_ptr ptr2 = std::make_shared(3, 5);

std::cout << "construct a shared_ptr of object array: " << std::endl;
std::shared_ptr ptr3 = std::make_shared(3);

输出结果为:

construct a shared_ptr of single object: 
MyStruct1, {X: 0, Y: 0}
construct a shared_ptr of single object: 
MyStruct2, {X: 3, Y: 5}
construct a shared_ptr of object array: 
MyStruct1, {X: 0, Y: 0}
MyStruct1, {X: 0, Y: 0}
MyStruct1, {X: 0, Y: 0}

引用计数

当使用一个shared_ptr赋值给一个shared_ptr时,被赋值的shared_ptr如果有管理的对象,那么这个对象的引用计数减1,同时赋值的shared_ptr管理对象的引用计数加1。接口use_count返回shared_ptr所指对象的引用计数。例如:

struct MyStruct
{
    MyStruct(int id)
        : ID(id)
    {
    }

    int ID = 0;
};

std::shared_ptr ptrA1 = std::make_shared(5);
printf("*****************\n");
printf("ptrA1 id = %d, use count = %ld\n", ptrA1->ID, ptrA1.use_count());

std::shared_ptr ptrA2 = ptrA1;
printf("*****************\n");
printf("ptrA1 id = %d, use count = %ld\n", ptrA1->ID, ptrA1.use_count());
printf("ptrA2 id = %d, use count = %ld\n", ptrA2->ID, ptrA2.use_count());

std::shared_ptr ptrB = std::make_shared(25);
ptrA1 = ptrB;
printf("*****************\n");
printf("ptrA1 id = %d, use count = %ld\n", ptrA1->ID, ptrA1.use_count());
printf("ptrA2 id = %d, use count = %ld\n", ptrA2->ID, ptrA2.use_count());
printf("ptrB id = %d, use count = %ld\n", ptrB->ID, ptrB.use_count());

输出结果为:

*****************
ptrA1 id = 5, use count = 1
*****************
ptrA1 id = 5, use count = 2
ptrA2 id = 5, use count = 2
*****************
ptrA1 id = 25, use count = 2
ptrA2 id = 5, use count = 1
ptrB id = 25, use count = 2

对象生命周期管理

当shared_ptr管理的对象的引用计数变为零时,该对象将被删除。

例如,某个shared_ptr管理对象的引用计数为1,当这个shared_ptr离开作用域时,shared_ptr被销毁,其管理对象的引用计数减1变为0,导致该对象被删除。

struct MyStruct
{
    MyStruct(int id)
        : ID(id)
    {
    }

    ~MyStruct()
    {
        printf("~MyStruct, id = %d\n", ID);
    }

    int ID = 0;
};

printf("test1: \n");
{
    {
        printf("Enter inner scope\n");
        std::shared_ptr ptr = std::make_shared(5);
        printf("About to leave inner scope\n");
    }
    printf("Enter outer scope\n");
}

输出结果为:

test1: 
Enter inner scope
About to leave inner scope
~MyStruct, id = 5
Enter outer scope

又比如,某个shared_ptr管理对象的引用计数为1,此时赋值给该shared_ptr另一个shared_ptr,被赋值的shared_ptr原来管理对象的引用计数减1变为0,导致原管理对象被删除:

printf("test2: \n");
{
    std::shared_ptr ptr1 = std::make_shared(5);
    std::shared_ptr ptr2 = std::make_shared(25);
    printf("About to assign ptr2 to ptr1\n");
    ptr1 = ptr2;
    printf("Finish assign ptr2 to ptr1\n");
}

输出结果为:

test2: 
About to assign ptr2 to ptr1
~MyStruct, id = 5
Finish assign ptr2 to ptr1
~MyStruct, id = 25

 自定义Deleter

shared_ptr也可以自定义Deleter,例如:

struct MyStruct
{
    MyStruct(int id)
        : ID(id)
    {
    }

    ~MyStruct()
    {
        printf("~MyStruct, id = %d\n", ID);
    }

    int ID;
};

struct Deleter
{
    void operator()(MyStruct* p) const
    {
        printf("Deleter, id = %d\n", p->ID);
        delete p;
    }
};

std::shared_ptr ptr1(new MyStruct(25), Deleter());
std::shared_ptr ptr2 = std::make_shared(5);

输出结果为:

~MyStruct, id = 5
Deleter, id = 25
~MyStruct, id = 25

访问对象

当shared_ptr管理的是单个对象时,可以使用重载操作符 operator* 来获取管理的对象,或者通过重载操作符 operator-> 来直接访问对象的成员:

struct MyStruct
{
    void Test()
    {
        printf("MyStruct::Test\n");
    }
};
printf("single object\n");
std::shared_ptr ptr1 = std::make_shared();
MyStruct& s1 = *ptr1;
s1.Test();
ptr1->Test();

输出结果为:

single object
MyStruct::Test
MyStruct::Test

当shared_ptr管理的是对象数组时,可以通过重载操作符 operator[] 来获取管理的对象:

printf("object array\n");
std::shared_ptr ptr2 = std::make_shared(5);
for (int i = 0; i < 5; ++i)
{
    MyStruct& s = ptr2[i];
    s.Test();
}

输出结果为:

object array
MyStruct::Test
MyStruct::Test
MyStruct::Test
MyStruct::Test
MyStruct::Test

其他接口

拷贝赋值函数

拷贝赋值会当被管理对象的引用计数加一,例如:

std::shared_ptr ptr1 = std::make_shared();
printf("ptr1 use count = %ld\n", ptr1.use_count());

std::shared_ptr ptr2 = ptr1;
printf("ptr1 use count = %ld\n", ptr1.use_count());
printf("ptr2 use count = %ld\n", ptr2.use_count());

输出结果为:

ptr1 use count = 1
ptr1 use count = 2
ptr2 use count = 2

移动赋值函数

移动赋值将共享对象从一个shared_ptr移动到另一个shared_ptr,移动后原shared_ptr不再占有对象,而对象的引用计数也不会变化。例如:

std::shared_ptr ptr1 = std::make_shared();
printf("ptr1 use count = %ld\n", ptr1.use_count());

std::shared_ptr ptr2 = std::move(ptr1);
printf("ptr1 use count = %ld\n", ptr1.use_count());
printf("ptr2 use count = %ld\n", ptr2.use_count());

输出结果为:

ptr1 use count = 1
ptr1 use count = 0
ptr2 use count = 1

get

获取shared_ptr管理对象的原始指针,例如:

std::shared_ptr ptr = std::make_shared(20.5);
double* val = ptr.get();
printf("number = %.3f\n", *val);

输出结果为:

number = 20.500

operator bool

判断shared_ptr是否占有对象,例如:

auto Func = [](const std::shared_ptr& ptr) -> void
{
    if (ptr)
    {
        printf("ptr not empty\n");
    }
    else
    {
        printf("ptr empty\n");
    }
};

std::shared_ptr ptr1 = std::make_shared();
std::shared_ptr ptr2 = nullptr;
Func(ptr1);
Func(ptr2);

输出结果为:

ptr not empty
ptr empty

owner_before

判断第一个shared_ptr是否在第二个shared_ptr之前拥有其指向的对象,例如:

std::shared_ptr ptr1 = std::make_shared();
std::shared_ptr ptr2 = ptr1;
std::shared_ptr ptr3 = std::make_shared();

printf("ptr1 owner before ptr2: %d\n", ptr1.owner_before(ptr2));
printf("ptr1 owner before ptr3: %d\n", ptr1.owner_before(ptr3));

输出结果为:

ptr1 owner before ptr2: 0
ptr1 owner before ptr3: 1

ptr1和ptr2拥有的是同一个对象,返回false。ptr1在ptr3之前占有其对象,返回true。

cast

用于智能指针shared_ptr类型的转换,包含接口static_pointer_cast、dynamic_pointer_cast、const_pointer_cast以及reinterpret_pointer_cast。实际上是对static_cast、dynamic_cast、const_cast以及reinterpret_cast做了一层封装。

例如:

struct Base
{
};

struct Derive : Base
{
    void Test()
    {
        printf("Derive::Test\n");
    }
};

std::shared_ptr ptr = std::make_shared();
std::shared_ptr ptr2 = std::static_pointer_cast(ptr);
ptr2->Test();

输出结果为:

Derive::Test

get_deleter

返回指定类型的删除器,例如:

struct MyStruct
{
};

struct Deleter1
{
    void Test1()
    {
        printf("Deleter1::Test1\n");
    }

    void operator()(MyStruct* p) const
    {
        delete p;
    }
};

struct Deleter2
{
    void Test2()
    {
        printf("Deleter2::Test2\n");
    }

    void operator()(int* p) const
    {
        delete p;
    }
};

std::shared_ptr ptr1(new MyStruct);
auto deleter1 = std::get_deleter(ptr1);
deleter1->Test1();

std::shared_ptr ptr2(new MyStruct);
auto deleter2 = std::get_deleter(ptr2);
deleter2->Test2();

输出结果为:

Deleter1::Test1
Deleter2::Test2

这里可以看出deleter和shared_ptr的类型并没有什么关系

比较shared_ptr

==、!=、<、<=、>、>=、<=>等符号可用于两个shared_ptr或者shared_ptr与nullptr的比较,比较的值是shared_ptr管理对象的指针的值,例如:

std::shared_ptr ptr1 = std::make_shared(5);
std::shared_ptr ptr2 = std::make_shared(5);
std::cout << "ptr1 == ptr1: " << (ptr1 == ptr1) << std::endl;
std::cout << "ptr1 == ptr2: " << (ptr1 == ptr2) << std::endl;

输出结果为:

ptr1 == ptr1: 1
ptr1 == ptr2: 0

operator<<

输出shared_ptr管理对象的指针的值到输出流中,例如:

std::shared_ptr ptr = std::make_shared(5);
std::cout << ptr << std::endl;

输出结果为:

0x600000203338

reset

用新的对象替换原来占有的对象,例如:

int* tmp = new int(25);
std::shared_ptr ptr = std::make_shared(5);
ptr.reset(tmp);
printf("ptr val = %d\n", *ptr);

 输出结果为:

ptr val = 25

swap

交换两个shared_ptr占有的对象,例如:

std::shared_ptr ptr1 = std::make_shared(5);
std::shared_ptr ptr2 = std::make_shared(25);
ptr1.swap(ptr2);

printf("ptr1 val = %d\n", *ptr1);
printf("ptr2 val = %d\n", *ptr2);

输出结果为: 

ptr1 val = 25
ptr2 val = 5

hash

计算对象的散列值,例如:

struct MyStruct
{
};

MyStruct* tmp = new MyStruct;
std::shared_ptr ptr(tmp);

std::cout << std::hash>()(ptr) << std::endl;
std::cout << std::hash()(tmp) << std::endl;

输出结果为: 

4281328325638402540
4281328325638402540

allocate_shared

创建一个shared_ptr对象,可以自定义内存分配器

你可能感兴趣的:(c++标准库基础,c++)