C++11智能指针——shared_ptr类成员函数详解
C++ 11 模板库的 <memory> 头⽂件中定义的智能指针,即 shared_ptr 模板类,⽤来管理指针的存储,提供有限的内存回收函数,可同时与其他对象共享该管理功能,从⽽帮助彻底消除内存泄漏和悬空指针的问题。
shared_ptr 类型的对象能够获得指针的所有权并共享该所有权:⼀旦他们获得所有权,指针的所有者组就会在最后⼀个释放该所有权时负责删除该指针。
shared_ptr 对象⼀旦它们⾃⼰被销毁,或者它们的值因赋值操作或显式调⽤ shared_ptr::reset ⽽改变时,就会释放它们共同拥有的对象的所有权。⼀旦通过指针共享所有权的所有shared_ptr 对象都释放了该所有权,则删除托管对象(通常通过调⽤ ::delete,也可以在构造时指定不同的删除器)。
同⼀个shared_ptr被多个线程读,是线程安全的;
同⼀个shared_ptr被多个线程写,不是线程安全的;
共享引⽤计数的不同的shared_ptr被多个线程写,是线程安全的。
shared_ptr 对象只能通过复制它们的值来共享所有权:如果两个 shared_ptr 是从同⼀个(⾮shared_ptr)
指针构造(或制造)的,它们都将拥有该指针⽽不共享它,当其中⼀个释放时会导致潜在的访问问题它(删除其托管对象)并将另⼀个指向⽆效位置。
此外,shared_ptr 对象可以共享⼀个指针的所有权,同时指向另⼀个对象。这种能⼒被称为别名(参见构造函数),通常⽤于在拥有成员对象时指向成员对象。因此,⼀个
shared_ptr 可能与两个指针相关:
1)⼀个存储的指针,即它所指向的指针,以及它⽤ operator* 取消引⽤的指针。
2)⼀个所有者的指针(可能是共享的),它是所有权组负责在某个时间点删除的指针,并计为使⽤。
通常,存储指针和所有者指针指向同⼀个对象,但别名 shared_ptr 对象(使⽤别名构造函数及其副本构造的对象)可能指向不同的对象。不拥有任何指针的 shared_ptr 称为null shared_ptr。不指向任何对象的 shared_ptr 称为null shared_ptr 并且不应取消引⽤。请注意,空的 shared_ptr 不⼀定是null shared_ptr,null shared_ptr 也不⼀定是空的shared_ptr。shared_ptr 对象通过提供对它们通过运算符 * 和 -> 指向的对象的访问来复制有限的指针功能。出于安全原因,它们不⽀持指针算术。类似weak_ptr,能够与
shared_ptr 对象共享指针,⽽⽆需拥有它们。shared_ptr 有以下成员函数:
(1)构造函数
shared_ptr的构造函数根据使⽤的参数类型构造 shared_ptr 对象:
1) 默认构造函数:constexpr shared_ptr() noexcept;
2) 从空指针构造:constexpr shared_ptr(nullptr_t) : shared_ptr() {}
3) 从指针构造:template <class U> explicit shared_ptr (U* p);
4) 从指针 + 删除器构造:template <class U, class D> shared_ptr (U* p, D del); template <class D> shared_ptr (nullptr_t p, D del);
5) 从指针 + 删除器 + 分配器构造: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);
6) 复制构造函数:shared_ptr (const shared_ptr& x) noexcept; template <class U> shared_ptr (const shared_ptr<U>& x) noexcept;
7) 从weak_ptr 复制:template <class U> explicit shared_ptr (const weak_ptr<U>& x);
8) 移动构造函数:shared_ptr (shared_ptr&& x) noexcept;template <class U> shared_ptr (shared_ptr<U>&& x) noexcept;
9) 从其他类型的托管指针移动:template <class U> shared_ptr (auto_ptr<U>&& x); template <class U, class D> shared_ptr (unique_ptr<U,D>&& x);
10) 别名构造函数:template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;
默认构造函数 1) 和 2)对象为空(不拥有指针,使⽤计数为零)。从指针构造3)该对象拥有 p,将使⽤计数设置为 1。从指针 + 删除器构造 4)与 3) 相同,但该对象还拥有删除器
del 的所有权(并在某些时候需要删除 p 时使⽤它)。从指针 + 删除器 + 分配器构造 5)与 4) 相同,但内部使⽤所需的任何内存都是使⽤ alloc 分配的(对象保留⼀份副本,但不取得所有权)。复制构造函数 6)如果 x 不为空,则对象共享 x 资产的所有权并增加使⽤次数。如果 x 为空,则构造⼀个空对象(如同默认构造)。从weak_ptr 7) 复制同上6),除了如果 x 已经过期,则抛出 bad_weak_ptr 异常。移动构造函数 8)该对象获取由 x 管理的内容,包括其拥有的指针。 x 变成⼀个空对象(就像默认构造的⼀样)。从其他类型的托管指针移动 9)对象获取由 x 管理的内容并将使⽤计数设置为 1。放弃的对象变为空,⾃动失去指针的所有权。别名构造函数 10)同6),除了存储的指针是p。该对象不拥有 p,也不会管
理其存储。相反,它共同拥有 x 的托管对象并算作 x 的⼀种额外使⽤。它还将在发布时删除 x 的指针(⽽不是 p)。它可以⽤来指向已经被管理的对象的成员。
1) p: 其所有权被对象接管的指针。此指针值不应已由任何其他托管指针管理(即,此值不应来⾃托管指针上的调⽤成员 get)。U* 应隐式转换为 T*(其中 T 是 shared_ptr 的模板参数)。
2) del: ⽤于释放拥有的对象的删除器对象。这应该是⼀个可调⽤对象,将指向 T 的指针作为其函数调⽤的参数(其中 T 是 shared_ptr 的模板参数)。
3) alloc:⽤于分配/取消分配内部存储的分配器对象。
4) X: 托管指针类型的对象。U* 应隐式转换为 T*(其中 T 是 shared_ptr 的模板参数)。
#include <iostream>
#include <memory>
struct C {int* data;};
int main () {
std::shared_ptr<int> p1;
std::shared_ptr<int> p2 (nullptr);
std::shared_ptr<int> p3 (new int);
std::shared_ptr<int> p4 (new int, std::default_delete<int>());
std::shared_ptr<int> p5 (new int, [](int* p){delete p;}, std::allocator<int>());
std::shared_ptr<int> p6 (p5);
std::shared_ptr<int> p7 (std::move(p6));
std::shared_ptr<int> p8 (std::unique_ptr<int>(new int));
std::shared_ptr<C> obj (new C);
std::shared_ptr<int> p9 (obj, obj->data);
std::cout << "use_count:\n";
std::cout << "p1: " << p1.use_count() << '\n';
std::cout << "p2: " << p2.use_count() << '\n';
std::cout << "p3: " << p3.use_count() << '\n';
std::cout << "p4: " << p4.use_count() << '\n';
std::cout << "p5: " << p5.use_count() << '\n';
std::cout << "p6: " << p6.use_count() << '\n';
std::cout << "p7: " << p7.use_count() << '\n';
std::cout << "p8: " << p8.use_count() << '\n';
std::cout << "p9: " << p9.use_count() << '\n';
return0;
}
(2)析构函数
析构函数的作⽤是销毁shared_ptr对象。 但是,在此之前,根据成员 use_count 的值,它可能会产⽣以下副作⽤:
1)如果 use_count ⼤于 1(即该对象与其他 shared_ptr 对象共享其托管对象的所有权):与其共享所有权的其他对象的使⽤计数减 1。
2)如果 use_count 为 1(即对象是托管指针的唯⼀所有者):删除其拥有指针所指向的对象(如果 shared_ptr 对象是⽤特定的删除器构造的,则调⽤此函数;否则,函数使⽤运算符 删除)。
3)如果 use_count 为零(即对象为空),则该析构函数没有副作⽤。
⽤法举例:
#include <iostream>
#include <memory>
int main() {
auto deleter = [](int* p) {
std::cout << "[deleter called]\n"; delete p;
};
std::shared_ptr<int> foo(new int, deleter);
std::cout << "use_count: " << foo.use_count() << '\n';
return0; // [deleter called]
}
(3)赋值运算“=”
1) 复制:shared_ptr& operator= (const shared_ptr& x) noexcept; template <class U> shared_ptr& operator= (const shared_ptr<U>& x) noexcept;
2) 移动:shared_ptr& operator= (shared_ptr&& x) noexcept; template <class U> shared_ptr& operator= (shared_ptr<U>&& x) noexcept;
3) 从...移动:template <class U> shared_ptr& operator= (auto_ptr<U>&& x); template <class U, class D> shared_ptr& operator= (unique_ptr<U,D>&& x);
复制分配1) 将对象添加为 x 资产的共享所有者,从⽽增加它们的 use_count。移动分配 2) 将所有权从 x 转移到 shared_ptr 对象⽽不改变 use_count。 x 变成⼀个空的
shared_ptr(就像默认构造的⼀样)。同样,来⾃其他托管指针类型 3) 的移动分配也会转移所有权,并使⽤ set a use count of 1 进⾏初始化。
指针调用成员函数此外,在上述所有情况下,对该函数的调⽤与在其值更改之前调⽤了 shared_ptr 的析构函数具有相同的副作⽤(如果此 shared_ptr 是唯⼀的,则包括删除托管对象)。不能将指针的值直接分配给 shared_ptr 对象。您可以改⽤ make_shared 或成员重置。
托管指针类型的对象。U* 应隐式转换为 T*(其中 T 是 shared_ptr 的模板参数)。
⽤法举例:
#include <iostream>
#include <memory>
int main () {
std::shared_ptr<int> foo;
std::shared_ptr<int> bar (new int(10));
foo = bar; // copy
bar = std::make_shared<int> (20); // move
std::unique_ptr<int> unique (new int(30));
foo = std::move(unique); // move from unique_ptr
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
return0;
}
(4)swap函数
函数声明:void swap (shared_ptr& x) noexcept; 参数x: 另⼀个相同类型的 shared_ptr 对象(即,具有相同的类模板参数 T)。作⽤是将 shared_ptr 对象的内容与 x 的内容交换,在它们之间转移任何托管对象的所有权,⽽不会破坏或改变两者的使⽤计数。
⽤法举例:
#include <iostream>
#include <memory>
int main () {
std::shared_ptr<int> foo (new int(10));
std::shared_ptr<int> bar (new int(20));
foo.swap(bar);
std::cout << "*foo: " << *foo << '\n';
std::cout << "*bar: " << *bar << '\n';
return0;
}
(5)reset函数
重置shared_ptr,对于声明1) 对象变为空(如同默认构造)。在所有其他情况下,shared_ptr 以使⽤计数为 1 获取 p 的所有权,并且 - 可选地 - 使⽤ del 和/或 alloc 作为删除器和分配器。另外,调⽤这个函数有同样的副作⽤,就像在它的值改变之前调⽤了shared_ptr 的析构函数⼀样(包括删除托管对象,如果这个shared_ptr 是唯⼀的)。
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);
⽤法举例:
#include <iostream>
#include <memory>
int main () {
std::shared_ptr<int> sp; // empty
*sp=10;
std::cout << *sp << '\n';
*sp=20;
std::cout << *sp << '\n';
return0;
}
(6)get函数
函数声明:element_type* get() const noexcept; get()返回存储的指针。存储的指针指向shared_ptr对象解引⽤的对象,⼀般与其拥有的指针相同。存储的指针(即这个函数返回的指针)可能不是拥有的指针
(即对象销毁时删除的指针)如果 shared_ptr 对象是别名(即,别名构造的对象及其副本)。
⽤法举例:
#include <iostream>
#include <memory>
int main () {
int* p = new int (10);
std::shared_ptr<int> a (p);
if (a.get()==p)
std::cout << "a and p point to the same location\n";
// three ways of accessing the same address:
std::cout << *a.get() << "\n";
std::cout << *a << "\n";
std::cout << *p << "\n";
return0;
}
(7)取对象运算“*”
函数声明:element_type& operator*() const noexcept; 取消引⽤对象。返回对存储指针指向的对象的引⽤。等价于:*get()。如果shared_ptr的模板参数为void,则该成员函数是否定义取决于平台和编译器,以及它的返回类型 在这种情况下。
⽤法举例:
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> foo(new int);
std::shared_ptr<int> bar(new int(100));
*foo = *bar * 2;
std::cout << "foo: " << *foo << '\n';
std::cout << "bar: " << *bar << '\n';
return0;
}
(8)“->”操作符
函数声明:element_type* operator->() const noexcept; 取消引⽤对象成员。返回⼀个指向存储指针指
向的对象的指针,以便访问其成员之⼀。如果存储的指针是空指针,则不应调⽤该成员函数,它返回与 get() 相同的值。
⽤法举例:
#include <iostream>
#include <memory>
struct C { int a; int b; };
int main() {
std::shared_ptr<C> foo;
std::shared_ptr<C> bar(new C);
foo = bar;
foo->a = 10;
bar->b = 20;
if (foo) std::cout << "foo: " << foo->a << '' << foo->b << '\n';
if (bar) std::cout << "bar: " << bar->a << '' << bar->b << '\n';
return0;
}
(9)use_count函数
函数声明:long int use_count() const noexcept; use_count 返回与此对象(包括它)在同⼀指针上共享所有权的 shared_ptr 对象的数量。如果这是⼀个空的 shared_ptr,则该函数返回零。库实现不需要保留任何特定所有者集的计数 ,因此调⽤此函数可能效率不⾼。 要具体检查 use_count 是否为 1,也可以使⽤ member unique 代替,这样可能更快。
(10)unique函数
函数声明:bool unique() const noexcept; 检查是否唯⼀ 返回 shared_ptr 对象是否不与其他 shared_ptr 对象共享其指针的所有权(即,它是唯⼀的)。 空指针从来都不是唯⼀的(因为它们不拥有任何指针)。 如果唯⼀的 shared_ptr 对象释放此所有权,则它们负责删除其托管对象(请参阅析构函数)。 此函数应返回与 (use_count()==1) 相同的值,尽管它可能以更有效的⽅式执⾏此操作。 如果这是唯⼀的 shared_ptr,则返回值 true,否则返回 false。
⽤法举例:
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> foo;
std::shared_ptr<int> bar(new int);
std::cout << "foo unique?\n" << std::boolalpha;
std::cout << "1: " << foo.unique() << '\n'; // false (empty)
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论