C++基础之内存管理(一)指针——shared_ptr详解

头文件
#include <memory>

std::shared_ptr (C++11)

模板类定义

template <class T> class shared_ptr;

类说明

共享指针。管理一个保存指针的内存区域,提供有限的垃圾回收机制,可能会在多个对象之间共享该管理权。

shared_ptr类型的对象可以取得一个指针的所有权,并且可以分享该所有权:一旦他们取得了一个指针的所有权,那么当该指针的这一组所有者中最后一个释放所有权的需要负责释放该指针

shared_ptr对象在他们被销毁的同时释放对指针的所有权,或者是通过赋值操作或者调用 shared_ptr::reset函数来改变shared_ptr对象的值的同时释放所有权。一旦所有共享指针所有权的shared_ptr对象都释放了所有权,被监管的对象就会被删除(通常是调用::delete,但是也可以在构造函数中制定一个不同的删除方法)

shared_ptr对象只能通过复制其值来共享所有权:如果两个shared_ptr对象是从同一个(非共享的)指针构造(或生成)的,则它们都将拥有该指针的所有权而不共享它的所有权,当其中一个释放指针(删除其托管对象)会使另一个shared_ptr对象指向无效位置,这样会导致潜在的访问问题。(这里主要是说共享一份所有权和分别拥有所有权的不同,构造出来的每个shared_ptr对象都有所有权,所有权相当于有多个;使用复制只是共享一份所有权)

另外,shared_ptr对象可以在指向另一个对象的同时共享另一个指针的所有权。这个功能被称作别名(参见构造函数),通常用在已经拥有一个对象所有权,然后用指针指向该对象的成员对象。因此一个shared_ptr可能有两个相关的指针:

  • 一个存储的指针,即它所指向的指针,可以使用运算符*进行解引用访问其中的内容
  • 一个拥有的指针(可能是共享的),是拥有所有权的群组在某时刻需要负责释放的指针,它也被当做计数器用

通常,这两个指针指向同一个对象,但是别名版本的shared_ptr对象(使用alias构造函数构造的对象或者这种对象的拷贝)可能会指向不同的对象。

一个shared_ptr如果不拥有任何指针的所有权称为empty shared_ptr。如果shared_ptr不指向任何对象(存储指针是空)称为null shared_ptr,并且不能够解引用。请注意,empty shared_ptr不一定是null shared_ptr,null shared_ptr也不一定是empty shared_ptr。

shared_ptr对象通过运算符*和->可以实现访问所指向的对象,从而复制了有限的指针功能。出于安全原因,它们不支持指针运算

一个相关的类weak_ptr可以与shared_ptr对象共享指针而不必拥有它们。

模板参数

T:托管对象的类型,别名为成员类型element_type。

成员类型

下面是shared_ptr成员类型的别名。
在这里插入图片描述

成员函数

构造函数

函数原型
在这里插入图片描述根据使用的函数签名构建一个shared_ptr对象:
默认构造函数(1)和(2)
构造一个empty对象(注意不是null,不拥有任何指针,计数为0)

使用指针构造(3)
构造的对象拥有p的所有权,使用计数设置为1。

使用指针+删除器deleter构造(4)
与(3)相同,但对象也拥有deleter del的所有权(如果在某个时刻需要删除p,则使用它来完成)。

使用指针+删除器+分配器allocator构造(5)
与(4)相同,但是内部所需的所有内存都使用alloc来申请(只保留alloc的副本,不拥有其所有权)。

拷贝构造函数(6)
如果X不为空的话,构造的对象会分享X管理目标的所有权, 使用计数加1。
如果X为空的话,则会构造一个empty对象(就像默认构造函数)。

从weak_ptr拷贝构造(7)
与(6)相同,只是如果X管理的指针已经无效的话会抛出bad_weak_ptr的异常。

移动构造函数(8)
新的对象将获取由x管理的内容,包括它拥有的指针。x变成一个空对象(就像默认构造函数构造出来的一样)。

使用其他类型的托管指针移动构造(9)
新创建的对象获取由x管理的内容并将使用计数设置为1。转让指针所有权的对象变为空,自动失去指针的所有权。

别名构造函数(10)
除存储的指针为p外,与(6)相同。该对象不拥有p,并且将不管理其存储。 相反,它共同拥有x的托管对象,并算作x的另一种用法。 它还会在释放时删除x的指针(而不是p)。它可以用来指向已经被管理的对象的成员。(也就是说新创建的对象等同于X)

