smart pointer
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的更多相关文章
- [CareerCup] 13.8 Smart Pointer 智能指针
13.8 Write a smart pointer class. A smart pointer is a data type, usually implemented with templates ...
- 理解smart pointer之三:unique_ptr
unique_ptr最先在boost中被定义,后来被C++标准委员会选中为C++11的feature之一. std::unique_ptr is a smart pointer that retain ...
- Why do we need smart pointer and how to implement it.
Here are two simple questions. Problem A #include <string> include <iostream> using name ...
- c++ smart pointer
智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露.它的一种通用实现技术是使用引用计数(reference ...
- c++(smart pointer)
(一)首先对智能指针有一些概念性的了解 **********本部分内容摘自开源中国社区http://my.oschina.net/u/158589/blog/28994******** 1.什么是智能 ...
- c/c++ 标准库 智能指针( smart pointer ) 是啥玩意儿
标准库 智能指针( smart pointer ) 是啥玩意儿 一,为什么有智能指针??? c++程序员需要自己善后自己动态开辟的内存,一旦忘了释放,内存就泄露. 智能指针可以帮助程序员"自 ...
- C++ smart pointer智能指针
在C++中,程序员可以直接操作内存,给编程增加了不少的灵活性.但是灵活性是有代价的,程序员必须负责自己负责释放自己申请的内存,否则就会出现内存泄露.智能指针就是为了解决这个问题而存在的.它和其他指 ...
- 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 ...
- Smart pointer 智能指针小总结
Smart pointer line 58之后smart pointer里的计数已经是0,所以会真正释放它引用的对象,调用被引用对象的析构函数.如果继续用指针访问,会出现如下图的内存访问异常.所以说如 ...
随机推荐
- 盒子模型,top和margin-top
1. 标准盒子模型: width只是内容的宽度. 元素的总宽度=width + padding*2 +border*2 +margin*2. IE盒子模型: width=内容的宽度 + padding ...
- 联想 S5 Pro(L78041)免解锁BL 免rec 保留数据 ROOT Magisk Xposed 救砖 ZUI 5.0.123
>>>重点介绍<<< 第一:本刷机包可卡刷可线刷,刷机包比较大的原因是采用同时兼容卡刷和线刷的格式,所以比较大第二:[卡刷方法]卡刷不要解压刷机包,直接传入手机后用 ...
- caffe2:conda路径和权限问题
在使用conda之后,总是不能直接使用 conda install 命令,需要把codna添加到系统路径,取代默认Python. 在-/.bashrc中,添加 # added by Anaconda2 ...
- Change the color of a link in an NSMutableAttributedString
Swift Updated for Swift 3 Use with a textView.linkTextAttributes = [NSForegroundColorAttributeName: ...
- 扩增子统计绘图1箱线图:Alpha多样性
绘制Alpha多样性线箱图 绘图和统计全部为R语言,建议复制代码,在Rstuido中运行,并设置工作目录为存储之前分析结果文件的result目录 # 运行前,请在Rstudio中菜单栏选择“Sessi ...
- CPU总线
总线(Bus)是计算机各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束.按照计算机所传输的信息种类,计算机的总线可以划分为数据总线.地址总线和控制总线,分别用来传输数据.数据地址和控制 ...
- bazel和TensorFlow安装
bazel安装:https://docs.bazel.build/versions/master/install-ubuntu.html#install-with-installer-ubuntu 安 ...
- 右键快捷打开Git Bash here失败
右键快捷打开Git Bash here失败,提示: Error: Could not fork child process: Resource temporarily unavailable (-1) ...
- 迭代器,生成器(generator)和Promise的“微妙”关系
需要Promise源码版的朋友:传送链接 本文主要讲述(iterator)和生成器*/yield之间的联系和各自的用法,以及生成器的高配版本aysnc/await的使用. 大纲: 迭代器(iterat ...
- C++ Error C2662 cannot convert 'this' pointer from 'const *'
---恢复内容开始--- 这个错误在于一点:常量对象只能调用常量成员(函数\变量),不能调用非常量成员.另一方面,非常量对象,既可以调用常量成员,又可以调用非常量成员. class A { publi ...