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

C++11中unique_ptr的使用

編輯:關於C++

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

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

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

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, (e.g. dereferencing, assignment) while providing additional memory management features.

std::unique_ptr is a smart pointer that retains sole ownership of an object through a pointer and destroys that object when the unique_ptr goes out of scope. No two unique_ptr instances can manage the same object.

The object is destroyed and its memory deallocated when either of the following happens: (1)、the managing unique_ptr object is destroyed; (2)、the managing unique_ptr object is assigned another pointer via operator= or reset().

A unique_ptr may alternatively own no object, in which case it is called empty.

Only non-const unique_ptr can transfer the ownership of the managed object to another unique_ptr. The lifetime of an object managed by const std::unique_ptr is limited to the scope in which the pointer was created.

std::unique_ptr may be constructed for an incomplete type T. Conversely, std::shared_ptr can't be constructed from a raw pointer to incomplete type, but can be destroyed where T is incomplete.

A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr, passed by value to a function, or used in any Standard Template Library (STL) algorithm that requires copies to be made. A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it.

When using unique_ptr, there can be at most one unique_ptr pointing at any one resource.When that unique_ptr is destroyed, the resource is automatically reclaimed.Because there can only be one unique_ptr to any resource, any attempt to make a copy of a unique_ptr will cause a compile-time error. However, unique_ptr can be moved using the new move semantics.

shared_ptr, allows for multiple pointers to point at a given resource. When the very last shared_ptr to a resource is destroyed, the resource will be deallocated. shared_ptr uses reference counting to track how many pointers refer to a resource, so you need to be careful not to introduce any reference cycles.

A unique_ptr is a container for a raw pointer, which the unique_ptr is said to own. A unique_ptr explicitly prevents copying of its contained pointer (as would happen with normal assignment), but the std::move function can be used to transfer ownership of the contained pointer to another unique_ptr. A unique_ptr cannot be copied because its copy constructor and assignment operators are explicitly deleted.

everything you can do with auto_ptr, unique_ptr will do as well.

There are two kinds of unique_ptr, one for scalars (i.e. non-arrays) and one for arrays:

(1)、unique_ptr can hold a scalar of type double;

(2)、unique_ptr can hold an array of double values with an unknown number of elements.

std::unique_ptr是C++11標准中用來取代std::auto_ptr的指針容器(在C++11中,auto_ptr被廢棄)。它不能與其它unique_ptr類型的指針對象共享所指對象的內存。這種”所有權”僅能夠通過標准庫的move函數來轉移。unique_ptr是一個刪除了拷貝構造函數、保留了移動構造函數的指針封裝類型。

一個unique_ptr"擁有"它所指向的對象。與shared_ptr不同,某個時刻只能有一個unique_ptr指向一個給定對象。當unique_ptr被銷毀時,它所指向的對象也被銷毀。與shared_ptr不同,在C++11中,沒有類似make_shared的標准庫函數返回一個unique_ptr。當定義一個unique_ptr時,需要將其綁定到一個new返回的指針上。類似shared_ptr,初始化unique_ptr必須采用直接初始化形式。由於一個unique_ptr擁有它指向的對象,因此unique_ptr不支持普通的拷貝或賦值操作。雖然不能拷貝或賦值unique_ptr,但可以通過調用release或reset將指針的所有權從一個(非const)unique_ptr轉移給另一個unique。

調用release會切斷unique_ptr和它原來管理的對象間的聯系。release返回的指針通過被用來初始化另一個智能指針或給另一個智能指針賦值。如果不用另一個智能指針來保存release返回的指針,程序就要負責資源的釋放。

不能拷貝unique_ptr的規則有一個例外:我們可以拷貝或賦值一個將要被銷毀的unique_ptr,最常見的例子是從函數返回一個unique_ptr。

類似shared_ptr,unique_ptr默認情況下用delete釋放它指向的對象。與shared_ptr一樣,可以重載一個unique_ptr中默認的刪除器。但是,unique_ptr管理刪除器的方式與shared_ptr不同。

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

\

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

 

#include "unique_ptr.hpp"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

///////////////////////////////////////////////////////
// reference: http://en.cppreference.com/w/cpp/memory/unique_ptr
struct Foo
{
	Foo()      { std::cout << "Foo::Foo\n"; }
	~Foo()     { std::cout << "Foo::~Foo\n"; }
	void bar() { std::cout << "Foo::bar\n"; }
};

void f(const Foo &)
{
	std::cout << "f(const Foo&)\n";
}

int test_unique_ptr1()
{
	std::unique_ptr p1(new Foo);  // p1 owns Foo
	if (p1) p1->bar();

	{
		std::unique_ptr p2(std::move(p1));  // now p2 owns Foo
		f(*p2);

		p1 = std::move(p2);  // ownership returns to p1
		std::cout << "destroying p2...\n";
	}

	if (p1) p1->bar();

	// Foo instance is destroyed when p1 goes out of scope

	return 0;
}

//////////////////////////////////////////////////////
// reference: http://www.cplusplus.com/reference/memory/unique_ptr/unique_ptr/
int test_unique_ptr2()
{
	std::default_delete d;
	std::unique_ptr u1;
	std::unique_ptr u2(nullptr);
	std::unique_ptr u3(new int);
	std::unique_ptr u4(new int, d);
	std::unique_ptr u5(new int, std::default_delete());
	std::unique_ptr u6(std::move(u5));
	std::unique_ptr u7(std::move(u6));
	std::unique_ptr u8(std::auto_ptr(new int));

	std::cout << "u1: " << (u1 ? "not null" : "null") << '\n';
	std::cout << "u2: " << (u2 ? "not null" : "null") << '\n';
	std::cout << "u3: " << (u3 ? "not null" : "null") << '\n';
	std::cout << "u4: " << (u4 ? "not null" : "null") << '\n';
	std::cout << "u5: " << (u5 ? "not null" : "null") << '\n';
	std::cout << "u6: " << (u6 ? "not null" : "null") << '\n';
	std::cout << "u7: " << (u7 ? "not null" : "null") << '\n';
	std::cout << "u8: " << (u8 ? "not null" : "null") << '\n';

	return 0;
}

//////////////////////////////////////////////////////
// reference: http://eli.thegreenplace.net/2012/06/20/c11-using-unique_ptr-with-standard-library-containers
struct Foo_0 {
	Foo_0() { std::cerr << "Foo_0 [" << this << "] constructed\n"; }
	virtual ~Foo_0() { std::cerr << "Foo_0 [" << this << "] destructed\n"; }
};

void sink(std::unique_ptr p) {
	std::cerr << "Sink owns Foo_0 [" << p.get() << "]\n";
}

std::unique_ptr source() {
	std::cerr << "Creating Foo_0 in source\n";
	return std::unique_ptr(new Foo_0);
}

int test_unique_ptr3()
{
	std::cerr << "Calling source\n";
	std::unique_ptr pmain = source();  // Can also be written as
	// auto pmain = source();

	std::cerr << "Now pmain owns Foo [" << pmain.get() << "]\n";
	std::cerr << "Passing it to sink\n";
	// sink(pmain);                    // ERROR! can't copy unique_ptr
	sink(move(pmain));              // OK: can move it!

	std::cerr << "Main done\n";
	return 0;
}

////////////////////////////////////////////////////
// reference: http://www.codeguru.com/cpp/article.php/c17775/The-Smart-Pointer-That-Makes-Your-C-Applications-Safer--stduniqueptr.htm
void func(int*)
{

}

int test_unique_ptr4()
{
	// default construction
	std::unique_ptr up; //creates an empty object

	// initialize with an argument
	std::unique_ptr uptr(new int(3));
	double *pd = new double;
	std::unique_ptr uptr2(pd);
	// overloaded * and ->
	*uptr2 = 23.5;
	std::unique_ptr ups(new std::string("hello"));
	int len = ups->size();

	// Reset() releases the owned resource and optionally acquires a new resource:
	uptr2.reset(new double); //delete pd and acquire a new pointer
	uptr2.reset(); //delete the pointer acquired by the previous reset() call

	// If you need to access the owned pointer directly use get()
	func(uptr.get());

	// Unique_ptr has implicit conversion to bool.
	// This lets you use unique_ptr object in Boolean expressions such as this:
	if (ups) //implicit conversion to bool
		std::cout << *ups << std::endl;
	else
		std::cout << "an empty smart pointer" << std::endl;

	// Array Support: Unique_ptr can store arrays as well.
	// A unique_ptr that owns an array defines an overloaded operator [].
	// Obviously, the * and -> operators are not available.
	// Additionally, the default deleter calls delete[] instead of delete:
	std::unique_ptr arrup(new int[5]);
	arrup[0] = 5;
	// std::cout << *arrup << std::endl; //error, operator * not defined 

	// Compatibility with Containers and Algorithms
	// You can safely store unique_ptr in Standard Library containers and let algorithms manipulate sequences of unique_ptr objects.
	std::vector> vi;
	vi.push_back(std::unique_ptr(new int(0)));  // populate vector
	vi.push_back(std::unique_ptr(new int(3)));
	vi.push_back(std::unique_ptr(new int(2)));
	std::sort(vi.begin(), vi.end());  // result: {0, 2, 3}

	return 0;
}

GitHub:https://github.com/fengbingchun/Messy_Test
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved