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<MyStruct> ptr1 = std::make_shared<MyStruct>();
std::cout << "construct a shared_ptr of single object: " << std::endl;
std::shared_ptr<MyStruct> ptr2 = std::make_shared<MyStruct>(3, 5);
std::cout << "construct a shared_ptr of object array: " << std::endl;
std::shared_ptr<MyStruct[]> ptr3 = std::make_shared<MyStruct[]>(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<MyStruct> ptrA1 = std::make_shared<MyStruct>(5);
printf("*****************\n");
printf("ptrA1 id = %d, use count = %ld\n", ptrA1->ID, ptrA1.use_count());
std::shared_ptr<MyStruct> 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<MyStruct> ptrB = std::make_shared<MyStruct>(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<MyStruct> ptr = std::make_shared<MyStruct>(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<MyStruct> ptr1 = std::make_shared<MyStruct>(5);
std::shared_ptr<MyStruct> ptr2 = std::make_shared<MyStruct>(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<MyStruct> ptr1(new MyStruct(25), Deleter());
std::shared_ptr<MyStruct> ptr2 = std::make_shared<MyStruct>(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<MyStruct> ptr1 = std::make_shared<MyStruct>();
MyStruct& s1 = *ptr1;
s1.Test();
ptr1->Test();
输出结果为:
single object
MyStruct::Test
MyStruct::Test
当shared_ptr管理的是对象数组时,可以通过重载操作符 operator[] 来获取管理的对象:
printf("object array\n");
std::shared_ptr<MyStruct[]> ptr2 = std::make_shared<MyStruct[]>(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<int> ptr1 = std::make_shared<int>();
printf("ptr1 use count = %ld\n", ptr1.use_count());
std::shared_ptr<int> 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<int> ptr1 = std::make_shared<int>();
printf("ptr1 use count = %ld\n", ptr1.use_count());
std::shared_ptr<int> 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<double> ptr = std::make_shared<double>(20.5);
double* val = ptr.get();
printf("number = %.3f\n", *val);
输出结果为:
number = 20.500
operator bool
判断shared_ptr是否占有对象,例如:
auto Func = [](const std::shared_ptr<int>& ptr) -> void
{
if (ptr)
{
printf("ptr not empty\n");
}
else
{
printf("ptr empty\n");
}
};
std::shared_ptr<int> ptr1 = std::make_shared<int>();
std::shared_ptr<int> ptr2 = nullptr;
Func(ptr1);
Func(ptr2);
输出结果为:
ptr not empty
ptr empty
owner_before
判断第一个shared_ptr是否在第二个shared_ptr之前拥有其指向的对象,例如:
std::shared_ptr<int> ptr1 = std::make_shared<int>();
std::shared_ptr<int> ptr2 = ptr1;
std::shared_ptr<int> ptr3 = std::make_shared<int>();
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<Base> ptr = std::make_shared<Derive>();
std::shared_ptr<Derive> ptr2 = std::static_pointer_cast<Derive>(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<MyStruct> ptr1(new MyStruct);
auto deleter1 = std::get_deleter<Deleter1>(ptr1);
deleter1->Test1();
std::shared_ptr<MyStruct> ptr2(new MyStruct);
auto deleter2 = std::get_deleter<Deleter2>(ptr2);
deleter2->Test2();
输出结果为:
Deleter1::Test1
Deleter2::Test2
这里可以看出deleter和shared_ptr的类型并没有什么关系
比较shared_ptr
==、!=、<、<=、>、>=、<=>等符号可用于两个shared_ptr或者shared_ptr与nullptr的比较,比较的值是shared_ptr管理对象的指针的值,例如:
std::shared_ptr<int> ptr1 = std::make_shared<int>(5);
std::shared_ptr<int> ptr2 = std::make_shared<int>(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<int> ptr = std::make_shared<int>(5);
std::cout << ptr << std::endl;
输出结果为:
0x600000203338
reset
用新的对象替换原来占有的对象,例如:
int* tmp = new int(25);
std::shared_ptr<int> ptr = std::make_shared<int>(5);
ptr.reset(tmp);
printf("ptr val = %d\n", *ptr);
输出结果为:
ptr val = 25
swap
交换两个shared_ptr占有的对象,例如:
std::shared_ptr<int> ptr1 = std::make_shared<int>(5);
std::shared_ptr<int> ptr2 = std::make_shared<int>(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<MyStruct> ptr(tmp);
std::cout << std::hash<std::shared_ptr<MyStruct>>()(ptr) << std::endl;
std::cout << std::hash<MyStruct*>()(tmp) << std::endl;
输出结果为:
4281328325638402540
4281328325638402540
allocate_shared
创建一个shared_ptr对象,可以自定义内存分配器