参数
p: 要被新对象接管其所有权的指针。此指针值不应已由任何其他托管指针管理(即,此值不应使用其他托管指针的get函数的返回值)。U应隐式转换为T(其中T是shared_ptr模板参数)。

del: 用于释放所属对象的Deleter对象。这应该是一个可调用的对象,将指向T的指针作为函数调用的参数(其中T是shared_ptr的模板参数)。

alloc: 用于分配/释放内部存储的分配器对象。

x: 托管指针类型的对象。U应隐式转换为T(其中T是shared_ptr的模板参数)。

Example

// shared_ptr constructor example
#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;
}

Output:

use_count:
p1: 0
p2: 0
p3: 1
p4: 1
p5: 2
p6: 0
p7: 2
p8: 1
p9: 2

析构函数

函数原型

~shared_ptr();

功能
销毁shared_ptr对象。但是,在此之前,它可能会产生以下额外影响,具体取决于成员使用计数的值:

  • 如果use_count大于1(即,该对象与其他shared_ptr对象共享其托管对象的所有权):与它共享所有权的其他对象的使用计数减少1。
  • 如果use_count为1(即,该对象是托管指针的唯一所有者):将删除其拥有的指针所指向的对象(如果shared_ptr对象是用指定deleter构造的,则调用此函数;否则,函数使用运算符delete)。
  • 如果use_count为零(即对象为空),则该析构函数没有额外影响。

参数

返回值

Example

// shared_ptr destructor example
#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]
}

Output:

use_count: 1
[deleter_called]

std::shared_ptr::operator=

函数原型

copy (1)	
shared_ptr& operator= (const shared_ptr& x) noexcept;
template <class U> shared_ptr& operator= (const shared_ptr<U>& x) noexcept;
move (2)	
shared_ptr& operator= (shared_ptr&& x) noexcept;
template <class U> shared_ptr& operator= (shared_ptr<U>&& x) noexcept;
move from (3)	
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赋值。
拷贝赋值(1), 将对象添加为x管理指针的共享所有者,从而增加其use_count。
移动赋值(2)将所有权从x转移到被赋值的shared_ptr对象,而不改变use_count。x变成一个空的shared_ptr(就像默认构造的一样)。
同样,来自其他托管指针类型(3)的移动分配也会转移所有权,并将use_count初始化为1。

此外,在上述几种赋值操作中,调用此函数有相同的副作用就像shared_ptr的析构函数在其值更改之前被调用(包括删除托管对象,如果该共享的构造函数是唯一的)。
不能将指针的值直接赋值给shared_ptr对象。您可以使用make_shared或成员reset来实现。
参数
x: 托管指针类型的对象。U应隐式转换为T(其中T是共享的模板参数)。
返回值
*this
Example

// shared_ptr::operator= example
#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;
}

Output:

*foo: 30
*bar: 20

std::shared_ptr::swap

函数原型

void swap (shared_ptr& x) noexcept;

功能
交换内容。交换shared_ptr对象的内容与x的内容,在它们之间转移所有托管对象的所有权,而销毁或更改它们的使用计数。
参数
x: 另一个相同类型 的共享对象(即,具有相同的类模板参数T)。
返回值

Example

// shared_ptr::swap example
#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;
}

Output:

*foo: 20
*bar: 10

std::shared_ptr::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);

功能
重新设置shared_ptr管理的指针。对于函数签名(1),对象变为空(如同默认构造)。
在所有其他情况下,shared-ptr会获取p的所有权,并把使用计数设置为1,并把可选的del和/或alloc分别作为deleter和allocator。
此外,对该函数的调用具有相同的副作用,就像在shared ptr的析构函数在其值更改之前被调用一样(如果这个共享的析构函数是唯一的,则包括删除托管对象)。
参数
p: 其所有权由对象接管的指针。通常,此指针不应该已经由任何其他托管指针管理(即,此值不应来自调用托管指针上的成员函数get)。U应隐式转换为T(其中T是共享的模板参数)。
del: 用于释放所属对象的Deleter对象。这应该是一个可调用的对象,将指向T的指针作为函数调用的参数(其中T是shared的模板参数)。
alloc: 用于分配/释放内部存储的分配器对象。
返回值

Example

// shared_ptr::reset example
#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;
}

Output:

10
20

std::shared_ptr::get

函数原型

element_type* get() const noexcept;

功能
获取保存的指针。存储的指针指向shared_ptr对象解引用的对象,该对象通常与其拥有的指针相同。
如果shared_ptr对象是别名(即别名构造的对象及其副本),则存储的指针(即此函数返回的指针)可能不是其保存的指针(即对象销毁时删除的指针)。
参数

返回值
存储的指针。
element_type是成员类型,是shared_ptr的模板参数(T)的别名。
Example

// shared_ptr::get example
#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;
}

Output:

a and p point to the same location
10
10
10

std::shared_ptr::operator*

函数原型

element_type& operator*() const noexcept;

功能
将shared_ptr对象进行解引用。返回存储指针所指向的对象的引用。
相当于:*get()。
如果shared_ptr的模板参数为void,则是否定义此成员函数以及在这种情况下返回的类型取决于平台和编译器。
参数

返回值
指向对象的引用。
element_type是一个成员类型,定义为shared_ptr的模板参数的别名。
Example

// shared_ptr::operator*
#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;
}

Output:

foo: 200
bar: 100

std::shared_ptr::operator->

函数原型

element_type* operator->() const noexcept;

功能
对对象成员进行解引用。主要用来访问多个成员中的一个,见例子
返回指向存储指针指向的对象的指针,以便访问其成员之一。
如果存储的指针是空指针,则不应调用此成员函数。
它返回与get()相同的值。有关详细信息,请参阅shared_ptr::get。
参数

返回值
指向shared_ptr管理的对象的指针。
element_type是一个成员类型,定义为shared_ptr的模板参数的别名。
Example

// shared_ptr::operator->
#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;
}

Output:

foo: 10 20
bar: 10 20

std::shared_ptr::use_count

函数原型

long int use_count() const noexcept;

功能
返回与此对象(包括它)共享同一指针所有权的shared_ptr对象数。
如果这是一个空的shared_ptr,则函数返回零。
库实现不需要对任何特定的所有者集进行计数,因此调用此函数可能执行效率比较低。如果要检查use_count是否为1,可以使用成员函数unique,这可能更快。
参数

返回值
在同一指针上共享所有权的对象数。

std::shared_ptr::unique

函数原型

bool unique() const noexcept;

功能
返回shared_ptr对象是否与其他shared_ptr对象共享其指针的所有权(即,它是唯一的)。
空指针从来不是唯一的(因为它们不拥有任何指针)。
如果shared_ptr对象是唯一的,则需负责在释放此所有权时删除其托管对象(请参见析构函数)。
此函数将返回与(use_count()==1)相同的结果,尽管它可能以更有效的方式返回。
参数

返回值
如果shared_ptr是唯一的,则为true,否则为false。
Example

// shared_ptr::unique
#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;
}

Output:

foo unique?
1: false
2: false
3: true

std::shared_ptr::operator bool

函数原型

explicit operator bool() const noexcept;

功能
返回存储的指针是否为空指针。
存储的指针指向shared_ptr对象解引用的对象,该对象通常与其拥有的指针相同(销毁时删除的指针)。如果共享对象是别名(即别名构造的对象及其副本),则它们可能不同。
函数返回的结果与get()!=0相同。
请注意,null的shared_ptr(即该函数返回false的指针)不一定是empy的shared ptr。别名可能拥有一些指针但指向null,或者所有者组甚至可能拥有null指针(请参见构造函数4和5)。
参数

返回值
如果shared_ptr是空指针,则返回false。
是的,否则。
Example

// example of shared_ptr::operator bool
#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;
}

Output:

foo is null
bar points to 34

std::shared_ptr::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前的对象是否遵循严格的按照所有者的弱顺序排列。
与重载运算符<不同,这种排序考虑了shared_ptr拥有的指针,而不是存储的指针,如果这两个对象都共享所有权,或者它们都是空的,甚至保存的指针不同,那么这两个对象被认为是等价的(即,无论操作数的顺序如何,此函数都返回false)。
如果shared_ptr对象是别名(别名构造的对象及其副本),则存储的指针(即共享的\u ptr对象取消引用的指针)可能不是拥有的指针(即,对象销毁时删除的指针)。
此函数由所有者调用以确定其结果。
参数
x: shared_ptr或weak_ptr对象。
返回值
如果该对象被认为与x不同,并且基于所有权以严格的弱顺序排在它前面,则为真。否则为假。
Example

// shared_ptr::owner_before
#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;
}

Output:

comparing a and b...
value-based: false
owner-based: true

Guess you like

Origin blog.csdn.net/itlilyer/article/details/108444937