智能指針 - shared_ptr

簡介

可以獲取一個指針的所有權,且可以共享該所有權。

當最後一個屬主釋放掉指針的所有權時,該屬主將負責指針所指空間的釋放工作。

屬主釋放所有權的方式:

  • 屬主被銷燬(析構);
  • 屬主通過賦值操作改變了它的值;
  • 屬主顯式調用 share_ptr::reset 函數。

共享所有權的方式:

  • 通過拷貝另一個屬主;

注意:如果兩個 shared_ptr 通過同一個原始指針(非 shared_ptr) 構造,則它們將同時擁有該指針,而非共享該指針!當一個 shared_ptr 釋放掉原始指針指向的內存時,另一個 shared_ptr 將指向無效的空間!!且會發生 double free 錯誤。

#include <iostream>
#include <memory>

int main() {
	
	int* p = new int(4);

	std::shared_ptr<int> sp1 = std::shared_ptr<int>(p);
    
    // 不行!!
	std::shared_ptr<int> sp2 = std::shared_ptr<int>(p);

	return 0;
}

一個 shared_ptr 和兩個指針關聯:

  • owned pointer:多個 shared_ptr 間共享的那個指針。
  • stored pointer:解引用指針時,將獲取這個指針指向的內容。

通常情況下,owned pointer = stored pointer 。兩者也可以不同(使用 aliasing 構造函數),如:owned pointer = &obj 且 stored pointer = &obj.member 。

構造

default (1) constexpr shared_ptr() noexcept;
from null pointer (2) constexpr shared_ptr(nullptr_t) : shared_ptr() {}
from pointer (3) template <class U> explicit shared_ptr (U* p);
with deleter (4) template <class U, class D> shared_ptr (U* p, D del);

template <class D> shared_ptr (nullptr_t p, D del);
with allocator (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);
copy (6) shared_ptr (const shared_ptr& x) noexcept;

template <class U> shared_ptr (const shared_ptr<U>& x) noexcept;
copy from weak (7) template <class U> explicit shared_ptr (const weak_ptr<U>& x);
move (8) shared_ptr (shared_ptr&& x) noexcept;

template <class U> shared_ptr (shared_ptr<U>&& x) noexcept;
move from managed (9) template <class U> shared_ptr (auto_ptr<U>&& x);

template <class U, class D> shared_ptr (unique_ptr<U,D>&& x);
aliasing (10) template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;

形式(10)中,shared_ptr 的 owned pointer 爲 x 共享的指針,stored pointer 爲 p 。

#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;
}
use_count:
p1: 0
p2: 0
p3: 1
p4: 1
p5: 2
p6: 0
p7: 2
p8: 1
p9: 2
使用 new 分配內存 template <class T, class… Args> shared_ptr<T> make_shared (Args&&… args);
使用指定的 allocator 分配內存 template <class T, class Alloc, class… Args> shared_ptr<T> allocate_shared (const Alloc& alloc, Args&&… args);

使用 args 構造一個類型爲 T 的對象,並返回一個 shared_ptr,其中包含 owned pointer 和 stored pointer 指向構造的對象。

// make_shared 

#include <iostream>
#include <memory>

int main () {

  std::shared_ptr<int> foo = std::make_shared<int> (10);
  // same as:
  std::shared_ptr<int> foo2 (new int(10));

  auto bar = std::make_shared<int> (20);

  auto baz = std::make_shared<std::pair<int,int>> (30,40);

  std::cout << "*foo: " << *foo << '\n';
  std::cout << "*bar: " << *bar << '\n';
  std::cout << "*baz: " << baz->first << ' ' << baz->second << '\n';

  return 0;
}
*foo: 10
*bar: 20
*baz: 30 40
// allocate_shared 

#include <iostream>
#include <memory>

int main () {
  std::allocator<int> alloc;    // the default allocator for int
  std::default_delete<int> del; // the default deleter for int

  std::shared_ptr<int> foo = std::allocate_shared<int> (alloc,10);

  auto bar = std::allocate_shared<int> (alloc,20);

  auto baz = std::allocate_shared<std::pair<int,int>> (alloc,30,40);

  std::cout << "*foo: " << *foo << '\n';
  std::cout << "*bar: " << *bar << '\n';
  std::cout << "*baz: " << baz->first << ' ' << baz->second << '\n';

  return 0;
}
*foo: 10
*bar: 20
*baz: 30 40

賦值

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);
#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;
}

重置

(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);

形式(1)使 shared_ptr 變爲空(empty 不是 null)。其他形式則改變其共享的指針。

#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;
}

解引用

* 運算符 element_type& operator*() const noexcept;
-> 運算符 element_type* operator->() const noexcept;

返回 stored pointer 所指向的內容。

#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;
}
foo: 200
bar: 100
#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;
}
foo: 10 20
bar: 10 20

交換

成員函數 void swap (shared_ptr& x) noexcept;
非成員函數 template <class T> void swap (shared_ptr<T>& x, shared_ptr<T>& y) noexcept;

