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_ptr,std::shared_ptr,std::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的主要内容,如果未能解决你的问题,请参考以下文章