1.C++ 标准库智能指针
c++11标准废除乐auto_ptr,
使用这些智能指针作为将指针封装为纯旧 C++ 对象 (POCO) 的首选项。
-
unique_ptr
只允许基础指针的一个所有者。 除非你确信需要 shared_ptr,否则请将该指针用作 POCO 的默认选项。 可以移到新所有者,但不会复制或共享。 替换已弃用的auto_ptr。 与 boost::scoped_ptr 比较。 unique_ptr 小巧高效;大小等同于一个指针且支持 rvalue 引用,从而可实现快速插入和对 STL 集合的检索。 头文件:<memory>。 有关更多信息,请参见如何:创建和使用 unique_ptr 实例和unique_ptr 类。 -
shared_ptr
采用引用计数的智能指针。 如果你想要将一个原始指针分配给多个所有者(例如,从容器返回了指针副本又想保留原始指针时),请使用该指针。 直至所有shared_ptr 所有者超出了范围或放弃所有权,才会删除原始指针。 大小为两个指针;一个用于对象,另一个用于包含引用计数的共享控制块。 头文件:<memory>。 有关更多信息,请参见如何:创建和使用 shared_ptr 实例和shared_ptr 类。 -
weak_ptr
结合 shared_ptr 使用的特例智能指针。 weak_ptr 提供对一个或多个 shared_ptr 实例拥有的对象的访问,但不参与引用计数。 如果你想要观察某个对象但不需要其保持活动状态,请使用该实例。 在某些情况下,需要断开 shared_ptr 实例间的循环引用。 头文件:<memory>。 有关更多信息,请参见如何:创建和使用共享 weak_ptr 实例和weak_ptr 类。
http://www.cnblogs.com/Kai-Xing/p/4414239.html
C++中的动态内存管理是通过new和delete两个操作符来完成的。new操作符,为对象分配内存并调用对象所属类的构造函数,返回一个指向该对象的指针。delete调用时,销毁对象,并释放对象所在的内存。但在程序中使用new和delete容易导致很多问题,这里列出三个比较容易犯的错误。
- 我们new了一个对象,但没有delete它。这会引起memory leak内存泄露,可能会导致程序崩溃。
- 用指针访问一个已经被free的对象。这就是我们常说的dangling pointer。
- delelte同一个内存区域两次。如果两个指针指向同一个内存区域,那我们delete一个指针后,再用另一个指针访问其指向的内存区域就会出现问题。
为了解决上面的问题,方便动态内存管理,c++提出了smart pointer的概念,在STL库中对应的实现主要有两种:shared_ptr、unique_ptr。下面我们主要介绍这两种smart pointer。
- shared_ptr:是一个模板类,定义在<memory>头文件里。shared_ptr对象会在其作用域结束时,自动销毁,如果该shared_ptr是指向某对象A的最后一个shared_ptr,那么A所在的内存会被释放。应用举例:shared_ptr<int> p(new int(4)); 或者用make_shared<T>()函数生成shared_ptr,shared_ptr<int> p = make_shared<int>(4)。
- unique_ptr:也是一个模板类,同样定义在<memory>头文件里。与shared_ptr不同的是,unique_ptr是自己”拥有“一个指向的对象,也就是说不同有两个或者以上的unique_ptr指向同一个对象。在一个unique_ptr对象的作用域结束时,unique_ptr指向的对象的内存被释放。为了保证unique_ptr对对象的独有性,赋值、复制操作是不允许的。但有一个例外,我们可以在函数中return一个unique_ptr。应用举例:unique_ptr<int> p(new int(4))。unique_ptr是c++11引入的,其之前对应的是auto_ptr,与unique_ptr不同的是,我们不能再函数中返回auto_ptr对象。
在程序中运用smart pointer还有一个优点,就是smart pointer保证其指向的动态内存被释放不论程序是否正常结束,而new和delete不能保证这一点。shared_ptr和unique_ptr在释放对象内存时默认用delete,不过我们可以指定自己的deleter,具体内容可参考C++ Primier Dynamic Memory部分。
2. 智能指针使用注意事项
2.1 shared_ptr
2.通过make_shared函数得到
3.通过另外一个智能指针初始化
std::shared_ptr<int> bptr(p);//方式1 std::shared_ptr<int> aptr = std::make_shared<int>(2);//方式 2 std::shared_ptr<int> cptr(aptr); //方式3
2.1.1 禁止纯指针给智能指针赋值或者拷贝构造
int* a=new int(2);
shared_ptr<int>sp=a;// error
sp=a;// error
2.1.2 shared_ptr多次引用同一数据,会导致两次释放同一内存
{ int* pInt = new int[100]; shared_ptr<int> sp1(pInt); // 一些其它代码之后… shared_ptr<int> sp2(pInt); }
2.1.3 使用shared_ptr包装this指针带来的问题
class tester { public: tester() ~tester() { std::cout << "析构函数被调用! "; } public: shared_ptr<tester> sget() { return shared_ptr<tester>(this); } }; int main() { tester t; shared_ptr<tester> sp = t.sget(); // … return 0; }
也将导致两次释放t对象破坏堆栈,一次是出栈时析构,一次就是shared_ptr析构。若有这种需要,可以使用下面代码
class tester : public enable_shared_from_this<tester> { public: tester() ~tester() { std::cout << "析构函数被调用! "; } public: shared_ptr<tester> sget() { return shared_from_this(); } }; int main() { shared_ptr<tester> sp(new tester); // 正确使用sp 指针。 sp->sget(); return 0; }
a.为何不直接传递this指针
使用智能指针的初衷就是为了方便资源管理,如果在某些地方使用智能指针,某些地方使用原始指针,很容易破坏智能指针的语义,从而产生各种错误。
b.可以直接传递share_ptr<this>么?
答案是不能,因为这样会造成2个非共享的share_ptr指向同一个对象,未增加引用计数导对象被析构两次。
c.为何会出现这种使用场合
因为在异步调用中,存在一个保活机制,异步函数执行的时间点我们是无法确定的,然而异步函数可能会使用到异步调用之前就存在的变量。为了保证该变量在异步函数执期间一直有效,我们可以传递一个指向自身的share_ptr给异步函数,这样在异步函数执行期间share_ptr所管理的对象就不会析构,所使用的变量也会一直有效了(保活)。
2.1.4 shared_ptr循环引用导致内存泄露
typedef shared_ptr<parent> parent_ptr; typedef shared_ptr<child> child_ptr; class parent { public: ~parent() { std::cout <<"父类析构函数被调用. "; } public: child_ptr children; }; class child { public: ~child() { std::cout <<"子类析构函数被调用. "; } public: parent_ptr parent; }; int main() { parent_ptr father(new parent()); child_ptr son(new child); // 父子互相引用。 father->children = son; son->parent = father; return 0; }
2.1.5 没有std::shared_ptr<T[]>.所以shared_ptr只能管理单个对象,而不能管理对象数组
2.1.6 shared_ptr reset
reset()包含两个操作。当智能指针中有值的时候,调用reset()会使引用计数减1.当调用reset(new xxx())重新赋值时,智能指针首先是生成新对象,然后将就对象的引用计数减1(当然,如果发现引用计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。
3.应用举例
1 #include <memory> 2 using namespace std; 3 4 int main(){ 5 shared_ptr<int> p(new int(4)); 6 shared_ptr<int> p2 = make_shared<int>(4); 7 cout << *p << " "; 8 unique_ptr<int> p3(new int(4)); 9 cout << *p3 << " "; 10 return 0; 11 }
//unique智能指针的所有权问题,这个时候就需要使用std::move:
#include<iostream> #include<vector> #include <memory> using namespace std; int main() { vector<unique_ptr<int>> vec; unique_ptr<int> sp(new int(126)); //vec.push_back(1); vec.push_back(std::move(sp));//尝试引用已删除的函数 cout << *vec[0]<< endl; // 输出126 //cout << *sp << endl; return 0; }