交換兩者的所有權。

#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;
}
*foo: 20
*bar: 10
#include <iostream>
#include <memory>

int main () {
  std::shared_ptr<int> foo (new int(10));
  std::shared_ptr<int> bar (new int(20));

  swap(foo,bar);

  std::cout << "foo: " << *foo << '\n';
  std::cout << "bar: " << *bar << '\n';

  return 0;
}
foo: 20
bar: 10

類型轉換

static_cast<T*>(sp.get()) 有效時纔可以 template <class T, class U> shared_ptr<T> static_pointer_cast (const shared_ptr<U>& sp) noexcept;
dynamic_cast<T*>(sp.get()) 有效時纔可以 template <class T, class U> shared_ptr<T> dynamic_pointer_cast (const shared_ptr<U>& sp) noexcept;
const_cast<T*>(sp.get()) 有效時纔可以 template <class T, class U> shared_ptr<T> const_pointer_cast (const shared_ptr<U>& sp) noexcept;

返回的是 sp 的拷貝,意味着屬主數加一。

// static_pointer_cast

#include <iostream>
#include <memory>

struct A {
  static const char* static_type;
  const char* dynamic_type;
  A() { dynamic_type = static_type; }
};
struct B: A {
  static const char* static_type;
  B() { dynamic_type = static_type; }
};

const char* A::static_type = "class A";
const char* B::static_type = "class B";

int main () {
  std::shared_ptr<A> foo;
  std::shared_ptr<B> bar;

  foo = std::make_shared<A>();

  // cast of potentially incomplete object, but ok as a static cast:
  bar = std::static_pointer_cast<B>(foo);

  std::cout << "foo's static  type: " << foo->static_type << '\n';
  std::cout << "foo's dynamic type: " << foo->dynamic_type << '\n';
  std::cout << "bar's static  type: " << bar->static_type << '\n';
  std::cout << "bar's dynamic type: " << bar->dynamic_type << '\n';

  return 0;
}
foo's static  type: class A
foo's dynamic type: class A
bar's static  type: class B
bar's dynamic type: class A
// dynamic_pointer_cast

#include <iostream>
#include <memory>

struct A {
  static const char* static_type;
  const char* dynamic_type;
  A() { dynamic_type = static_type; }
};
struct B: A {
  static const char* static_type;
  B() { dynamic_type = static_type; }
};

const char* A::static_type = "class A";
const char* B::static_type = "class B";

int main () {
  std::shared_ptr<A> foo;
  std::shared_ptr<B> bar;

  bar = std::make_shared<B>();

  foo = std::dynamic_pointer_cast<A>(bar);

  std::cout << "foo's static  type: " << foo->static_type << '\n';
  std::cout << "foo's dynamic type: " << foo->dynamic_type << '\n';
  std::cout << "bar's static  type: " << bar->static_type << '\n';
  std::cout << "bar's dynamic type: " << bar->dynamic_type << '\n';

  return 0;
}
 Edit & Run

foo's static  type: class A
foo's dynamic type: class B
bar's static  type: class B
bar's dynamic type: class B
// const_pointer_cast

#include <iostream>
#include <memory>

int main() {
	std::shared_ptr<const int> foo;
	std::shared_ptr<int> bar;

	foo = std::make_shared<const int>(10);

	bar = std::const_pointer_cast<int>(foo);

	std::cout << "*foo: " << *foo << '\n';
	
	*bar = 20;
	std::cout << "*foo: " << *foo << '\n';

	return 0;
}
*foo: 10
*foo: 20

其他操作

獲取 stored pointer

element_type* get() const noexcept;
#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;
}
a and p point to the same location
10
10
10

獲取屬主數

long int use_count() const noexcept;

是否唯一

bool unique() const noexcept;

等價於 use_count() == 1 。

轉換爲布爾值

explicit operator bool() const noexcept;

stored pointer 爲 null -> false;不爲 null -> true 。

#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;
}
foo is null
bar points to 34

關係運算符

支持 == , !=, < , <= , > , >= 。比較的是兩者的 stored pointer 。

#include <iostream>
#include <memory>

int main () {
  std::shared_ptr<int> a,b,c,d;

  a = std::make_shared<int> (10);
  b = std::make_shared<int> (10);
  c = b;

  std::cout << "comparisons:\n" << std::boolalpha;

  std::cout << "a == b: " << (a==b) << '\n';
  std::cout << "b == c: " << (b==c) << '\n';
  std::cout << "c == d: " << (c==d) << '\n';

  std::cout << "a != nullptr: " << (a!=nullptr) << '\n';
  std::cout << "b != nullptr: " << (b!=nullptr) << '\n';
  std::cout << "c != nullptr: " << (c!=nullptr) << '\n';
  std::cout << "d != nullptr: " << (d!=nullptr) << '\n';

  return 0;
}
comparisons:
a == b: false
b == c: true
c == d: false
a != nullptr: true
b != nullptr: true
c != nullptr: true
d != nullptr: false
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章