【C++沉思录】句柄1

1、在【C++沉思录】代理类中,使用了代理类,存在问题:
a、代理复制,每次创建一个副本,这个开销有可能很大
b、有些对象不能轻易创建副本,比如文件
2、怎么解决这个问题?
使用引用计数句柄,对动态资源封装,句柄包含指针,多个句柄可以指向同一个对象。复制的时候,只是复制句柄的指针。
3、使用引用计数句柄,是为了避免不必要的对象复制,因此我们要知道有多少个句柄绑定到当前对象,也就是引用计数,
这样才能确定何时可以释放资源。
4、需要注意的是:引用计数不能是句柄的一部分,如果怎么做,当前句柄必须知道指向同一个对象的其他句柄,引用计数也要保持一致。
同时,引用计数不能成为对象的一部分,如果这样做,要求我们重写已经存在的对象类。
5、以Point类为例说明,解决办法是:增加一个新的类UPoint,包含Point对象和引用计数u,如下:
class Point
{
public:
Point():_x(0),_y(0){}
Point(int x,int y):_x(x),_y(y){}
Point(const Point& rhs):_x(rhs._x),_y(rhs._y){}

Point& SetX(int x)
{
_x = x;
return *this;
}

int GetX()
{
return _x;
}

Point& SetY(int y)
{
_y = y;
return *this;
}

int GetY()
{
return _y;
}

private:
int _x;
int _y;
};

#include "point.h"

// UPoint的目的是对Point和引用计数封装,用户是不可见的
class UPoint
{
friend class Handle_1;

private:
Point p;
int u;

UPoint():u(1){}

UPoint(int x,int y)
{
p.SetX(x);
p.SetY(y);
u = 1;
}

UPoint(const Point& rhs)
{
p = rhs;
u = 1;
}
};

6、现在考虑Handle_1的实现细节,
#include "u_point.h"

class Handle_1
{
public:
Handle_1():_up(new UPoint){}

Handle_1(int x,int y):_up(new UPoint(x,y)){}

Handle_1(const Point& rhs):_up(new UPoint(rhs)){}

~Handle_1()
{
subRef();
}

// copy构造,复制指针,增加引用
Handle_1(const Handle_1& rhs)
{
addRef(rhs._up);
}

// copy赋值,左边减少引用计数,判断是否delete,右边增加引用计数,考虑自我赋值
Handle_1& operator=(const Handle_1& rhs)
{
if(this != &rhs)
{
subRef();
addRef(rhs._up);
}

return * this;
}

int GetX()
{
return _up->p.GetX();
}

int GetY()
{
return _up->p.GetY();
}

Handle_1& SetX(int x)
{
_up->p.SetX(x);
return *this;
}

Handle_1& SetY(int y)
{
_up->p.SetY(y);
return *this;
}

private:
void addRef(UPoint* up) // 复制指针,增加引用
{
_up = up;
++_up->u;
}

void subRef() // 减少引用,判断是否delete
{
if(--_up->u == 0)
{
delete _up;
}
}

private:
UPoint* _up;
};

7、考虑下面的情况,
Handle_1 h1(3,4);
Handle_1 h2(h1);
h2.SetX(5);
int dd = h1.GetX();
dd的值是5,也就是说,多个句柄指向同一个对象,避免了不必要的对象复制,实现的是 指针语义。但是对于上面的情况,往往不是用户所期望的,
怎么解决这个问题?
8、使用写时拷贝,每次修改的时候重新创建一个对象。也就是说,修改的时候变成值语义,原对象h1是不可变对象,使用h2修改,会导致重新创建一个对象。
如下:
Handle_1& SetX(int x)
{
//_up->p.SetX(x);
if(_up->u == 1) // 当前是唯一的引用
{
_up->p.SetX(x);
}
else
{
--_up->u;
_up = new UPoint(x,_up->p.GetY());
}
return *this;
}

Handle_1& SetY(int y)
{
//_up->p.SetY(y);
if(_up->u == 1) // 当前是唯一的引用
{
_up->p.SetY(y);
}
else
{
--_up->u;
_up = new UPoint(_up->p.GetX(),y);
}
return *this;
}

时间: 2025-01-31 02:39:10

【C++沉思录】句柄1的相关文章

【C++沉思录】句柄2

1.[C++沉思录]句柄1 存在问题: 句柄为了绑定到Point的对象上,必须定义一个辅助类UPoint,如果要求句柄绑定到Point的子类上,那就存在问题了.2.有没有更简单的办法呢? 句柄使用Point*直接绑定到Point对象上(包括子类),为了保持多个句柄引用计数的一致性,使用int* 指向引用计数.3.代码如下:#include "point.h"class Handle_2{public: Handle_2():_p(new Point),_u(new int(1)){}

《C++沉思录》——类设计核查表、代理类、句柄类

<C++沉思录>集中反映C++的关键思想和编程技术,讲述如何编程,讲述为什么要这么编程,讲述程序设计的原则和方法,讲述如何思考C++编程. 一.类设计核查表 1.你的类需要一个构造函数吗? 2.你的数据成员都是私有的合理吗? 3.你的类需要一个无参的构造函数吗? 是否需要生成类对象的数组! 4.你的每一个构造函数都初始化所有的数据成员了吗? 虽然这种说法未必总是正确,但是要积极思考! 5.你的类需要析构函数吗? 6.你的类需要一个虚析构函数吗? 7.你的类需要一个拷贝构造函数吗? 8.你的类需

C++沉思录之二——虚函数使用的时机

虚函数使用的时机 为什么虚函数不总是适用? 1. 虚函数有事会带来很大的消耗: 2. 虚函数不总是提供所需的行为: 3. 当我们不考虑继承当前类时,不必使用虚函数. 必须使用虚函数的情况: 1. 当你想删除一个表面上指向基类对象,实际却是指向派生类对象的指针,就需要虚析构函数. C++沉思录之二--虚函数使用的时机,布布扣,bubuko.com

个性化的亲切——《沉思录》引发的感悟

记得初中那阵子,曾经追过明星,甚至美的标准也变成了他——恨不得所有的明星都是长的和他一样,唱的和他一样.除了他的歌,我几乎欣赏不了其他人的歌. 还记得差不多在那个年纪,曾经幻想过世界“大统”——我认为“大统”是达到“大同”,消弭纷争的有效方式.惭愧,后来知道希特勒也是这么想的.此乃后话,不提. 也几乎是那个时候,我不愿再做“出头鸟”,我相信“人多力量大”,我总愿意融在身边的“圈子”,不想显得自己不合群. 个性化,在我们的应试教育体制中从来都没有得到特别的提倡,如果没有良师益友的及时提点,一定会让

迷你MVVM框架 avalonjs 沉思录 第1节 土耳其开局

#cnblogs_post_body p{ text-indent:2em; margin-top: 1em; } 正如一切传说的开端那样,有一远古巨神开天辟地,然后就是其他半神喧宾夺主.我们对最巨贡献与创建力的远古巨神懵懂不知,却对巫师们的话语津津乐道.这同样也是我们前端的现实. MVVM是来自.NET,另一个遥远的界域.前端,相对于后端,怎么看都是蛮夷之地.JS这个肩负着前端一切交互工作的语言,竟然被视为恶魔,屡屡被屏蔽禁用.些微可用的脚本,变量与函数没有组织地野蛮生长着,直到JAVA的传教

Trie树沉思录(1)

发现自己已经很久没有写解题报告了,很大一部分是因为懒,做完题之后不想再怎样了~不过最近发现写解题报告确实是有好处的,一方面可以复习,一方面可以梳理.还有就是可以给自己的岁月留下一点什么东西~今天是五一劳动节,就应该要劳动!我要重新着手写我的博客了~ 最近几个星期都在研究字符串,有点难,不过到现在为止Trie数学得还算是有那么点意思,写篇博文来记录一下! (关于Trie数是什么东西我就不想写了,我只写我个人的一些思考) Trie树通过共享前缀来达到了节约内存的目标,十分的强大!关于他的实现大概有两

PHP沉思录-第六篇-Drupal的性能问题-左轻侯-《程序员》2008年11月号

创建时间:2008-11-09 01:12:51   最后修改时间:2008-11-09 01:12:51 本文发表在<程序员>杂志2008年第11期 PHP沉思录之六:Drupal的性能问题 左轻侯 Drupal是一个基于PHP的开源CMS系统,也是我认为技术上实现得最好的一个PHP应用.Drupal的架构非常优秀,通过微内核+plugin的方式,实现了极佳的扩展性,从而使Drupal远远超出一般的CMS这一范畴.从这个意义上来说,把Drupal称为Web OS似乎更加合适一些.关于Drup

迷你MVVM框架 avalonjs 沉思录 第3节 动态模板

模板的发明是编程史上的一大里程碑,让我们摆脱了烦锁且易出错的字符串拼接,维护性大大提高. 都在JSP,ASP时代,人们已经学会使用include等语句,将多个页面片断拼接成一个页面. 此外,为了将数据库中的数据或业务中用到的变量输出到页面,我们需要将页面某个地方标记一下,将变量塞到里面去. 最后,出于方便循环输出一组数据,就需要将each语句从HTML里撕开一道口子,加上其他什么if语句,页面上其实变撕裂成两部分 一种是与后端语言相近的逻辑部分,一个是够为纯净的HTML部分,到最后,模板引擎就发

C++沉思录之三——设计容器类

一.对容器的基本认识 总的来说,容器应该包含放在其中的对象的副本,而不是对象本身. 二.复制容器意味着什么? 通常将容器成为模板,而容器内的对象的类型就是模板参数.Container<T> 显然,如果复制容器只不过简单地使两个容器指向同一个底层对象,而不是复制容器内的元素,那么效率就会提高很多.毕竟,大多数复制是在调用函数时发生的,像下面这样编写代码会犯一个很常见的错误: void f(Container<T> ); 而用 void f(const Constainer<T&