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 可能与两个指针相关:

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';
  return 0;
}

(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'; return 0; // [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'; return 0;
}

(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'; return 0;
}

(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.reset (new int); // takes ownership of pointer
*sp=10;
std::cout << *sp << '\n'; sp.reset (new int); // deletes managed object, acquires new pointer
*sp=20;
std::cout << *sp << '\n'; sp.reset(); // deletes managed object return 0;
}

(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"; return 0;
}

(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'; return 0;
}

(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'; return 0;
}

(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) foo = bar;
std::cout << "2: " << foo.unique() << '\n'; // false (shared with bar) bar = nullptr;
std::cout << "3: " << foo.unique() << '\n'; // true return 0;
}

(11)“bool”操作

函数声明:explicit operator bool() const noexcept; 检查是否为 null。 返回存储的指针是否为空指针。 存储的指针指向 shared_ptr 对象解除引用的对象,通常与其拥有的指针相同(销毁时删除的指针)。 如果 shared_ptr 对象是别名(即别名构造的对象及其副本),它们可能会有所不同。该函数返回的结果与 get()!=0 相同。 请注意,空的 shared_ptr(即此函数返回 false 的指针)不一定是空的 shared_ptr。 别名可能拥有某个指针但指向空,或者所有者组甚至可能拥有空指针(参见构造函数 4 和 5)。

用法举例:

#include <iostream>
#include <memory> int main() {
std::shared_ptr<int> foo;
std::shared_ptr<int> bar(new int(34)); if (foo) std::cout << "foo points to " << *foo << '\n';
else std::cout << "foo is null\n"; if (bar) std::cout << "bar points to " << *bar << '\n';
else std::cout << "bar is null\n"; return 0;
}

(12)owner_before函数

函数声明:

template <class U> bool owner_before (const shared_ptr<U>& x) const;
template <class U> bool owner_before (const weak_ptr<U>& x) const;

基于所有者的排序。根据严格的弱基于所有者的顺序返回是否认为对象在 x 之前。与 operator< 重载不同,此排序考虑了 shared_ptr 的拥有指针,而不是存储的指针,使得两个 如果它们都共享所有权,或者它们都为空,即使它们存储的指针值不同,这些对象中的一个被认为是等效的(即,无论操作数的顺序如何,该函数都返回 false)。 如果 shared_ptr 对象是一个别名(别名构造的对象及其副本),则 shared_ptr 对象解引用)可能不是拥有的指针(即对象销毁时删除的指针)。该函数由 owner_less 调用以确定其结果。

用法举例:

#include <iostream>
#include <memory> int main() {
int* p = new int(10); std::shared_ptr<int> a(new int(20));
std::shared_ptr<int> b(a, p); // alias constructor 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'; delete p;
return 0;
}

C++ 11 智能指针(shared_ptr)类成员函数详解的更多相关文章

  1. CString 的成员函数详解

    CSTRING的成员函数详解   typedef ATL::CStringT< TCHAR, StrTraitMFC_DLL< TCHAR > > CString;CStrin ...

  2. C++函数指针和类成员函数指针

    一.函数指针——基本形式 char Fun(int n) { return char(n); } //char(*pFun)(int); void main() { char(*pFun)(int); ...

  3. CString成员函数详解[转]

    1.构造函数(常用) CString( const unsigned char* psz );      例:char s[]="abcdef";              cha ...

  4. 深入理解类成员函数的调用规则(理解成员函数的内存为什么不会反映在sizeof运算符上、类的静态绑定与动态绑定、虚函数表)

    本文转载自:http://blog.51cto.com/9291927/2148695 总结: 一.成员函数的内存为什么不会反映在sizeof运算符上?             成员函数可以被看作是类 ...

  5. C/C++ 类成员函数指针 类成员数据指针

    普通函数指针:  "return_type (*ptr_name)(para_types) " 类成员函数指针: "return_type (class_name::*p ...

  6. c++11 智能指针 unique_ptr、shared_ptr与weak_ptr

    c++11 智能指针 unique_ptr.shared_ptr与weak_ptr C++11中有unique_ptr.shared_ptr与weak_ptr等智能指针(smart pointer), ...

  7. 类成员函数指针的特殊之处(成员函数指针不是指针,内含一个结构体,需要存储更多的信息才能知道自己是否virtual函数)

    下面讨论的都是类的非静态成员函数. 类成员函数指针的声明及调用: 1 2 3 4 5 6 7 //pr是指向Base类里的非静态成员函数的指针 //其行参为(int, int),返回值为void vo ...

  8. C++的类成员和类成员函数指针

    类成员函数指针: 用于访问类成员函数,和一般函数指针有区别. 类成员函数处理的是类数据成员,声明类成员函数指针的同时,还要指出具体是哪个类的函数指针才可以.调用时也要通过对象调用. 而对于类的静态成员 ...

  9. 并发编程: c++11 thread(Func, Args...)利用类成员函数创建线程

    c++11是VS2012后支持的新标准,为并发编程提供了方便的std::thread. 使用示例: #include <thread> void thread_func(int arg1, ...

随机推荐

  1. Elasticserch与Elasticsearch_dsl用法

    Elasticserch与Elasticsearch_dsl用法 Elasticsearch_dsl::https://elasticsearch-dsl.readthedocs.io/en/late ...

  2. GO学习-(5) Go语言基础之基本数据类型

    Go语言中有丰富的数据类型,除了基本的整型.浮点型.布尔型.字符串外,还有数组.切片.结构体.函数.map.通道(channel)等.Go 语言的基本类型和其他语言大同小异. 基本数据类型 整型 整型 ...

  3. Error attempting to get column from result set

    当使用mybatis plus3.2.0+springboot2.1.1 报错 Error attempting to get column from result set 1.一般出现这种问题,最简 ...

  4. 自定义Kubernetes调度程序来编排高可用性应用程序

    自定义Kubernetes调度程序来编排高可用性应用程序 只要愿意遵守规则,在Kubernetes上进行部署和乘飞机旅行就可以很愉快.通常,事情会"正常工作".但是,如果有兴趣与必 ...

  5. 利用UltraScale和UltraScale+FPGA和MPSOC加速DSP设计生产力

    利用UltraScale和UltraScale+FPGA和MPSOC加速DSP设计生产力 Accelerating DSP Design Productivity with UltraScale an ...

  6. 5, java数据结构和算法: 栈 , 入栈, 出栈, 正序遍历,,逆序遍历

    直接上代码: class ArrayStack{ //用数组模拟栈 int maxSize; int[] stack; int top = -1;//表示栈顶 public ArrayStack(in ...

  7. PageHelper--Mybatis分页插件(ssm框架下的使用)

    1.导入PageHelper依赖 <!-- MyBatis 分页插件 --> <dependency> <groupId>com.github.pagehelper ...

  8. QT Dialog模态与非模态

    模态 // 创建对话框窗口 TestDialog* dlg = new TestDialog(this); // 阻塞程序的运行 dlg->exec(); 这样的话,当运行对话窗口的时候,会阻塞 ...

  9. .NET Worker Service 作为 Windows 服务运行及优雅退出改进

    上一篇文章我们了解了如何为 Worker Service 添加 Serilog 日志记录,今天我接着介绍一下如何将 Worker Service 作为 Windows 服务运行. 我曾经在前面一篇文章 ...

  10. 【NX二次开发】Block UI 选择对象

    属性说明 属性   类型   描述   常规           BlockID    String    控件ID    Enable    Logical    是否可操作    Group    ...