smart pointer是一种abstract data type,它可以模仿指针的行为,而且额外提供了一系列诸如自己主动内存管理、边界检查等特性,这些特性是为了在保证效率的基础上降低因为对指针的不正常使用而带来的bug。

smart pointer可以自己主动进行object的销毁:当某个object的最后一个拥有者被destroy的时候(如局部变量离开了作用域)。由smart pointer管理的object会被自己主动销毁。smart
pointer是被声明在stack中的

在C++中。smart pointer是以operator overload的方式模仿传统的指针(raw pointer)而用template class实现的

smart pointer在内存和性能方面被设计得尽可能的高效。比如,unique_ptr中唯一的data member就是封装好的指针,这意味着unique_ptr的大小与那个指针全然同样(4/8 Byte)。通过smart pointer中被重载的"->"和"*"訪问被封装的指针并不会比直接訪问raw
pointer慢非常多

smart pointer一般会提供一个直接訪问它包括的raw pointer的方法。如get()的member function,从而能够实现既通过smart pointer进行内存管理,又能够将raw pointer传递给不支持smart pointer的代码

some_type* foo();	// we don't know if caller should delete the memory when it no more needs the data
// by declaring the return type to "unique_ptr", it's made explicit that caller owns the result
unique_ptr<some_type> foo(); // prior to C++11, "unique_ptr" can be replaced with "auto_ptr"

为什么要使用smart pointer

1. 自己主动清理:不须要释放内存

2. 自己主动初始化:会自己主动被初始化为NULL

3. 避免了dangling pointer的问题:释放后会自己主动置为NULL

4. 出现exception时会自己主动释放内存,而不必在catch{}中进行delete

5. 垃圾回收(并没有在背景中单独执行的垃圾回收机制,而是通过标准的C++ scope rules进行内存管理,所以更高速高效)

6. 高效:能更高效地使用内存来缩短allocation和de-allocation的时间

7. 支持STL:比方要用vector来存放base class和derived class,假设用vector<base>会发生slicing problem,所以应该用一个vector<base*>,而这种话在使用完vector要逐个地手动释放当中的元素。会非常麻烦。

这时使用smart pointer能够自己主动释放(可是要注意某些smart pointer可能不能用于STL container)

unique_ptr

定义在头文件<memory>中,它是raw pointer的container。而且拥有这个raw pointer。

unique_ptr显式地禁止了对所包括的pointer的复制(比如通过"="),须要通过"std::move()"函数来将所包括的指针的拥有权转移给还有一个unique_ptr。假设令一个unique_ptr被指向一个新的资源。则原来指向的资源会被自己主动释放。unique_ptr不能进行复制由于它的copy
constructor和赋值运算符都被显式地删除了。当unique_ptr离开作用域时,假设声明的对象是数组,则delete[]会自己主动代替delete被调用。unique_ptr的大小就是一个指针的大小,而且它支持rvalue reference来实现对STL的高速插入和获取

unique_ptr的接口和普通指针类似,但不同意对指针进行数学计算。unique_ptr提供了release()和reset(),二者的差别是release()仅仅是交出了对资源的全部权但并未销毁资源。而reset()会销毁资源

std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; // compile error.
std::unique_ptr<int> p3 = std::move(p1); // transfers ownership. p3 now owns the memory and p1 is rendered invalid. p3.reset(); // deletes the memory.
p1.reset(); // does nothing. int *p0 = new int(5);
std::unique_ptr<int> pa(p0);
std::unique_ptr<int> pb(p0); // runtime error

"std::auto_ptr"仍然存在。但在C++11中已被弃用。"auto_ptr"的copy constructor和赋值运算符都并不是对存储的指针进行复制,而是实际进行了移动,执行后会使原来的auto_ptr指向的object成为空。

然而在曾经的代码中会存在用auto_ptr中进行复制的情景。所以无法将其改变为仅仅进行移动的指针而不改变对曾经代码的向后兼容性

class LargeObject
{
public:
void DoSomething(){}
}; void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject()); //Call a method on the object
pLarge->DoSomething(); // Pass a reference to a method.
ProcessLargeObject(*pLarge); } //pLarge is deleted automatically when function block goes out of scope.

上例展示了使用smart pointer的几个必要步骤:

1. 将smart pointer声明为自己主动(局部)变量,而不要对smart pointer本身使用new或malloc

2. 在type parameter中。声明所指向的被封装好的指针的类型

3. 传递一个raw pointer或new的object给smart pointer的constructor

4. 使用被重载过的"->"、"*"来訪问object

5. 让smart pointer删除object

shared_ptr

C++11定义了shared pointer:"std::shared_ptr"。在头文件<memory>中。

"shared_ptr"也是raw pointer的container。它通过维护它所包括pointer的reference-counted全部权而与其它全部shared_ptr的副本进行协作。仅仅有当全部shared_ptr的副本都被销毁时,被它所包括的raw
pointer所指向的object才会被销毁。当想要将一个raw pointer赋给多个拥有者时,使用shared_ptr。

