C/C++智能指针auto_ptr,share_ptr,unique_ptr

Posted mick_seu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++智能指针auto_ptr,share_ptr,unique_ptr相关的知识,希望对你有一定的参考价值。

《Effective C++》中提到,为了避免内存泄露,我们需要使用RAII(Resource Acquisition Is Initalization, "资源获得时机便是初始化时机"),即“以对象管理资源”,充分利用对象在离开其作用域时,析构函数将会被自动调用,在析构函数中释放其管理的资源。

注:如果在资源释放的过程中导致抛出异常,事情就变得有点棘手,我们需要借用《Effective C++》的条款8(别让异常逃离析构函数)来解决。


这里介绍几个常用的智能指针。三中指针的使用可以参见官方文档:std::auto_ptrstd::shared_ptrstd::unique_ptr

可以发现,对于一个智能指针 p,重载了 operator* 和 operator->,都用来访问被管理对象,给人一种使用原始指针的感觉,便于使用。其次,也可以通过 get() 函数直接获取原始指针。



一、std::auto_ptr(C++11弃用)

#include <iostream>
#include <memory>

class Box 
	public:
		Box()  std::cout << "Box()" << std::endl; 
		~Box()  std::cout << "~Box()" << std::endl; 
		void Show()  std::cout << "Show()" << std::endl; 
;


int main() 
	std::auto_ptr<Box> pb1(new Box);
	std::cout << pb1.get() << std::endl;
	pb1->Show();				// 使用 ->
	(*pb1).Show();				// 使用 *
	pb1.get()->Show();			// 使用 get()
	std::auto_ptr<Box> pb2 = pb1;		// =拷贝赋值后,指针所有权转移,一定注意!!!
	std::cout << pb1.get() << std::endl;
	std::cout << pb2.get() << std::endl;

	return 0;


输出如:

Box()
0x188fc20
Show()
Show()
Show()
0
0x188fc20
~Box()

auto_ptr对象被删除时,一定会释放掉它所管理的资源,所以让多个 auto_ptr 指向同时同一个对象是一件十分可怕的事,因为有可能被多次删除。为了预防这个问题,通过拷贝构造及拷贝赋值复制它们后,原 auto_ptr 管理的指针将会置为 NULL,而通过复制得到 新智能指针将获取资源的唯一所有权。这一诡异的复制行为以及"受auto_ptr管理的资源不能有多于一个的auto_ptr指向它",意味着 auto_ptr并不是最佳的管理动态分配资源的利器。所以已被弃用。




替代方案是“引用计数型智能指针”,这种智能指针持续追踪共有多少对象指向某笔资源,在无人指向它时自动删除该资源(当引用值为0时行为可重新定制)。如 std::shared_ptr

二、std::shared_ptr

#include <iostream>
#include <memory>

class Box 
	public:
		Box()  std::cout << "Box()" << std::endl; 
		~Box()  std::cout << "~Box()" << std::endl; 
		void Show()  std::cout << "Show()" << std::endl; 
;


int main() 
	std::shared_ptr<Box> pb1(new Box);
	std::cout << pb1.get() << std::endl;
	pb1->Show();
	(*pb1).Show();
	pb1.get()->Show();
	std::shared_ptr<Box> pb2 = pb1;
	std::cout << pb1.use_count() << std::endl;
	std::cout << pb1.get() << std::endl;
	std::cout << pb2.get() << std::endl;

	return 0;

输出如:

Box()
0x188fc20
Show()
Show()
Show()
2
0x188fc20
0x188fc20
~Box()

注意:只能通过复制构造或复制赋值其值给另一 shared_ptr ,将对象所有权与另一 shared_ptr 共享。用另一 shared_ptr 所占有的底层指针创建新的 shared_ptr 导致未定义行为。



三、std::unique_ptr

是用于取代c++98的 auto_ptr 的产物。在c++98的时候还没有移动语义(move semantics)的支持,因此对于auto_ptr的控制权转移的实现没有核心元素的支持,但是还是实现了auto_ptr 的移动语义,这样带来的问题是拷贝构造函数和拷贝赋值函数语义让人迷惑。在c++11当中有了移动语义,使用move()把unique_ptr传入函数,你就知道原先的unique_ptr已经失效了。移动语义本身就说明了这样的问题,比较坑爹的是标准描述是说对于move之后使用原来的内容是未定义行为,并非抛出异常,所以还是要靠人遵守规则。


unique_ptr 是一个独享所有权的智能指针,它提供了严格意义上的所有权,包括:
1、拥有它指向的对象
2、无法进行复制构造,无法进行复制赋值操作。即无法使两个unique_ptr指向同一个对象。但是可以进行移动构造和移动赋值操作
3、保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它指向的对象


举例如下:

#include <iostream>
#include <memory>

class Box 
	public:
		explicit Box(const std::string name):name_(name)  std::cout << "Box()" << std::endl; 
		~Box()  std::cout << "~Box()" << std::endl; 
		void Show()  std::cout << name_ << std::endl; 
	private:
		std::string name_;
;

std::unique_ptr<Box> pass_through(std::unique_ptr<Box> p) 
	return p;


int main() 
	std::unique_ptr<Box> pb1(new Box("hello"));			// Box()
	std::unique_ptr<Box> pb2(new Box("world"));			// Box()
	pb1->Show();							// hello
	pb2->Show();							// world

	pb2 = std::move(pb1);						// 不能直接 pb2 = pb1	~Box()
	pb2->Show();							// hello
	std::cout << pb1.get() << std::endl;				// 0

	pb1 = std::make_unique<Box>("world");				// Box()     make_unique<>() 可以生成新的 unique_ptr 对象
	pb1->Show();							// world

	pb1 = pass_through(std::move(pb2));				// ~Box()
	pb1->Show();							// hello
	
	Box *p = pb1.release();
	pb2.reset(p);
	pb2->Show();							// hello

	return 0;							// ~Box()


输出如下:

Box()
Box()
hello
world
~Box()
hello
0
Box()
world
~Box()
hello
hello
~Box()

unique_ptr 和 auto_ptr用法很相似,不过不能使用=直接赋值,应该使用std::move。unique_ptr 可以直接用与nullptr 比较来判断是否空指针,release、get、reset等用法也和auto_ptr一致,使用函数的返回值赋值时,可以直接使用=,这里使用 c++11 的移动语义特性。另外注意当把它当做参数传递给函数时(值传递,不包含引用传递),传实参时也要使用std::move,比foo(std::move(pb2))。

以上是关于C/C++智能指针auto_ptr,share_ptr,unique_ptr的主要内容,如果未能解决你的问题,请参考以下文章

C/C++智能指针

智能指针之 auto_ptr

c++中的智能指针auto_ptr解析

智能指针分析及auto_ptr源码

智能指针(auto_ptr)vc版

智能指针之auto_ptr和scoped_ptr