一转:http://www.cppblog.com/yearner/archive/2008/11/09/66447.html

浅谈C++的智能指针

内存泄露是C++程序员都头疼的大问题。C++缺乏像JAVA、C#一样,拥有GC这么一项有利的武器,它将内存管理的部分权限交给了程序员。虽然GC的存在节约了开发、排错的时间与成本,但是C++为了追求运行速度而20年来坚决不予补充进其标准。(题外话:C++通过加大开发难度去换取执行速度的做法,在现在看来不知是否能给与正面的评价,还是留给将来再说吧。)

从此,在堆上申请了内存忘了释放、所造成的内存泄露的问题就一直困扰着C++程序员。也许为了稍许弥补没有垃圾回收器所造成的开发门槛高,各大厂商开发的C++库中都像COM学习引入智能指针试图解决部分目前存在的问题。

智能指针是存储指向动态分配(堆)对象指针的类, 用于生存期控制, 能够确保自动正确的销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。

说到智能指针,我们一定要看看标准C++库提供的“搞笑的”智能指针:auto_ptr。

标准库中提供了C++程序的基本设施。虽然C++标准库随着C++标准折腾了许多年,直到标准的出台才正式定型,网上评论C++标准库时都说:“在标准库的实现上却很令人欣慰得看到多种实现,并且已被实践证明为有工业级别强度的佳作。”但目前的标准C++中,只有一种独苗智能指针:std::auto_ptr。

auto_ptr指针是一个RAII对象,它初始化时获得资源,析构时自动释放资源(生命期结束).它的缺点数不胜数:
1、auto_ptr要求一个对象只能有一个拥有者,严禁一物二主
2、缺少对引用数和数组的支持。
3、不可将auto_ptr对象作为STL容器的元素。C++标准明确禁止这样做,否则可能会碰到不可预见的结果。(这一条晕死一大片)。
4、auto_ptr在被复制的时候会传输所有权

反正由此可见:标准库的智能指针就是无甚大用。

在这样的情况下,C++标准委员会自然需要考虑引入新的智能指针。目前由C++标准委员会库工作组发起的Boost 组织开发了Boost系列智能指针。

在Boost中的智能指针有五种: scoped_ptr,scoped_array,shared_ptr,shared_array,weak_ptr.

前4种完全是针对标准库中的auto_ptr提出解决方案,如:scope_ptr是针对“auto_ptr在被复制的时候会传输所有权”这一弱点提出的。最后一种没见过,看名字像是弱引用智能指针,我怀疑是不是类似于JAVA中弱引用一样,有待进一步学习。

Smart Pointer是C++中的一个大题目,要说清楚他的所有好处很需要费点力气。我就一个功能一个功能的说。有我理解不透的地方希望大家指点。

1.copy-to-write
当生成一个C++ object的时候如果这个class很大,这个object会占用很多空间。那么每生成一个就占用一片空间,这样会占用很多系统资源。同时降低效率。一个解决方法就是对用拷贝构造函数生成的object,让他不存储数据,而只存储一个指向原来object数据的指针。 这样空间就节省了很多。但问题在于这样两个object完全联结在了一起。如果修改了其中一个,另一个也跟着变了。所以这种方法不可取。这里讲的 copy-to-write技术就是解决这类问题的方法。当通过引用一个已有object去拷贝构造新object时,新object只有一个指向已有 object的指针。这两个object共享数据。直到其中一个需要修改数据的时候,再把这两块数据分离。这里举一个最简化的例子。假设一个class叫 CLargeObject,里面存有很多数据。我们用一个inner class来把所有数据放在一起,叫CData。CData里面存有大量数据,例如一个数据库。这里用最简单的模型来表示,假设只有一个整数int m_nVal; CData里面需要包含另一个变量。叫作索引数目(reference count)。它记录了指向这个CData object的来自CLargetObject类的指针各数。也就是说,总共有多少CLargeObject的object正在引用着当前的CData object。

class CLargeObject
{
private:
    struct CData
    {
    private:
        int m_nVal;
        int m_nReferenceCount;
    }
};

对于每个CLargeObject的object,我们用一个CData类的指针来指向其数据。
CData *m_pData;

CLargeObject至少有两个构造函数。第一个是标准的构造函数,初始化其数据。这时数据是唯一的,所以必须新生成一个CData的object来存储数据。
CLargeObject::CLargeObject(int nVal)
{
    m_pData = new Data(nVal);
}
而对于CData类的构造函数而言,初始化他的CLargeObject是第一个指向他的,这一时刻索引数目m_nReferenceCount是1。
CLargeObject::Data::Data(int nVal) : m_nVal(nVal), m_nReferenceCount(1) {}

CLargeObject的第二个构造函数是拷贝构造(copy constructor)。这样生成的object不需要有新的数据,和已有的object共享数据就可以了。这是索引数目需要加1。表示又有一个object指向当前的CData了。
CLargeObject::CLargeObject(const CLargeObject &ob) // copy constructor
{
    ob.m_pData->m_nReferenceCount++;
    m_pData = ob.m_pData;
}

这样CLargeObject就构造好了,使用了可能的最少的内存。下面看看他的析够函数(destructor)。当一个object被delete的时候,它的数据不一定无效,如果别的object还在引用着这个数据,数据需要留下来。当然,数据的索引数目无论如何都要减1。
CLargeObject::~CLargeObject()
{
    if (--m_pData->m_nReferenceCount == 0)
        delete m_pData;
}

下面看一看赋值操作。先说用已有的CLargeObject赋值给这个CLargeObject。这时当前CLargeObject里面的数据要指向已有的这个object,就搞定了。
CLargeObject& CLargeObject::operator = (const CLargeObject& ob)    // copy assignment
{
    ob.m_pData->m_nReferenceCount++;
    if (--m_pData->m_nReferenceCount == 0)
        delete m_pData;
    m_pData = ob.m_pData;

return *this;
}

再来看看如何对CLargeObject里面的数据进行真正的修改。这样就一定需要对当前的object独立操作了,否则就影响到了其它指向同一块数据的CLargeObject。这样CData类需要一个新的函数,生成只用于当前CLargetObject的数据。如果当前的引用数目是1,那么当然这个CData就是只用于这个CLargeObject的了。否则就重新new一个CData返回。
        Data* CLargeObject::CData::get_own_copy()    // clone if necessary
        {
            if (m_nReferenceCount==1)
                return this;
            m_nReferenceCount--;
            return new Data(m_nVal);
        }
CLargeObject修改前用这个函数得到唯一的object,然后对它赋值。
void CLargeObject::SetVal(int nNewVal)
{
    m_pData = m_pData->get_own_copy();
    m_pData->m_nVal = nNewVal;
}
对于所有可能改变CData值的操作,都需要用这种方法。

下面是只读函数,简单。直接返回值,什么特殊的都不用作。
int CLargeObject::GetVal() const
{
    return m_pData->m_nVal;
}

这样copy-to-write技术就实现了。下面把完整的程序写一下:
class CLargeObject
{
public:
    CLargeObject(int nVal);
    CLargeObject(const CLargeObject &ob);
    ~CLargeObject();

CLargeObject& operator = (const CLargeObject& ob);
    void SetVal(int nNewVal);
    int GetVal() const;
private:
    struct Data
    {
    public:
        Data(int nVal) : m_nVal(nVal), m_nReferenceCount(1) {}
    private:
        friend class CLargeObject;
        Data* get_own_copy()    // clone if necessary
        {
            if (m_nReferenceCount==1)
                return this;
            m_nReferenceCount--;
            return new Data(m_nVal);
        }

// control variables.
        int m_nReferenceCount;
    
        // actual data portion
        int m_nVal;
    };

Data *m_pData;
};

CLargeObject::CLargeObject(int nVal)
{
    m_pData = new Data(nVal);
}

CLargeObject::CLargeObject(const CLargeObject &ob) // copy constructor
{
    ob.m_pData->m_nReferenceCount++;
    m_pData = ob.m_pData;
}

CLargeObject::~CLargeObject()
{
    if (--m_pData->m_nReferenceCount == 0)
        delete m_pData;
}

CLargeObject& CLargeObject::operator = (const CLargeObject& ob)    // copy assignment
{
    ob.m_pData->m_nReferenceCount++;
    if (--m_pData->m_nReferenceCount == 0)
        delete m_pData;
    m_pData = ob.m_pData;

return *this;
}

void CLargeObject::SetVal(int nNewVal)
{
    m_pData = m_pData->get_own_copy();
    m_pData->m_nVal = nNewVal;
}

int CLargeObject::GetVal() const
{
    return m_pData->m_nVal;
}

很多存储数据的系统class,如string,CString等都有这种设计。所以记住这个应用是很有必要的。