shared_ptr的大小是两个指针,一个指向object。还有一个指向包括reference count的shared control block

std::shared_ptr<int> p1(new int(5));
std::shared_ptr<int> p2 = p1; // both now own the memory. p1.reset(); // memory still exists, due to p2.
p2.reset(); // deletes the memory, since no one else owns the memory.

shared_ptr的使用可能会出现故障,比如:

void main( )
{
int* p = new int;
shared_ptr<int> sptr1(p);
shared_ptr<int> sptr2(p);
}

上面的代码会产生问题,由于不同group的两个shared_ptr共享了同一个资源。

假设用delete释放了p指向的空间,则离开scope时。shared_ptr会再次释放已被释放的空间。

还有一个问题是也是资源可能被反复释放。例如以下图:

所以,最好不要用raw pointer创建多个shared_ptr。

以下的代码就没有问题:

void main( )
{
shared_ptr<int> sptr1(new int);
shared_ptr<int> sptr2 = sptr1;
shared_ptr<int> sptr3;
sptr3 = sptr2;
}

shared_ptr还有一个可能的问题是circular reference而导致资源无法被释放,例如以下例:

class B;
class A
{
public:
A() : m_sptrB(nullptr) {};
~A() {
cout<<" A is destroyed"<<endl;
}
shared_ptr<B> m_sptrB;
}; class B
{
public:
B() : m_sptrA(nullptr) {};
~B( ) {
cout<<" B is destroyed"<<endl;
}
shared_ptr<A> m_sptrA;
};
//***********************************************************
void main()
{
shared_ptr<B> sptrB(new B);
shared_ptr<A> sptrA(new A);
sptrB->m_sptrA = sptrA;
sptrA->m_sptrB = sptrB;
}

详细步骤例如以下图:

所以。使用shared_ptr可能会出现下面问题:

1. 假设一个memory block与多个不同group的shared_ptr相关联,则会出现错误。全部共享同一个reference的shared_ptr必须属于同一个group

2. 用raw pointer创建shared_ptr可能会出现故障

3. circular reference

weak_ptr

C++11在头文件<memory>中定义了weak pointer:"weak_ptr"。它也是raw pointer的container。能够解决上述shared_ptr可能出现的问题。weak pointer仅仅能提供sharing semantics而不能提供owning
semantics,意味着它要和shared_ptr一起使用。它被创建为一个shared_ptr的副本,而且仅仅能共享已经被shared_ptr拥有的资源。weak_ptr副本的存在和销毁对相应的shared_ptr和这个shared_ptr的其它副本没有影响。由于它并不參与strong reference counting。假设一个shared_ptr的全部副本都已经被销毁,则这个shared_ptr相应的weak_ptr副本会变成空

weak pointer不提供普通指针所支持的接口(如"*"。"->"),由于它不是资源的拥有者进而不会给程序员不论什么错误使用它的机会。假设要使用weak pointer指向的资源。须要通过weak_ptr创建shared_ptr,由于这样保证resource不会被销毁(由于对strong reference count加了1)。否则可能在使用weak_ptr时。shared_ptr拥有的资源已被释放。要用weak_ptr产生shared_ptr,能够调用lock()或直接将weak_ptr转换成shared_ptr

另外当想要观察一个object但并不要求这个object一直有效时,也能够使用weak_ptr

weak_ptr的constructor将shared_ptr作为它的一个參数。用一个shared_ptr创建weak_ptr添加了shared_ptr的weak reference counter,另外将一个weak_ptr赋给还有一个weak_ptr也会添加weak reference count。

std::shared_ptr<int> p1(new int(5));
std::weak_ptr<int> wp1 = p1; // p1 owns the memory. {
std::shared_ptr<int> p2 = wp1.lock(); // create shared_ptr from weak_ptr. now p1 and p2 own the memory.
if(p2) // since p2 is initialized from a weak pointer, you have to check if the memory still exists!
{
// do something with p2
}
} // p2 is destroyed. Memory is owned by p1. p1.reset(); // memory is deleted. std::shared_ptr<int> p3 = wp1.lock(); // memory is gone, so we get an empty shared_ptr.
if(p3)
{
// will not execute this.
}

因为shared_ptr的实现使用了reference counting。circular references可能会成为问题。

一个circular shared_ptr chain能够通过将当中的一个reference更改为weak_ptr来打破,例如以下例:

class B;
class A
{
public:
A() : m_a(5) {};
~A() {
cout<<" A is destroyed"<<endl;
}
void PrintSpB( );
weak_ptr<B> m_sptrB;
int m_a;
}; class B
{
public:
B() : m_b(10) {};
~B() {
cout<<" B is destroyed"<<endl;
}
weak_ptr<A> m_sptrA;
int m_b;
}; void A::PrintSpB( ) {
if( !m_sptrB.expired() ) {
cout<< m_sptrB.lock()->m_b<<endl;
}
} void main( )
{
shared_ptr<B> sptrB(new B);
shared_ptr<A> sptrA(new A);
sptrB->m_sptrA = sptrA;
sptrA->m_sptrB = sptrB;
sptrA->PrintSpB();
}

当一个weak_ptr指向的资源被shared_ptr释放时,weak pointer就expire了。有两种方法检查weak pointer是否expire:

1. 调用use_count()。返回strong reference数

2. 调用expired(), 这比调用use_count()更快

多个线程能够同一时候地通过不同的shared_ptr和weak_ptr而对同一个object进行安全的訪问

smart pointer的更多相关文章

  1. [CareerCup] 13.8 Smart Pointer 智能指针

    13.8 Write a smart pointer class. A smart pointer is a data type, usually implemented with templates ...

  2. 理解smart pointer之三:unique_ptr

    unique_ptr最先在boost中被定义,后来被C++标准委员会选中为C++11的feature之一. std::unique_ptr is a smart pointer that retain ...

  3. Why do we need smart pointer and how to implement it.

    Here are two simple questions. Problem A #include <string> include <iostream> using name ...

  4. c++ smart pointer

    智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露.它的一种通用实现技术是使用引用计数(reference ...

  5. c++(smart pointer)

    (一)首先对智能指针有一些概念性的了解 **********本部分内容摘自开源中国社区http://my.oschina.net/u/158589/blog/28994******** 1.什么是智能 ...

  6. c/c++ 标准库 智能指针( smart pointer ) 是啥玩意儿

    标准库 智能指针( smart pointer ) 是啥玩意儿 一,为什么有智能指针??? c++程序员需要自己善后自己动态开辟的内存,一旦忘了释放,内存就泄露. 智能指针可以帮助程序员"自 ...

  7. C++ smart pointer智能指针

      在C++中,程序员可以直接操作内存,给编程增加了不少的灵活性.但是灵活性是有代价的,程序员必须负责自己负责释放自己申请的内存,否则就会出现内存泄露.智能指针就是为了解决这个问题而存在的.它和其他指 ...

  8. Effective C++ Item 17 Store newed objects in smart pointer in standalone statements

    If you trying to do multiple things in one statement, you should think carefully abnormal behavior e ...

  9. Smart pointer 智能指针小总结

    Smart pointer line 58之后smart pointer里的计数已经是0,所以会真正释放它引用的对象,调用被引用对象的析构函数.如果继续用指针访问,会出现如下图的内存访问异常.所以说如 ...

随机推荐

  1. PLC学习资料

    常用字母 X 输入点(I)可连接外部输入信号 如感应器或限位/按钮等M 内部辅助继电器S 内部步进 不作步进使用时,可用作内部辅助继电器T 时间继电器 内部使用C计数器 内部使用Y输出点(O)输出给外 ...

  2. Pro ASP.NET Core MVC 第6版翻译 目录页

    Pro ASP.NET Core MVC 第6版 目录 第一部分 第一章 ASP.NET Core MVC 的前世今生 第二章 第一个MVC应用程序(上) 第二章 第一个MVC应用程序(下) 第三章 ...

  3. Sql server 本地临时表、全局临时表的区别

    创建了本地临时表#Tmp.全局临时表##Tmp: 本地临时表: 取名以#开头.如:#Tmp(会自动加上一串后缀) 只有当前的连接可以访问 连接关闭后,本地临时表自动释放 全局临时表: 取名以##开头. ...

  4. #NOIP前数学知识总结

    我好菜啊…… 欧拉函数 欧拉函数φ(n),是小于n且和n互质的正整数(包括1)的个数. 性质: 1.对于质数n: φ(n)=n-1 2..对于n=pk φ(n)=(p-1)*pk-1 3.积性函数的性 ...

  5. svn无法显示日期和作者

    当遇到这种情况,只要把这个read改为none就可以显示了  亲测绝对管用

  6. Javascript 原型链与constructor

    Javascript中的constructor与prototype 在学习javascript面向对象编程的过程中, constructor和prototype一直让我觉得理解不透,慢慢的学习过程中记 ...

  7. 解决移动端 手机号input 属性为 number,maxlength无效情况

    <input type="number" oninput="if(value.length>11)value=value.slice(0,11)" ...

  8. UVA-1572 Self-Assembly(拓扑排序判断有向环)

    题目: 给出几种正方形,每种正方形有无穷多个.在连接的时候正方形可以旋转.翻转. 正方形的每条边上都有一个大写英文字母加‘+’或‘-’.00,当字母相同符号不同时,这两条边可以相连接,00不能和任何边 ...

  9. Python学习-字符串函数操作2

    字符串函数操作 find( sub, start=None, end=None):从左到右开始查找目标子序列,找到了结束查找返回下标值,没找到返回 -1 sub:需要查找的字符串 start=None ...

  10. Linux 安装 Tomcat 详解

    说明:安装的 tomcat 为解压版(即免安装版):apache-tomcat-8.5.15.tar.gz (1)使用 root 用户登录虚拟机,在根目录下的 opt 文件夹新建一个 software ...