文章目录
C++智能指针之shared_ptr
shared_ptr篇
简介
shared_ptr是一种定义在<memory>
中的智能指针,用于管理指针的存储,提供智能资源回收,并且与内置指针相比几乎没有开销(取决于使用的删除器)。与unique_ptr 区别就是该指针可以和其它对象共享,shared_ptr 类型的对象具有获得指针所有权并共享该所有权的能力:一旦获得所有权,指针的所有者组将在他们中的最后一个释放该所有权时负责删除该指针(内部实现采用引用计数的方式),当然也可以通过shared_ptr::reset主动释放。
如果俩个对象都是通过指针构造来的,那么他们是俩个对象指针,不会共享对象。
成员函数
构造函数
默认构造
constexpr shared_ptr() noexcept;
{
std::cout << "\n\n";
std::shared_ptr<TestPtr> p1;
std::cout << "p1 use count:" << p1.use_count() << '\n';
/*
* output:
* p1 use count:0
*/
}
空指针构造
constexpr shared_ptr(nullptr_t) : shared_ptr() {}
{
std::cout << "\n\n";
std::shared_ptr<TestPtr> p2(nullptr);
std::cout << "p2 use count:" << p2.use_count() << '\n';
/*
* output:
* p2 use count:0
*/
}
指针构造
获取对象的所有权,并将use_count置为1。
template <class U> explicit shared_ptr (U* p);
{
std::cout << "\n\n";
std::shared_ptr<TestPtr> p3(new TestPtr("p3"));
std::cout << "p3 use count:" << p3.use_count() << '\n';
/*
* output:
* p3 use count:1
*/
}
指针构造 + 删除器
template <class U, class D> shared_ptr (U* p, D del);
template <class D> shared_ptr (nullptr_t p, D del);
{
std::cout << "\n\n";
std::shared_ptr<TestPtr> p4(new TestPtr("p4"), std::default_delete<TestPtr>());
std::cout << "p4 use count:" << p4.use_count() << '\n';
/*
* output:
* p4 use count:1
*/
}
指针构造 + 删除器 + 内存分配器
template <class U, class D, class Alloc> shared_ptr (U* p, D del, Alloc alloc);
template <class D, class Alloc> shared_ptr (nullptr_t p, D del, Alloc alloc);
{
std::cout << "\n\n";
std::shared_ptr<TestPtr> p5(new TestPtr("p5"), std::default_delete<TestPtr>(), std::allocator<TestPtr>());
std::cout << "p5 use count:" << p5.use_count() << '\n';
/*
* output:
* p5 use count:1
*/
}
拷贝构造
若p不会空,则use_count加1;若p为空,则构造一个空对象。
shared_ptr (const shared_ptr& x) noexcept;
template <class U> shared_ptr (const shared_ptr<U>& x) noexcept;
{
std::cout << "\n\n";
std::shared_ptr<TestPtr> p(new TestPtr("p"));
std::shared_ptr<TestPtr> p6(p);
std::cout << "p6 use count:" << p6.use_count() << '\n';
/*
* output:
* p6 use count:2
*/
}
移动构造
获取P对象的所有权,并且P成为空对象。
shared_ptr (shared_ptr&& x) noexcept;
template <class U> shared_ptr (shared_ptr<U>&& x) noexcept;
{
std::cout << "\n\n";
std::shared_ptr<TestPtr> p(new TestPtr("p"));
std::cout << "p use count:" << p.use_count() << " -> " << p.get() << "\n";
std::shared_ptr<TestPtr> p7(std::move(p));
std::cout << "p7 use count:" << p7.use_count() << " -> " << p7.get() << "\n";
std::cout << "p use count:" << p.use_count() << " -> " << p.get() << "\n";
/*
* output:
* p use count:1 -> 0x557fcd4c2280
* p7 use count:1 -> 0x557fcd4c2280
* p use count:0 -> 0
*/
}
从其它类型的管理指针移动构造
获取p对象的所有权,并将use_count置为1,对象P释放所有权。
template <class U> shared_ptr (auto_ptr<U>&& x);
template <class U, class D> shared_ptr (unique_ptr<U,D>&& x);
{
std::cout << "\n\n";
std::unique_ptr<TestPtr> p(new TestPtr("unique_ptr"));
std::cout << " p-> " << p.get() << "\n";
std::shared_ptr<TestPtr> p8(std::move(p));
std::cout << "p8 use count:" << p8.use_count() << " -> " << p8.get() << "\n";
std::cout << " p-> " << p.get() << "\n";
/*
* output:
* p-> 0x5588296dc280
* p8 use count:1 -> 0x5588296dc280
* p-> 0
*/
}
别名构造函数
该对象不拥有 p,并且不会管理其存储。 相反,它共同拥有 x 的托管对象并算作 x 的一种附加用途。 它还将在释放时删除 x 的指针(而不是 p)它可以用来指向已经被管理的对象的成员。
template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;
{
std::cout << "\n\n";
struct Data
{
/* data */
int *total;
};
std::shared_ptr<Data> p = std::make_shared<Data>();
p->total = new int;
*p->total = 10;
std::cout << "p use count:" << p.use_count() << " -> " << p.get() << "\n";
std::cout << "p->total:" << p->total << " -> " << *p->total << "\n";
std::shared_ptr<int> p9(p, p->total);
std::cout << "p9 use count:" << p9.use_count() << " -> " << p9.get() << "\n";
std::cout << "p use count:" << p.use_count() << " -> " << p.get() << "\n";
std::cout << "p->total:" << p->total << " -> " << *p->total << "\n";
delete p->total;
/*
* p use count:1 -> 0x56523715a2e0
* p->total:0x56523715a2f0 -> 10
* p9 use count:2 -> 0x56523715a2f0
* p use count:2 -> 0x56523715a2e0
* p->total:0x56523715a2f0 -> 10
*/
}
析构函数
销毁shared_ptr对象:
- 如果 use_count 大于 1(即对象与其他 shared_ptr 对象共享其托管对象的所有权):与其共享所有权的其他对象的使用计数减 1。
- 如果 use_count 为 1(即对象是托管指针的唯一所有者):删除其拥有的指针指向的对象(如果 shared_ptr 对象是用特定的删除器构造的,则调用 this;否则,函数使用运算符 删除)。
- 如果 use_count 为零(即对象为空),则此析构函数没有副作用。
{
{
std::shared_ptr<int> p0;
{
{
std::cout << "\n\n";
auto deleter = [](int *p)
{
delete p;
std::cout << "[deleter called] done\n";
};
std::shared_ptr<int> p(new int, deleter);
p0 = p;
std::cout << "p use count:" << p.use_count() << " -> " << p.get() << "\n";
std::cout << "p0 use count:" << p0.use_count() << " -> " << p0.get() << "\n";
}
// 在此 use_count 减为1,不析构
std::cout << "[OK] after leave p\n";
std::cout << "p0 use count:" << p0.use_count() << " -> " << p0.get() << "\n";
}
}
//在此 use_count 减为0,析构p
}
/* output:
* p use count:2 -> 0x55754bee52d0
* p0 use count:2 -> 0x55754bee52d0
* [OK] after leave p
* p0 use count:1 -> 0x55754bee52d0
* [deleter called] done
*/
get
返回对象所存储指针的地址。
element_type* get() const noexcept;
operator*
返回对象存储指针的解引用。
element_type& operator*() const noexcept;
operator->
解引用对象管理指针的成员。
element_type* operator->() const noexcept;
use_count
返回在与此对象(包括它)相同的指针上共享所有权的 shared_ptr 对象的数量。
long int use_count() const noexcept;
unique
返回 shared_ptr 对象是否不与其他 shared_ptr 对象共享其指针的所有权(即,它是唯一的)。
bool unique() const noexcept;
{
std::shared_ptr<TestPtr> p1(nullptr);
std::shared_ptr<TestPtr> p2 = std::make_shared<TestPtr>("p2");
std::shared_ptr<TestPtr> p3 = std::make_shared<TestPtr>("p3");
p1 = p2;
std::cout << "p1 unqiue :" << (p1.unique() ? " true" : " false") << "\n"; // false
std::cout << "p2 unqiue :" << (p2.unique() ? " true" : " false") << "\n"; // false
std::cout << "p3 unqiue :" << (p3.unique() ? " true" : " false") << "\n"; // true
}
/* output:
TestPtr : p2
TestPtr : p3
p1 unqiue : false
p2 unqiue : false
p3 unqiue : true
~TestPtr : p3
~TestPtr : p2
*/
operator bool
检查对象存储的指针是否为空;
explicit operator bool() const noexcept;
{
std::shared_ptr<TestPtr> p1(nullptr);
std::shared_ptr<TestPtr> p2 = std::make_shared<TestPtr>("p2");
std::cout << "p1 bool :" << (!p1 ? " true" : " false") << "\n"; // false
std::cout << "p2 bool :" << (!p2 ? " true" : " false") << "\n"; // true
}
/* output:
TestPtr : p2
p1 bool : true
p2 bool : false
~TestPtr : p2
*/
owner_before
根据严格的所有者的弱顺序,返回对象是否被认为在 x 之前。
template <class U> bool owner_before (const shared_ptr<U>& x) const;
template <class U> bool owner_before (const weak_ptr<U>& x) const;
{
int* p = new int(10);
std::shared_ptr<int> a(new int(20));
std::shared_ptr<int> b(a, p); // 别名构造
std::shared_ptr<int> c = a;
std::cout << "*a:" << *a << '\n';
std::cout << "*b:" << *b << '\n';
std::cout << "comparing a and b...\n" << std::boolalpha;
std::cout << "value-based: " << (!(a < b) && !(b < a)) << '\n';
std::cout << "owner-based: " << (!a.owner_before(b) && !b.owner_before(a)) << '\n';
std::cout << "c is before a: " << (c.owner_before(a)) << '\n';
std::cout << "a is before : " << (a.owner_before(c)) << '\n';
std::cout << "b is before a: " << (b.owner_before(a)) << '\n';
delete p;
}
/*output:
*a:20
*b:10
comparing a and b...
value-based: false
owner-based: true
c is before a: false
a is before : false
b is before a: false
*/
operator
- 复制(1) 将该对象添加为 x 资产的共享所有者,从而增加它们的 use_count。
shared_ptr& operator= (const shared_ptr& x) noexcept;
template <class U> shared_ptr& operator= (const shared_ptr<U>& x) noexcept;
- 移动 (2) 将所有权从 x 转移到 shared_ptr 对象,而不改变 use_count。 x 变成一个空的 shared_ptr (就像默认构造的一样)。
shared_ptr& operator= (shared_ptr&& x) noexcept;
template <class U> shared_ptr& operator= (shared_ptr<U>&& x) noexcept;
- 来自其他托管指针类型 (3) 的移动分配也转移所有权,并使用设置为使用计数 1 进行初始化。
template <class U> shared_ptr& operator= (auto_ptr<U>&& x);
template <class U, class D> shared_ptr& operator= (unique_ptr<U,D>&& x);
- 此外,在上述所有情况下,调用此函数的副作用与在其值更改之前调用 shared_ptr 的析构函数具有相同的副作用(如果此 shared_ptr 是唯一的,则包括删除托管对象)。
- 不能将指针的值直接分配给 shared_ptr 对象。 可以使用 make_shared 或 member reset 代替。
{
std::shared_ptr<TestPtr> p1(nullptr);
std::shared_ptr<TestPtr> p2 = make_shared<TestPtr>("p2");
std::shared_ptr<TestPtr> p3 = make_shared<TestPtr>("p3");
std::cout << "A:\n";
std::cout << "p1 use count:" << p1.use_count() << ' ' << p1.get() << '\n';
std::cout << "p2 use count:" << p2.use_count() << ' ' << p2.get() << '\n';
std::cout << "p3 use count:" << p3.use_count() << ' ' << p3.get() << '\n';
/* p2拷贝给p1后是放所有权(use_count + 1) */
p1 = p2;
std::cout << "copy:\n";
std::cout << "p1 use count:" << p1.use_count() << ' ' << p1.get() << '\n';
std::cout << "p2 use count:" << p2.use_count() << ' ' << p2.get() << '\n';
std::cout << "p3 use count:" << p3.use_count() << ' ' << p3.get() << '\n';
/* p3移动给p1后释放所有权,p1释放p2的所有权(use_count - 1) */
p1 = std::move(p3);
std::cout << "move:\n";
std::cout << "p1 use count:" << p1.use_count() << ' ' << p1.get() << '\n';
std::cout << "p2 use count:" << p2.use_count() << ' ' << p2.get() << '\n';
std::cout << "p3 use count:" << p3.use_count() << ' ' << p3.get() << '\n';
}
/* output:
TestPtr : p2
TestPtr : p3
A:
p1 use count:0 0
p2 use count:1 0x559cab432290
p3 use count:1 0x559cab4322f0
copy:
p1 use count:2 0x559cab432290
p2 use count:2 0x559cab432290
p3 use count:1 0x559cab4322f0
move:
p1 use count:1 0x559cab4322f0
p2 use count:1 0x559cab432290
p3 use count:0 0
~TestPtr : p2
~TestPtr : p3
*/
swap
交换对象所有权,将 shared_ptr 对象的内容与 x 的内容交换,在它们之间转移任何托管对象的所有权,而不会破坏或更改其中任何一个的使用计数。
void swap (shared_ptr& x) noexcept;
{
std::shared_ptr<TestPtr> p1 = make_shared<TestPtr>("p1");
std::shared_ptr<TestPtr> p2 = make_shared<TestPtr>("p2");
std::shared_ptr<TestPtr> p3(nullptr);
std::cout << "init:\n";
std::cout << "p1 use count:" << p1.use_count() << ' ' << p1.get() << '\n'; // 1
std::cout << "p2 use count:" << p2.use_count() << ' ' << p2.get() << '\n'; // 1
std::cout << "p3 use count:" << p3.use_count() << ' ' << p3.get() << '\n'; // 0
p1.swap(p2);
std::cout << "swap p2 -> p1:\n";
std::cout << "p1 use count:" << p1.use_count() << ' ' << p1.get() << '\n'; // 1
std::cout << "p2 use count:" << p2.use_count() << ' ' << p2.get() << '\n'; // 1
std::cout << "p3 use count:" << p3.use_count() << ' ' << p3.get() << '\n'; // 0
p3.swap(p1);
std::cout << "swap p1 -> p3:\n";
std::cout << "p1 use count:" << p1.use_count() << ' ' << p1.get() << '\n'; // 0
std::cout << "p2 use count:" << p2.use_count() << ' ' << p2.get() << '\n'; // 1
std::cout << "p3 use count:" << p3.use_count() << ' ' << p3.get() << '\n'; // 1
}
/* output:
init:
p1 use count:1 0x556a3c161290
p2 use count:1 0x556a3c1612f0
p3 use count:0 0
swap p2 -> p1:
p1 use count:1 0x556a3c1612f0
p2 use count:1 0x556a3c161290
p3 use count:0 0
swap p1 -> p3:
p1 use count:0 0
p2 use count:1 0x556a3c161290
p3 use count:1 0x556a3c1612f0
~TestPtr : p2
~TestPtr : p1
*/
reset
(1) void reset() noexcept;
(2) template <class U> void reset (U* p);
(3) template <class U, class D> void reset (U* p, D del);
(4) template <class U, class D, class Alloc> void reset (U* p, D del, Alloc alloc);
- 对于 reset (1),对象变为空(就像默认构造的一样),释放所有权。
- 在其他情况下,shared_ptr 获得 p 的所有权,使用计数为 1,并且 - 可选 - 分别使用 del 和/或 alloc 作为删除器和分配器。
- 调用此函数具有与 shared_ptr 的析构函数在其值更改之前调用相同的副作用(包括如果此 shared_ptr 是唯一的则删除托管对象)。
{
std::shared_ptr<TestPtr> p1(nullptr);
std::shared_ptr<TestPtr> p2 = make_shared<TestPtr>("p2");
std::cout << "init:\n";
std::cout << "p1 use count:" << p1.use_count() << ' ' << p1.get() << '\n'; // 0
std::cout << "p2 use count:" << p2.use_count() << ' ' << p2.get() << '\n'; // 1
p1.reset(new TestPtr("new p1"));
p2.reset();
std::cout << "reset:\n";
std::cout << "p1 use count:" << p1.use_count() << ' ' << p1.get() << '\n'; // 1
std::cout << "p2 use count:" << p2.use_count() << ' ' << p2.get() << '\n'; // 0
/* output:
TestPtr : p2
init:
p1 use count:0 0
p2 use count:1 0x5603af170290
TestPtr : new p1
~TestPtr : p2
reset:
p1 use count:1 0x5603af1702e0
p2 use count:0 0
~TestPtr : new p1
*/
}