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容易导致很多问题,这里列出三个比较容易犯的错误。

  1. 我们new了一个对象,但没有delete它。这会引起memory leak内存泄露,可能会导致程序崩溃。
  2. 用指针访问一个已经被free的对象。这就是我们常说的dangling pointer。
  3. delelte同一个内存区域两次。如果两个指针指向同一个内存区域,那我们delete一个指针后,再用另一个指针访问其指向的内存区域就会出现问题。

为了解决上面的问题,方便动态内存管理,c++提出了smart pointer的概念,在STL库中对应的实现主要有两种:shared_ptr、unique_ptr。下面我们主要介绍这两种smart pointer。

  1. 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)。
  2. 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

shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数(reference counting),每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。

shared_ptr 可以通过三种方式得到(拷贝初始化

1.通过一个指向堆上申请的空间的指针初始化(切记不要用栈上的指针,否则,当智能指针全部释放控制权(栈中的对象离开作用域本身就会析构一次),将会析构对象,导致出错)
2.通过make_shared函数得到
3.通过另外一个智能指针初始化
std::shared_ptr<int> bptr(p);//方式1
std::shared_ptr<int> aptr = std::make_shared<int>();//方式 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[];
shared_ptr<int> sp1(pInt);
// 一些其它代码之后…
shared_ptr<int> sp2(pInt);
}

2.1.3 使用shared_ptr包装this指针带来的问题

参考:enable_shared_from_this

class tester
{
public:
tester()
~tester()
{
std::cout << "析构函数被调用!\n";
}
public:
shared_ptr<tester> sget()
{
return shared_ptr<tester>(this);
}
}; int main()
{
tester t;
shared_ptr<tester> sp = t.sget(); // …
return ;
}

也将导致两次释放t对象破坏堆栈,一次是出栈时析构,一次就是shared_ptr析构。若有这种需要,可以使用下面代码

class tester : public enable_shared_from_this<tester>
{
public:
tester()
~tester()
{
std::cout << "析构函数被调用!\n";
}
public:
shared_ptr<tester> sget()
{
return shared_from_this();
}
}; int main()
{
shared_ptr<tester> sp(new tester);
// 正确使用sp 指针。
sp->sget();
return ;
}
std::enable_shared_from_this 能让一个对象(假设其名为 t ,且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ... ) ,它们与 pt 共享对象 t 的所有权。

当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr。

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 <<"父类析构函数被调用.\n";
}
public:
child_ptr children;
}; class child
{
public:
~child() {
std::cout <<"子类析构函数被调用.\n";
}
public:
parent_ptr parent;
}; int main()
{
parent_ptr father(new parent());
child_ptr son(new child);
// 父子互相引用。
father->children = son;
son->parent = father;
return ;
}
如上代码,将在程序退出前,father的引用计数为2,son的计数也为2,退出时,shared_ptr所作操作就是简单的将计数减1,如果为0则释放,显然,这个情况下,引用计数不为0,于是造成father和son所指向的内存得不到释放,导致内存泄露

2.1.5 没有std::shared_ptr<T[]>.所以shared_ptr只能管理单个对象,而不能管理对象数组

2.1.6 shared_ptr reset

reset()包含两个操作。当智能指针中有值的时候,调用reset()会使引用计数减1.当调用reset(new xxx())重新赋值时,智能指针首先是生成新对象,然后将就对象的引用计数减1(当然,如果发现引用计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。

3.应用举例

 #include <memory>
using namespace std; int main(){
shared_ptr<int> p(new int());
shared_ptr<int> p2 = make_shared<int>();
cout << *p << " ";
unique_ptr<int> p3(new int());
cout << *p3 << " ";
return ;
}

//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()); //vec.push_back(1); vec.push_back(std::move(sp));//尝试引用已删除的函数
cout << *vec[]<< endl; // 输出126
//cout << *sp << endl;
return ;
}

