智能指针
2016-03-20 00:57
288 查看
在C++中,我们使用普通指针指向一个内存对象时,在使用完之后我们需要释放它。
如果我们在使用完之后忘记delete或者foo调用发生异常都会造成内存泄漏。
这个时候智能指针的出现就是为了控制对象的生命周期。在智能指针中一个对象什么时候什么条件下释放由智能指针自身控制,用户不需要管理。
根据具体的条件我们一般会讨论这样几种智能指针。如下的指针指针也都是boost库里面定义的。
1,scoped_ptr:
这是一个比较简单的智能指针,正如其名,scoped_ptr所指的对象在作用域之外会被释放。没有赋值和复制构造函数,所以他是不具有copy能力的,这也是为了防止对象被多次释放(没有多次拷贝就没有多次释放)。另外scoped_ptr不支持自增自检不可复制所以它不能作为容器的元素。在局部中使用较合适,可以避免指针的常见问题。
线程安全问题:因为scoped_ptr是不支持拷贝的,所以它注定只能像局部变量一样在局部范围使用,出了范围就析构了。所以可以把它看作一个堆栈对象。无所谓多线程问题。
boost::scoped_ptr的常用操作:
可以简化为如下形式:
它的常用操作如下:
用法详情可参考:/article/8553143.html
2,shared_ptr:
shared_ptr与scoped_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针 ,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。shared_ptr也可以安全地放到标准容器中,并弥补了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷。
线程安全问题:
shared_ptr 本身不是 100% 线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。根据文档,shared_ptr 的线程安全级别和内建类型、标准库容器、string 一样
使用示例:
[b][cpp] view
plain copy[/b]
shared_ptr<int> sp(new int(10)); //一个指向整数的shared_ptr
assert(sp.unique()); //现在shared_ptr是指针的唯一持有者
shared_ptr<int> sp2 = sp; //第二个shared_ptr,拷贝构造函数
assert(sp == sp2 && sp.use_count() == 2); //两个shared_ptr相等,指向同一个对象,引用计数为2
*sp2 = 100; //使用解引用操作符修改被指对象
assert(*sp == 100); //另一个shared_ptr也同时被修改
sp.reset(); //停止shared_ptr的使用
assert(!sp); //sp不再持有任何指针(空指针)
源码请参考:/article/1802372.html
使用注意事项:/article/5553994.html
3,weak_ptr:
强引用和弱引用
一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。
相对而言,弱引用当引用的对象活着的时候不一定存在。仅仅是当它存在的时候的一个引用。弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。
boost::weak_ptr<T>是boost提供的一个弱引用的智能指针,它的声明可以简化如下:
可以看到,boost::weak_ptr必须从一个boost::share_ptr或另一个boost::weak_ptr转换而来,这也说明,进行该对象的内存管理的是那个强引用的boost::share_ptr。boost::weak_ptr只是提供了对管理对象的一个访问手段。
boost::weak_ptr除了对所管理对象的基本访问功能(通过get()函数)外,还有两个常用的功能函数:expired()用于检测所管理的对象是否已经释放;lock()用于获取所管理的对象的强引用指针。
详情可参考:/article/5553996.html
4.intrusive智能指针:
boost::intrusive_ptr一种“侵入式”的引用计数指针,它实际并不提供引用计数功能,而是要求被存储的对象自己实现引用计数功能,并提供intrusive_ptr_add_ref和intrusive_ptr_release函数接口供boost::intrusive_ptr调用。
具体使用举例请查考:/article/5553995.html
对比boost::shared_ptr
使用boost::shared_ptr用户类本省不需要具有引用计数功能,而是由boost::shared_ptr来提供;使用boost::shared_ptr的一大陷阱就是用一个raw pointer多次创建boost::shared_ptr,这将导致该raw pointer被多次销毁当boost::shared_ptr析构时。即不能如下使用:
boost::intrusive_ptr完全具备boost::shared_ptr的功能,且不存在shared_ptr的问题,即可以利用raw pointer创建多个intrusive _ptr,其原因就在于引用计数的ref_count对象,shared_ptr是放在shared_ptr结构里,而目标对象T通过继承intrusive_ptr_base将引用计数作为T对象的内部成员变量,就不会出现同一个对象有两个引用计数器的情况出现。
那么为什么通常鼓励大家使用shared_ptr,而不是intrusive_ptr呢, 在于shared_ptr不是侵入性的,可以指向任意类型的对象; 而intrusive_ptr所要指向的对象,需要继承intrusive_ptr_base,即使不需要,引用计数成员也会被创建。
结论:如果创建新类且需要进行传递,则继承intrusive_ptr_base,使用intrusive_ptr。
demo_ptr* = new demo_ptr; demo_ptr->foo(); delete demo_ptr;
如果我们在使用完之后忘记delete或者foo调用发生异常都会造成内存泄漏。
这个时候智能指针的出现就是为了控制对象的生命周期。在智能指针中一个对象什么时候什么条件下释放由智能指针自身控制,用户不需要管理。
根据具体的条件我们一般会讨论这样几种智能指针。如下的指针指针也都是boost库里面定义的。
1,scoped_ptr:
这是一个比较简单的智能指针,正如其名,scoped_ptr所指的对象在作用域之外会被释放。没有赋值和复制构造函数,所以他是不具有copy能力的,这也是为了防止对象被多次释放(没有多次拷贝就没有多次释放)。另外scoped_ptr不支持自增自检不可复制所以它不能作为容器的元素。在局部中使用较合适,可以避免指针的常见问题。
线程安全问题:因为scoped_ptr是不支持拷贝的,所以它注定只能像局部变量一样在局部范围使用,出了范围就析构了。所以可以把它看作一个堆栈对象。无所谓多线程问题。
boost::scoped_ptr的常用操作:
可以简化为如下形式:
namespace boost { template<typename T> class scoped_ptr : noncopyable { privite: <span style="white-space:pre"> </span>scoped_ptr(const scoped_ptr& p); <span style="white-space:pre"> </span>scoped_ptr& operate=(const scoped_ptr& p); public: explicit scoped_ptr(T* p = 0); ~scoped_ptr(); void reset(T* p = 0); T& operator*() const; T* operator->() const; T* get() const; void swap(scoped_ptr& b); }; template<typename T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b); }
它的常用操作如下:
成员函数 | 功能 |
operator*() | 以引用的形式访问所管理的对象的成员 |
operator->() | 以指针的形式访问所管理的对象的成员 |
reset() | 释放所管理的对象,管理另外一个对象 |
swap(scoped_ptr& b) | 交换两个boost::scoped_ptr管理的对象 |
2,shared_ptr:
shared_ptr与scoped_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针 ,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。shared_ptr也可以安全地放到标准容器中,并弥补了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷。
线程安全问题:
shared_ptr 本身不是 100% 线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为 shared_ptr 有两个数据成员,读写操作不能原子化。根据文档,shared_ptr 的线程安全级别和内建类型、标准库容器、string 一样
使用示例:
[b][cpp] view
plain copy[/b]
shared_ptr<int> sp(new int(10)); //一个指向整数的shared_ptr
assert(sp.unique()); //现在shared_ptr是指针的唯一持有者
shared_ptr<int> sp2 = sp; //第二个shared_ptr,拷贝构造函数
assert(sp == sp2 && sp.use_count() == 2); //两个shared_ptr相等,指向同一个对象,引用计数为2
*sp2 = 100; //使用解引用操作符修改被指对象
assert(*sp == 100); //另一个shared_ptr也同时被修改
sp.reset(); //停止shared_ptr的使用
assert(!sp); //sp不再持有任何指针(空指针)
源码请参考:/article/1802372.html
使用注意事项:/article/5553994.html
3,weak_ptr:
强引用和弱引用
一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。
相对而言,弱引用当引用的对象活着的时候不一定存在。仅仅是当它存在的时候的一个引用。弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。
boost::weak_ptr<T>是boost提供的一个弱引用的智能指针,它的声明可以简化如下:
namespace boost { template<typename T> class weak_ptr { public: template <typename Y> weak_ptr(const shared_ptr<Y>& r); weak_ptr(const weak_ptr& r); ~weak_ptr(); T* get() const; bool expired() const; shared_ptr<T> lock() const; }; }
可以看到,boost::weak_ptr必须从一个boost::share_ptr或另一个boost::weak_ptr转换而来,这也说明,进行该对象的内存管理的是那个强引用的boost::share_ptr。boost::weak_ptr只是提供了对管理对象的一个访问手段。
boost::weak_ptr除了对所管理对象的基本访问功能(通过get()函数)外,还有两个常用的功能函数:expired()用于检测所管理的对象是否已经释放;lock()用于获取所管理的对象的强引用指针。
详情可参考:/article/5553996.html
4.intrusive智能指针:
boost::intrusive_ptr一种“侵入式”的引用计数指针,它实际并不提供引用计数功能,而是要求被存储的对象自己实现引用计数功能,并提供intrusive_ptr_add_ref和intrusive_ptr_release函数接口供boost::intrusive_ptr调用。
具体使用举例请查考:/article/5553995.html
对比boost::shared_ptr
使用boost::shared_ptr用户类本省不需要具有引用计数功能,而是由boost::shared_ptr来提供;使用boost::shared_ptr的一大陷阱就是用一个raw pointer多次创建boost::shared_ptr,这将导致该raw pointer被多次销毁当boost::shared_ptr析构时。即不能如下使用:
int *a = new int(5); boost::shared_ptr ptr1(a); boost::shared_ptr ptr2(a); //错误!
boost::intrusive_ptr完全具备boost::shared_ptr的功能,且不存在shared_ptr的问题,即可以利用raw pointer创建多个intrusive _ptr,其原因就在于引用计数的ref_count对象,shared_ptr是放在shared_ptr结构里,而目标对象T通过继承intrusive_ptr_base将引用计数作为T对象的内部成员变量,就不会出现同一个对象有两个引用计数器的情况出现。
那么为什么通常鼓励大家使用shared_ptr,而不是intrusive_ptr呢, 在于shared_ptr不是侵入性的,可以指向任意类型的对象; 而intrusive_ptr所要指向的对象,需要继承intrusive_ptr_base,即使不需要,引用计数成员也会被创建。
结论:如果创建新类且需要进行传递,则继承intrusive_ptr_base,使用intrusive_ptr。
相关文章推荐
- 向Android Studio中添加jar包
- 微擎系统搭建(转)
- Ubuntu12.04安装Java环境和Eclipse
- Boost智能指针——weak_ptr
- [angularjs] angularjs系列笔记(二)指令
- C++实验2-模拟ATM
- Java Gradle入门指南之依赖管理(添加依赖、仓库、版本冲突) (转)
- C++派生类的构造函数
- Swfit中视图跳转
- 仿QQ写说说效果的实现
- 源码之前,了无秘密(一)——iterator adapter
- 远程方法调用(RMI)原理与示例 (转)
- 一维斜率限制器测试算例
- 三十分钟理解博弈论“纳什均衡” -- Nash Equilibrium
- 三十分钟理解博弈论“纳什均衡” -- Nash Equilibrium
- 三十分钟理解博弈论“纳什均衡” -- Nash Equilibrium
- 【计导作业】链表——差集与交集
- 大臣的旅费--蓝桥杯
- 可统计任一整数中某个位数出现的次数。例如-21252中,2出现了3次,则该函数应该返回3。
- 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用