浅谈C++的智能指针相关推荐

  1. 浅谈以太坊智能合约的设计模式与升级方法

    浅谈以太坊智能合约的设计模式与升级方法 1. 最佳实践 2. 实用设计案例 2.1 控制器合约与数据合约: 1->1 2.2 控制器合约与数据合约: 1->N 2.3 控制器合约与数据合约 ...

  2. 浅谈Object Pascal的指针[引用 Nicrosoft]

    浅谈Object Pascal的指针 Nicrosoft(nicrosoft@sunistudio.com) -- 2001.8.26     http://www.sunistudio.com/ni ...

  3. 浅谈Object Pascal的指针

    浅谈Object Pascal的指针 作者:Nicrosoft 时间:2001-8-26 来源:Nicrosoft个人网站 大家都认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上 ...

  4. c语言指针很危险,浅谈C语言中指针使用不当的危险性.doc

    浅谈C语言中指针使用不当的危险性.doc 第 19 卷 Vol . 19 第 2 期 No . 2 洛阳师专学报 Journal of Luoyang Teachers College 2000 年 ...

  5. 浅谈C中的指针和数组(一)

    本文转载地址:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242138.html 在原文的基础上加入自己的想法作为修改. 指针是C/C ...

  6. 浅谈物联网时代智能停车发展趋势

    我国目前是世界上第三大汽车生产国和第二大汽车消费国,随着汽车数目的不断增长,停车场的需求也逐渐增加.到目前为止,停车难是让车主困扰的一大难题.伴随着物联网技术的日益成熟及智慧城市的建设,智能停车场成为 ...

  7. 浅谈 “空指针、野指针、void*”

            Author: JW. Zhou Date: 2014/7/2 一.空指针(0/NULL) 返回NULL和返回0是完全等价的,因为NULL和0都表示空指针,换句话说:空指针是什么,就是 ...

  8. 浅谈人机混合智能——计算-算计模型

    摘要:人工智能取得成果斐然,但是现阶段的人工智能体还远未达到接近人类心智的水平.在面对复杂环境下,计算体系中的人工智能水平有限,无法发挥其特点.智能是一个复杂系统,在追求算力与算法实现人工智能应用的时 ...

  9. 浅谈安科瑞电力智能运维在高速铁路电力系统的应用分析

    摘  要:高速铁路电力智能运维管理系统采用终端感知层.系统网络层.系统平台层的三层网络架构模式,通过集成网关,共享通信传输设备,利用铁路专用运维传输网络通道将各类监测数据上传至运维管理平台数据进行实时 ...

最新文章

  1. 2021年中国工业互联网安全大赛核能行业赛道writeup之传统流量取证
  2. 安装sql 2005 闪退 连接到服务器失败。错误:0x80070424
  3. 为ubuntu操作系统增加root用户
  4. ThinkPHP 3.2 Token表单令牌
  5. 你的网站添加X-UA-Compatible meta标签了吗?
  6. 如何使用hyperopt对xgboost进行自动调参
  7. sql优化的方法及思路_微生物发酵 技术优化思路 与方法
  8. OpenCV Gunnar Farneback的密集光流算法(附完整代码)
  9. mysql 执行计划 代价_mysql explain执行计划详解
  10. 矩池云上安装yolov4 darknet
  11. (day 11 - 模拟)剑指 Offer 29. 顺时针打印矩阵
  12. vb计算机安装教程,windows10系统安装vb6.0图文教程 Win10攻略
  13. 实现echarts中国地图迁徙图
  14. windows ghost备份
  15. 无法启动此程序,因为计算机中丢失MSVCP120.dll文件、应用程序无法正常启动0xc000007b
  16. 高颜值时尚小巧蓝牙耳机推荐,女友生日过节最强送礼装备
  17. php xmp,在jpeg中编写XMP元数据(使用PHP) – 使用单个或多个rdf:描述块
  18. C语言课程设计题目汇总
  19. 使用HTML表单和表格完成静态QQ登陆界面
  20. 计算机硬件相关行业,2021年中国电脑硬件行业市场规模及发展前景预测分析(图)...

热门文章

  1. script-百度换肤效果
  2. 实验4-1-8 求给定精度的简单交错序列部分和 (15 分)
  3. Open3d之点云顶点法线估计
  4. nodejs计算时间间隔_nodejs 如何定时执行一个函数
  5. spring mvc: xml练习
  6. OpenCV探索之路(九):模板匹配
  7. 一道『easy』等级的力扣题,我写了两个小时的笔记...
  8. 【汇编语言与计算机系统结构笔记19】虚存概念初步,MIPS内存管理
  9. 【汇编语言与计算机系统结构笔记06】地址计算指令,lea / leal,x86-32与x86-64下的swap对比,汇编的格式对比(Intel/Microsoft Differs from GAS)
  10. 吉林建筑大学电气与计算机学院院长,吉林建筑大学电气与计算机学院研究生导师:张玉红...