shared_ptr 和 unique_ptr的更多相关文章

  1. C++动态内存管理之shared_ptr、unique_ptr

    C++中的动态内存管理是通过new和delete两个操作符来完成的.new操作符,为对象分配内存并调用对象所属类的构造函数,返回一个指向该对象的指针.delete调用时,销毁对象,并释放对象所在的内存 ...

  2. c++智能指针的使用,shared_ptr,unique_ptr,weak_ptr

    c++智能指针的使用 官方参考 普通指针的烦恼:内存泄漏,多次释放,提前释放 智能指针 负责自动释放所指向的对象. 三种智能指针 shared_ptr,unique_ptr,weak_ptr: 将sh ...

  3. C++ shared_ptr、unique_ptr、weak_ptr

    shared_ptr unique_ptr weak_ptr 内存泄漏 智能指针 引用计数 循环引用 reset

  4. 关于std:auto_ptr std:shared_ptr std:unique_ptr

    很多人听说过标准auto_ptr智能指针机制,但并不是每个人都天天使用它.这真是个遗憾,因为auto_ptr优雅地解决了C++设计和编码中常见的问题,正确地使用它可以生成健壮的代码.本文阐述了如何正确 ...

  5. shared_ptr & unique_ptr & weak_ptr (C++11)

    c++11标准废除乐auto_ptr, C++ 标准库智能指针 使用这些智能指针作为将指针封装为纯旧 C++ 对象 (POCO) 的首选项. unique_ptr 只允许基础指针的一个所有者. 除非你 ...

  6. c++智能指针(unique_ptr 、shared_ptr、weak_ptr、auto_ptr)

    一.前序 什么是智能指针? ——是一个类,用来存储指针(指向动态分配对象也就是堆中对象的的指针). c++的内存管理是让很多人头疼的事,当我们写一个new语句时,一般就会立即把delete语句直接也写 ...

  7. 智能指针shared_ptr的用法

    为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...

  8. C++ Primer : 第十二章 : 动态内存之shared_ptr类

    在C++中,动态内存是的管理是通过一对运算符来完成的:new  ,在动态内存中为对象分配空间并返回一个指向该对象的指针,delete接受一个动态对象的指针,销毁该对象,并释放该对象关联的内存. 动态内 ...

  9. Boost源代码学习---shared_ptr.hpp

    最近观看Boost库源代码.Boost功能强大的库,但它的许多源代码,十一细读太费时间,毕竟,还有其他东西要学.所以我决定脱脂感兴趣的章节,他们的设计思路和难以理解的地方记录. shared_ptr是 ...

随机推荐

  1. php正则

    PHP代码 $str = preg_replace("/(<a.*?>)(.*?)(<\/a>)/", '\1<span class="li ...

  2. MongoDB的安全(五)

    MongoDB用户管理操作: MongoDB开启权限认证的方式有两种一种是auth形式,一种是keyfile形式 MongoDB创建用户: 1. 创建用户语法:在MongoDB2.6版本之后使用cre ...

  3. Unity手游之路<一>C#版本Protobuf

    http://blog.csdn.net/janeky/article/details/17104877 个游戏包含了各种数据,包括本地数据和与服务端通信的数据.今天我们来谈谈如何存储数据,以及客户端 ...

  4. Sqli-LABS通关笔录-16

    这个关卡之前我还使用了一下工具跑,发现居然跑不出来.这就尴尬了.行吧手工试试. payload admin") and If(ascii(substr(database(),1,1))=11 ...

  5. 37 网络相关函数(五)——live555源码阅读(四)网络

    37 网络相关函数(五)——live555源码阅读(四)网络 37 网络相关函数(五)——live555源码阅读(四)网络 简介 10)MAKE_SOCKADDR_IN构建sockaddr_in结构体 ...

  6. RPC(Remote Procedure Call Protocol)——远程过程调用协议 学习总结

        首先了解什么叫RPC,为什么要RPC,RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需 ...

  7. 浏览器js自动查表脚本

    javascript: void((function() {$.get("", {wen: "880350384879600241",action: " ...

  8. @PathVariable注解

    spring mvc中的@PathVariable是用来获得请求url中的动态参数的,十分方便,复习下: . @Controller public class TestController { @Re ...

  9. centos 6.5 u盘 安装问题 :vesamenu.c32: Not a COM32R image

    大致可以参考这里:http://www.computerandyou.net/2012/03/how-to-solve-vesamenu-c32-not-a-com32r-image-error-in ...

  10. SQL关于分页的sql查询语句 limit 和row_number() OVER函数

    在做项目的时候需要些分页,用的数据库是mysql,之前看到的参考例子是用MS SQL做的,在MS SQL.ORACLE里面有ROW_NUMBER() OVER函数可以在数据库里对数据进行分组.百度后的 ...