C++中的指针(三) 智能指针

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


1.copy-to-write
当生成一个C++ object的时候如果这个class很大,这个object会占用很多空间。那么每生成一个就占用一片空间,这样会占用很多系统资源

同时降低效率。一个解决方法就是对用拷贝构造函数生成的object,让他不存储数据,而只存储一个指向原来object数据的指针。

这样空间就节省了很多。但问题在于这样两个object完全联结在了一起。如果修改了其中一个,另一个也跟着变了。所以这种方法不可取。

这里讲的copy-to-write技术就是解决这类问题的方法。当通过引用一个已有object去拷贝构造新object时,新object只有一个指向已有object的指针。

这两个object共享数据。直到其中一个需要修改数据的时候,再把这两块数据分离。这里举一个最简化的例子。

假设一个classCLargeObject,里面存有很多数据。我们用一个inner class来把所有数据放在一起,叫CData

CData里面存有大量数据,例如一个数据库。这里用最简单的模型来表示,假设只有一个整数int m_nVal; CData里面需要包含另一个变量

叫作索引数目(reference count)。它记录了指向这个CData object的来自CLargetObject类的指针各数。

也就是说,总共有多少CLargeObjectobject正在引用着当前的CData object

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

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

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

当一个objectdelete的时候,它的数据不一定无效,如果别的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,如stringCString等都有这种设计。所以记住这个应用是很有必要的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值