| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 3533 人关注过本帖, 4 人收藏
标题:[原创]C++中的指针(三) 智能指针
只看楼主 加入收藏
myajax95
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
威 望:30
帖 子:2978
专家分:0
注 册:2006-3-5
收藏(4)
 问题点数:0 回复次数:8 
[原创]C++中的指针(三) 智能指针

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等都有这种设计。所以记住这个应用是很有必要的。

[此贴子已经被作者于2006-9-25 15:32:21编辑过]

搜索更多相关主题的帖子: 指针 智能 
2006-09-25 15:06
yushui
Rank: 3Rank: 3
等 级:论坛游民
威 望:7
帖 子:1355
专家分:22
注 册:2006-7-19
收藏
得分:0 
谢谢哦  虽然我现在还是有点看不懂 指针学得不好 好多都弄不明白 拿你的慢慢去学习呵呵

fighting!from now on!
2006-09-25 16:51
Alome
Rank: 1
等 级:新手上路
帖 子:13
专家分:0
注 册:2007-1-14
收藏
得分:0 
爸爸说 做人要厚道 看帖就要回
谢谢,还是老样子。我会努力的!

如果我也认为自己很懒,大概神也这么认为吧~!
2007-01-14 17:26
lkssy
Rank: 1
等 级:新手上路
帖 子:12
专家分:0
注 册:2007-1-22
收藏
得分:0 
MARK

2007-02-11 13:53
dlcdavid
Rank: 3Rank: 3
来 自:成都
等 级:新手上路
威 望:6
帖 子:193
专家分:0
注 册:2005-12-23
收藏
得分:0 

DATE实际上就是句柄类吧?

你为什么要用struct?为什么不用class?(是无意的还是故意用的struct?)
为什么要把DATE内嵌在CLargeObject中?(是为了教学方便,还是内嵌比独立好?)

[此贴子已经被作者于2007-2-14 3:39:17编辑过]


为了C++,我放弃了课本
为了高考,我又放弃了C++
现在而今眼目下,我能做什么?www.
2007-02-14 03:38
zhangyq73
Rank: 1
等 级:新手上路
帖 子:4
专家分:0
注 册:2007-2-12
收藏
得分:0 

copy-to-write其实是windows操作系统中虚拟内存中的一个概念,操作系统给共享内存块赋予了copy-to-write属性,当一个. e x e或D L L模块被映射到一个内存地址时,系统将计算有多少页面是可以写入的,然后,系统从页文件中分配内存,以适应这些可写入的页面的需要。
当一个进程中的线程试图将数据写入一个共享内存块时,系统就会进行干预并执行下列操作步骤
系统查找R A M中的一个空闲内存页面,将试图被修改的页面内容拷贝到第一步中找到的页面,系统更新进程的页面表,使得被访问的虚拟地址被转换成新的R A M页面.

2007-02-15 10:09
cedricporter
Rank: 1
等 级:新手上路
帖 子:49
专家分:3
注 册:2007-2-6
收藏
得分:0 


清脆的口琴聲﹏悠揚的旋律﹏然而︵每個音符︵?°都充滿了悲傷︵?°~↘
2007-02-21 16:00
hitcc
Rank: 1
等 级:新手上路
帖 子:1
专家分:0
注 册:2008-10-29
收藏
得分:0 
一派胡言,错误百出,拷贝构造函数的作用如你所说?通篇有谈到智能指针吗?崩溃。自己研究明白再来发表文章好不好
2008-10-29 16:53
wangluxi
Rank: 2
等 级:论坛游民
帖 子:27
专家分:29
注 册:2008-10-22
收藏
得分:0 
智能指针?都一次听说
2008-10-29 16:57
快速回复:[原创]C++中的指针(三) 智能指针
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.015910 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved