程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++11中weak_ptr的使用

C++11中weak_ptr的使用

編輯:關於C++

在C++中,動態內存的管理是通過一對運算符來完成的:new,在動態內存中為對象分配空間並返回一個指向該對象的指針,可以選擇對對象進行初始化;delete,接受一個動態對象的指針,銷毀該對象,並釋放與之關聯的內存。

動態內存的使用很容易出問題,因為確保在正確的時間釋放內存是極其困難的。有時會忘記釋放內存,在這種情況下會產生內存洩露;有時在尚有指針引用內存的情況下就釋放了它,在這種情況下就會產生引用非法內存的指針。

為了更容易(同時也更安全)地使用動態內存,C++11標准庫提供了兩種智能指針(smart pointer)類型來管理動態對象。智能指針的行為類似常規指針,重要的區別是它負責自動釋放所指的對象。C++11標准庫提供的這兩種智能指針的區別在於管理底層指針的方式:shared_ptr允許多個指針指向同一個對象;unique_ptr則"獨占"所指向的對象。C++11標准庫還定義了一個名為weak_ptr的輔助類,它是一種弱引用,指向shared_ptr所管理的對象。這三種類型都定義在memory頭文件中。智能指針是模板類而不是指針。類似vector,智能指針也是模板,當創建一個智能指針時,必須提供額外的信息即指針可以指向的類型。默認初始化的智能指針中保存著一個空指針。智能指針的使用方式與普通指針類似。解引用一個智能指針返回它指向的對象。如果在一個條件判斷中使用智能指針,效果就是檢測它是否為空。

Pointer misuse can be a major source of bugs. Smart pointers prevent most situations of memory leaks by making the memory deallocation automatic. More generally, they make object destruction automatic: an object controlled by a smart pointer is automatically destroyed (finalized and then deallocated) when the last (or only) owner of an object is destroyed.

In C++, a smart pointer is implemented as a template class that mimics, by means of operator overloading, the behaviors of a traditional (raw) pointer, while providing additional memory management features.

std::weak_ptr is a smart pointer that holds a non-owning ("weak") reference to an object that is managed by std::shared_ptr. It must be converted to std::shared_ptr in order to access the referenced object.

std::weak_ptr models temporary ownership: when an object needs to be accessed only if it exists, and it may be deleted at any time by someone else, std::weak_ptr is used to track the object, and it is converted to std::shared_ptr to assume temporary ownership. If the original std::shared_ptr is destroyed at this time,the object's lifetime is extended until the temporary std::shared_ptr is destroyed as well.

In addition,std::weak_ptr is used to break circular references of std::shared_ptr.

weak_ptr is primarily useful in the rare cases where it is needed in order to break'circular references' – a problem with the use of reference counting in shared_ptr.

Sometimes an object must store a way to access the underlying object of a shared_ptr without causing the reference count to be incremented. Typically, this situation occurs when you have cyclic references between shared_ptr instances. The best design is to avoid shared ownership of pointers whenever you can. However, if you must have shared ownership of shared_ptr instances, avoid cyclic references between them. When cyclic references are unavoidable, or even preferable for some reason, use weak_ptr to give one or more of the owners a weak reference to another shared_ptr. By using a weak_ptr, you can create a shared_ptr that joins to an existing set of related instances, but only if the underlying memory resource is still valid. A weak_ptr itself does not participate in the reference counting, and therefore, it cannot prevent the reference count from going to zero. However, you can use a weak_ptr to try to obtain a new copy of the shared_ptr with which it was initialized. If the memory has already been deleted, a bad_weak_ptr exception is thrown. If the memory is still valid, the new shared pointer increments the reference count and guarantees that the memory will be valid as long as the shared_ptr variable stays in scope.

weak_ptr被設計為與shared_ptr共同工作,可以從一個shared_ptr或者另一個weak_ptr對象構造,獲得資源的觀測權。但weak_ptr沒有共享資源,它的構造不會引起指針引用計數的增加。同樣,在weak_ptr析構時也不會導致引用計數的減少,它只是一個靜靜地觀察者。weak_ptr沒有重載operator*和->,這是特意的,因為它不共享指針,不能操作資源,這是它弱的原因。但它可以使用一個非常重要的成員函數lock()從被觀測的shared_ptr獲得一個可用的shared_ptr對象,從而操作資源。

weak_ptr用於解決”引用計數”模型循環依賴問題,weak_ptr指向一個對象,並不增減該對象的引用計數器。weak_ptr用於配合shared_ptr使用,並不影響動態對象的生命周期,即其存在與否並不影響對象的引用計數器。weak_ptr並沒有重載operator->和operator *操作符,因此不可直接通過weak_ptr使用對象。weak_ptr提供了expired()與lock()成員函數,前者用於判斷weak_ptr指向的對象是否已被銷毀,後者返回其所指對象的shared_ptr智能指針(對象銷毀時返回”空”shared_ptr)。

智能指針是模板類而不是指針.

weak_ptr並沒有重載operator->和operator *操作符,因此不可直接通過weak_ptr使用對象,典型的用法是調用其lock函數來獲得shared_ptr示例,進而訪問原始對象。

weak_ptr是一種不控制所指向對象生存期的智能指針,它指向由一個shard_ptr管理的對象。將一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用計數。一旦最後一個指向對象的shared_ptr被銷毀,對象就會被釋放。即使有weak_ptr指向對象,對象也還是會被釋放。

當創建一個weak_ptr時,要用一個shared_ptr來初始化它。不能使用weak_ptr直接訪問對象,而必須調用lock。此函數檢查weak_ptr指向的對象是否仍存在。如果存在,lock返回一個指向共享對象的shared_ptr。與任何其它shared_ptr類似,只要此shared_ptr存在,它所指向的底層對象也就會一直存在。

下圖列出了weak_ptr支持的操作(來源於C++ Primer Fifth Edition 中文版):

\   下面是從其他文章中copy的測試代碼,詳細內容介紹可以參考對應的reference:
#include "weak_ptr.hpp"
#include 
#include 
#include 
#include 
#include 

///////////////////////////////////////////////////////
// reference: http://en.cppreference.com/w/cpp/memory/weak_ptr
std::weak_ptr gw;

void f()
{
	if (auto spt = gw.lock()) { // Has to be copied into a shared_ptr before usage
		std::cout << *spt << "\n";
	}
	else {
		std::cout << "gw is expired\n";
	}
}

int test_weak_ptr1()
{
	{
		auto sp = std::make_shared(42);
		gw = sp;

		f();
	}

	f();

	return 0;
}

/////////////////////////////////////////////////
// reference: http://stackoverflow.com/questions/12030650/when-is-stdweak-ptr-useful
int test_weak_ptr2()
{
	// OLD, problem with dangling pointer
	// PROBLEM: ref will point to undefined data!
	int* ptr = new int(10);
	int* ref = ptr;
	delete ptr;

	// NEW
	// SOLUTION: check expired() or lock() to determine if pointer is valid
	// empty definition
	std::shared_ptr sptr;

	// takes ownership of pointer
	sptr.reset(new int);
	*sptr = 10;

	// get pointer to data without taking ownership
	std::weak_ptr weak1 = sptr;

	// deletes managed object, acquires new pointer
	sptr.reset(new int);
	*sptr = 5;

	// get pointer to new data without taking ownership
	std::weak_ptr weak2 = sptr;

	// weak1 is expired!

	if (auto tmp = weak1.lock())
		std::cout << *tmp << '\n';
	else
		std::cout << "weak1 is expired\n";

	// weak2 points to new data (5)

	if (auto tmp = weak2.lock())
		std::cout << *tmp << '\n';
	else
		std::cout << "weak2 is expired\n";

	return 0;
}

//////////////////////////////////////////////////////
// reference: https://msdn.microsoft.com/en-us/library/hh279672.aspx
class Controller
{
public:
	int Num;
	std::string Status;
	std::vector> others;
	explicit Controller(int i) : Num(i), Status("On")
	{
		std::cout << "Creating Controller" << Num << std::endl;
	}

