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;
}

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

  1. 【C++沉思录】句柄2

    1.[C++沉思录]句柄1 存在问题: 句柄为了绑定到Point的对象上,必须定义一个辅助类UPoint,如果要求句柄绑定到Point的子类上,那就存在问题了.2.有没有更简单的办法呢? 句柄使用Po ...

  2. 生活沉思录 via 哲理小故事(四)

    1.围墙里的墓碑 第一次世界大战期间,驻守意大利某小镇的年轻军官结识了镇上的牧师.虽然军官信仰信教,而牧师是天主教牧师,但两人一见如故. 军官在一次执行任务中身负重伤,弥留之际嘱托牧师无论如何要把自己 ...

  3. 生活沉思录 via 哲理小故事

    本文转载:http://www.cnblogs.com/willick/p/3174803.html 1.小托蒂的悲剧 意大利小男孩托蒂,有一只十分奇怪的眼睛,因为从生理上看,这是一只完全正常的眼睛, ...

  4. 生活沉思录 via 哲理小故事(一)

    1.小托蒂的悲剧 意大利小男孩托蒂,有一只十分奇怪的眼睛,因为从生理上看,这是一只完全正常的眼睛,但却是失明的. 原来,托蒂刚出生时,这只眼睛轻度感染,曾用绷带缠了两个星期.这对常人来说几乎没有人任何 ...

  5. Atitit。 沉思录 与it软件开发管理中的总结 读后感

    Atitit. 沉思录 与it软件开发管理中的总结 读后感 1. <沉思录>,古罗马唯一一位哲学家皇帝马可·奥勒留所著 2 2. 沉思录与it软件开发管理中的总结 2 2.1. 要有自己的 ...

  6. c++沉思录 学习笔记 第六章 句柄(引用计数指针雏形?)

    一个简单的point坐标类 class Point {public: Point():xval(0),yval(0){} Point(int x,int y):xval(x),yval(y){} in ...

  7. c++学习书籍推荐《C++沉思录》下载

    百度云及其他网盘下载地址:点我 编辑推荐 经典C++图书,应广大读者的强烈要求再版 目录 第0章 序幕第一篇 动机第1章 为什么我用C++第2章 为什么用C++工作第3章 生活在现实世界中 第二篇 类 ...

  8. C++模板沉思录(上)

    花下猫语: 在我们读者群里,最近出现了比较多关于 C++ 的讨论,还兴起了一股学习 C++ 的风气.樱雨楼小姐姐对 C++ 的模板深有研究,系统地梳理成了一篇近 4 万字的文章!本文是上篇,分享给大家 ...

  9. C++模板沉思录

    0 论抽象--前言 故事要从一个看起来非常简单的功能开始: 请计算两个数的和. 如果你对Python很熟悉,你一定会觉得:"哇!这太简单了!",然后写出以下代码: def Plus ...

随机推荐

  1. OperationalError: (2002, “Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)”)

    OperationalError: (2002, “Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld ...

  2. android shape的使用

    文章出自:http://www.cnblogs.com/cyanfei/archive/2012/07/27/2612023.html <?xml version="1.0" ...

  3. Mysql --分区表(6)Hash分区

    HASH分区 HASH分区主要用来分散热点读,确保数据在预先确定个数的分区中尽可能平均分布.对一个表执行HASH分区时,MySQL会对分区键应用一个散列函数,以此确定数据应当放在N个分区中的哪个分区 ...

  4. Android性能优化方法(八)

    Android SDK tools目录下提供一个观察布局的工具,层级观察器(Hierarchy Viewer).Hierarchy Viewer工具是一个非常好的布局优化工具,同时,你也可以通过它学习 ...

  5. background-position的百分比

    看到了几篇文章,总结下先: 1. background-position是依赖于no-repeat的,在repeat的状态下和默认的状态下(默认即为repeat),background-positio ...

  6. alert()、confirm()和prompt()的区别

    1.警告消息框alertalert 方法有一个参数,即希望对用户显示的文本字符串.该字符串不是 HTML 格式.该消息框提供了一个“确定”按钮让用户关闭该消息框,并且该消息框是模式对话框,也就是说,用 ...

  7. Log4j 配置数据库连接池(将日志信息保存到数据库)

    org.apache.log4j.jdbc.JDBCAppender 是利用传统的 JDBC 连接方法,这种方式连接数据库效率低下,为了解决这个问题,现在自定义一个 Log4j 的 Appender, ...

  8. Hibernate SQL 方言(hibernate.dialect)

    RDBMS Dialect DB2 org.hibernate.dialect.DB2Dialect DB2 AS/400 org.hibernate.dialect.DB2400Dialect DB ...

  9. java获取classpath文件路径空格转变成了转义字符%20的问题

    java获取classpath文件路径空格转变成了转义字符%20的问题 这个问题很纠结,服务器的文件路径带有空格,空格被转化是%20了,悲剧就出现了 下面展示一段代码String path = get ...

  10. SQL Server 利用游标解决Tempdb究极竞争-DBA-程序员需知

    SQL Server tempdb分配竞争算是DBA老生常谈的问题了,几乎现在所有的DBA都知道多建几个文件来解决/缓解问题.但是深层次的的竞争依旧不可避免.这里给大家剖析下游标在tempdb中的特点 ...