智能指針 - weak_ptr

簡介

將一個 shared_ptr 賦值給一個 weak_ptr 類型的變量時,該 weak_ptr 便屬於 該 shared_ptr 所屬的 owing group 。但與 shared_ptr不同的是,weak_ptr 不會獲取指針的所有權,亦即該指針的屬主數並不會加一。

其存在的目的是爲了打破 shared_ptr 造成的循環引用。如:

#include <iostream>
#include <memory>
using namespace std;

struct Node {
	int value;
	shared_ptr<Node> parent, left, right;

	Node(int value, shared_ptr<Node> parent, shared_ptr<Node> left, shared_ptr<Node> right) {
		this->value = value;
		this->parent = parent;
		this->left = left;
		this->right = right;
	}

	~Node() {
		cout << "delete " << value << endl;
	}
};

int main() {
	shared_ptr<Node> root = make_shared<Node>(1, nullptr, nullptr, nullptr);
	// 孩子節點引用父節點
	shared_ptr<Node> left = make_shared<Node>(2, root, nullptr, nullptr);
	shared_ptr<Node> right = make_shared<Node>(3, root, nullptr, nullptr);

	// 父節點引用子節點 -> 循環引用,誰都死不了,內存無法釋放!
	root->left = left;
	root->right = right;

	return 0;
}

什麼都不輸出。當 left/right 離開作用域時,其 stored pointer 的屬主數減一,變爲了1(不爲 0),所以它指向的 Node 對象無法被銷燬。當 root 離開作用域時,其 stored pointer 的屬主數減一,變爲了 2 ,所以它指向的 Node 對象也無法被銷燬。

改用 weak_ptr:

#include <iostream>
#include <memory>
using namespace std;

struct Node {
	int value;
	weak_ptr<Node> parent;
	shared_ptr<Node> left, right;

	Node(int value, weak_ptr<Node> parent, shared_ptr<Node> left, shared_ptr<Node> right) {
		this->value = value;
		this->parent = parent;
		this->left = left;
		this->right = right;
	}

	~Node() {
		cout << "delete " << value << endl;
	}
};

int main() {
	shared_ptr<Node> root = make_shared<Node>(1, weak_ptr<Node>(), nullptr, nullptr);

	shared_ptr<Node> left = make_shared<Node>(2, root, nullptr, nullptr);
	shared_ptr<Node> right = make_shared<Node>(3, root, nullptr, nullptr);

	root->left = left;
	root->right = right;
	
	return 0;
}
delete 1
delete 3
delete 2

構造

default (1) constexpr weak_ptr() noexcept;
copy (2) weak_ptr (const weak_ptr& x) noexcept;

template <class U> weak_ptr (const weak_ptr<U>& x) noexcept;
from shared_ptr (3) template <class U> weak_ptr (const shared_ptr<U>& x) noexcept;
move (4) weak_ptr (weak_ptr&& x) noexcept;

template <class U> weak_ptr (weak_ptr<U>&& x) noexcept;

注意:無法使用 nullptr 構造 weak_ptr !

// weak_ptr constructor example

#include <iostream>
#include <memory>

struct C {int* data;};

int main () {
  std::shared_ptr<int> sp (new int);

  std::weak_ptr<int> wp1;
  std::weak_ptr<int> wp2 (wp1);
  std::weak_ptr<int> wp3 (sp);

  std::cout << "use_count:\n";
  std::cout << "wp1: " << wp1.use_count() << '\n';
  std::cout << "wp2: " << wp2.use_count() << '\n';
  std::cout << "wp3: " << wp3.use_count() << '\n';
  return 0;
}
use_count:
wp1: 0
wp2: 0
wp3: 1

賦值

copy (1) weak_ptr& operator= (const weak_ptr& x) noexcept;

template <class U> weak_ptr& operator= (const weak_ptr<U>& x) noexcept;
from shared_ptr (2) template <class U> weak_ptr& operator= (const shared_ptr<U>& x) noexcept;
move (3) weak_ptr& operator= (weak_ptr&& x) noexcept;

template <class U> weak_ptr& operator= (weak_ptr<U>&& x) noexcept;

注意:無法將 nullptr 賦值給 weak_ptr !

// weak_ptr::operator= example

#include <iostream>
#include <memory>

int main () {
  std::shared_ptr<int> sp1,sp2;
  std::weak_ptr<int> wp;
                                       // sharing group:
                                       // --------------
  sp1 = std::make_shared<int> (10);    // sp1
  wp = sp1;                            // sp1, wp

  sp2 = wp.lock();                     // sp1, wp, sp2
  sp1.reset();                         //      wp, sp2

  sp1 = wp.lock();                     // sp1, wp, sp2

  std::cout << "*sp1: " << *sp1 << '\n';
  std::cout << "*sp2: " << *sp2 << '\n';

  return 0;
}
*sp1: 10
*sp2: 10

重置

void reset() noexcept;

置爲 empty 。

// weak_ptr::reset example

#include <iostream>
#include <memory>

int main () {
  std::shared_ptr<int> sp (new int(10));

  std::weak_ptr<int> wp(sp);

  std::cout << "1. wp " << (wp.expired()?"is":"is not") << " expired\n";

  wp.reset();

  std::cout << "2. wp " << (wp.expired()?"is":"is not") << " expired\n";

  return 0;
}
1. wp is not expired
2. wp is expired

升級爲 shared_ptr

bool expired() const noexcept;

如果該 weak_ptr 爲 empty 、或 owner group 中已經沒有 shared_ptr ,則返回 true 。

// weak_ptr::expired example

#include <iostream>
#include <memory>

int main () {
  std::shared_ptr<int> sp (new int(10));

  std::weak_ptr<int> wp(sp);

  std::cout << "1. wp " << (wp.expired()?"is":"is not") << " expired\n";

  wp.reset();

  std::cout << "2. wp " << (wp.expired()?"is":"is not") << " expired\n";

  return 0;
}
1. wp is not expired
2. wp is expired
shared_ptr<element_type> lock() const noexcept;

如果還未過期,則返回關聯的 shared_ptr ;如果已經過期,則返回一個 empty 的 shared_ptr 。(原子操作)

// weak_ptr::lock example

#include <iostream>
#include <memory>

int main () {
  std::shared_ptr<int> sp1,sp2;
  std::weak_ptr<int> wp;
                                       // sharing group:
                                       // --------------
  sp1 = std::make_shared<int> (20);    // sp1
  wp = sp1;                            // sp1, wp

  sp2 = wp.lock();                     // sp1, wp, sp2
  sp1.reset();                         //      wp, sp2

  sp1 = wp.lock();                     // sp1, wp, sp2

  std::cout << "*sp1: " << *sp1 << '\n';
  std::cout << "*sp2: " << *sp2 << '\n';

  return 0;
}
*sp1: 20
*sp2: 20

交換

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

交換所屬的組。

// weak_ptr::swap example
#include <iostream>
#include <memory>

int main () {
  std::shared_ptr<int> sp1 (new int(10));
  std::shared_ptr<int> sp2 (new int(20));

  std::weak_ptr<int> wp1(sp1);
  std::weak_ptr<int> wp2(sp2);

  wp1.swap(wp2);

  std::cout << "sp1 -> " << *sp1 << '\n';
  std::cout << "sp2 -> " << *sp2 << '\n';
  std::cout << "wp1 -> " << *wp1.lock() << '\n';
  std::cout << "wp2 -> " << *wp2.lock() << '\n';

  return 0;
}
sp1 -> 10
sp2 -> 20
wp1 -> 20
wp2 -> 10
// weak_ptr swap specialization
#include <iostream>
#include <memory>

int main () {
  std::shared_ptr<int> sp1 (new int(10));
  std::shared_ptr<int> sp2 (new int(20));

  std::weak_ptr<int> wp1(sp1);
  std::weak_ptr<int> wp2(sp2);

  swap(wp1,wp2);

  std::cout << "sp1 -> " << *sp1 << '\n';
  std::cout << "sp2 -> " << *sp2 << '\n';
  std::cout << "wp1 -> " << *wp1.lock() << '\n';
  std::cout << "wp2 -> " << *wp2.lock() << '\n';

  return 0;
}
foo: 20
bar: 10

其他操作

long int use_count() const noexcept;

獲取所在的 owing group 中 shared_ptr 的數目。

不支持比較!

發佈了118 篇原創文章 · 獲贊 18 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章