	~Controller()
	{
		std::cout << "Destroying Controller" << Num << std::endl;
	}

	// Demonstrates how to test whether the pointed-to memory still exists or not.
	void CheckStatuses() const
	{
		for_each(others.begin(), others.end(), [](std::weak_ptr wp) {
			try {
				auto p = wp.lock();
				std::cout << "Status of " << p->Num << " = " << p->Status << std::endl;
			}
			catch (std::bad_weak_ptr b) {
				std::cout << "Null object" << std::endl;
			}
		});
	}
};

void RunTest()
{
	std::vector> v;

	v.push_back(std::shared_ptr(new Controller(0)));
	v.push_back(std::shared_ptr(new Controller(1)));
	v.push_back(std::shared_ptr(new Controller(2)));
	v.push_back(std::shared_ptr(new Controller(3)));
	v.push_back(std::shared_ptr(new Controller(4)));

	// Each controller depends on all others not being deleted.
	// Give each controller a pointer to all the others. 
	for (int i = 0; i < v.size(); ++i) {
		for_each(v.begin(), v.end(), [v, i](std::shared_ptr p) {
			if (p->Num != i) {
				v[i]->others.push_back(std::weak_ptr(p));
				std::cout << "push_back to v[" << i << "]: " << p->Num << std::endl;
			}
		});
	}

	for_each(v.begin(), v.end(), [](std::shared_ptr& p) {
		std::cout << "use_count = " << p.use_count() << std::endl;
		p->CheckStatuses();
	});
}

int test_weak_ptr3()
{
	RunTest();
	std::cout << "Press any key" << std::endl;
	char ch;
	std::cin.getline(&ch, 1);

	return 0;
}

////////////////////////////////////////////////
// reference: https://oopscenities.net/2014/08/03/c-smart-pointers-part-5-weak_ptr/
struct Child;
struct Parent
{
	std::shared_ptr child;

	~Parent() { std::cout << "Bye Parent" << std::endl; }

	void hi() const { std::cout << "Hello" << std::endl; }
};

struct Child
{
	std::weak_ptr parent;
	//std::shared_ptr parent; // memory leak

	~Child() { std::cout << "Bye Child" << std::endl; }
};

int test_weak_ptr4()
{
	auto parent = std::make_shared();
	auto child = std::make_shared();

	parent->child = child;
	child->parent = parent;
	child->parent.lock()->hi();
	// child->parent->hi();

	return 0;
}

/////////////////////////////////////////////////////
// reference: http://thispointer.com/shared_ptr-binary-trees-and-the-problem-of-cyclic-references/
class Node
{
	int value;
public:
	std::shared_ptr leftPtr;
	std::shared_ptr rightPtr;
	// Just Changed the shared_ptr to weak_ptr
	std::weak_ptr parentPtr;
	Node(int val) : value(val)     {
		std::cout << "Contructor" << std::endl;
	}
	~Node()     {
		std::cout << "Destructor" << std::endl;
	}
};

int test_weak_ptr5()
{
	std::shared_ptr ptr = std::make_shared(4);
	ptr->leftPtr = std::make_shared(2);
	ptr->leftPtr->parentPtr = ptr;
	ptr->rightPtr = std::make_shared(5);
	ptr->rightPtr->parentPtr = ptr;
	std::cout << "ptr reference count = " << ptr.use_count() << std::endl;
	std::cout << "ptr->leftPtr reference count = " << ptr->leftPtr.use_count() << std::endl;
	std::cout << "ptr->rightPtr reference count = " << ptr->rightPtr.use_count() << std::endl;
	std::cout << "ptr->rightPtr->parentPtr reference count = " << ptr->rightPtr->parentPtr.lock().use_count() << std::endl;
	std::cout << "ptr->leftPtr->parentPtr reference count = " << ptr->leftPtr->parentPtr.lock().use_count() << std::endl;

	return 0;
}

GitHub:https://github.com/fengbingchun/Messy_Test

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved