第六章:句柄类
代理类: 能在一个容器中存贮类型不同但相互关联的对象。创建一个代理,并要将代理存储在容器中。创建代理将会复制所代理的对象,就象复制代理一样。
问题:怎么才能避免这些复制呢?
6-1 问题
某些类,避免复制好处?对象会很大;不能轻易被复制的资源,比如文件;某些其他的数据结构已经存储了对象的地址,把副本的地址插入到那些数据结构中代价会非常大;对象代表着网络连接另一端的其他对象;一个多态性的环境中,我们能知道对象基类的类型,但是不知道对象本身的类型或者怎样复制这种类型的对象。
未初始化的指针是非常危险的。
无论何时,只要有几个指针指向同一个对象,就必须考虑要在什么时候删除这个对象。
Handle类:这些类的对象通常被绑定在他们控制的类的对象上。
6-2 一个简单的类
class Point{
public:
Point():xval(0),yval(0) {}
int x() const { return xval; }
int y() const { return yval; }
Point& x(int xv) { xval = xv; return *this; }
Point& y(int yv) { yval=yv; return *this; }
private:
int xval, yval;
};
6-3 绑定到句柄
Point p;
Handle h(p);
一旦用户删除p;handle 又会怎样?
Handle应该控制和销毁对象。
6-4 获取对象
class Handle {
public:
Point* operator->()
//...
};
Point* addr = h.operator->();
如果想把真实的地址隐藏起来,就必须避免使用operator->
6-5 简单的实现
class Handle {
public:
Handle();
Handle(int, int);
Handle(const Point&);
Handle(const Handle*);
Handle& operator=(const Handle&);
~Handle();
int x() const;
Handle& x(int);
int y() const;
Handle& y(int);
private:
//...
};
6-6引用记数行句柄
为什么使用句柄:之一避免不必要的对象复制。得允许多个句柄绑定到单个对象上。
引用计数不能是句柄的一部分?如果这样干,那么每一个句炳都必须知道跟它一起被绑定到同一个对象的其他所有句柄的位置,惟有如此才能去更新其他句柄的引用计数数据。也不能是对象的一部分?因为那样要求我们重写已经存在的对象类。
因此:必须定义一个新类(注意搞明白每个分析,不要求快)。
class UPoint {
// 所有成员都是私有的
friend class Handle;
Point p;
int u;
UPoint() :u(1) {}
UPoint(int x, int y) :p(x, y), u(1) {}
UPoint(const Point& p0) :p(p0), u(1) {}
};
class Handle {
public:
//和以前一样
Handle();
Handle(int, int );
Handle(const Point&);
Handle(const Handle&);
Handle& operator=(const Handle&);
~Handle();
int x() const;
Handle& x(int);
int y() const;
Handle& y(int);
private:
//添加的
Upoint *up;
}
为什么都是私有的?这样怎样访问他呢?为什么把Point 的构造函数照搬过来?
Handle::Handle():up(new UPoint) {}
Handle::Handle(int x, int y):up(new UPoint(x, y)) {}
Handle::Handle(const Point& p) : up(new UPoint(p)) {}
Handle::~Handle()
{
if(--up->u ==)) //书本上错误,少u
delete up;
}
Handle::Handle(const Handle& h) : up(h.up) { ++up->u; }
赋值操作符稍微复杂一点?左侧句柄指向的目标将会被改写。
如果两个句柄引用同一个对象能正确工作吗?
Handle& Handle::operator=(const Handle& h)
{
++h.up->u;
if(--up->u ==0)
delete up;
up = h.up;
return *this;
}
存取函数也是简单明了!
int Handle::x() const { return up->p.x(); }
int Handle::y() const { return up->p.y(); }
6-7写时复制
从实现角度:将Handle类设计成“无需对Point 对象进行复制”的相式。关键是:是否希望句炳类在用户面前的行为也是这样的?
Handle h(3, 4);
Handle h2 = h;
h2.x(5);
int n = h.x(); //3 or 5值语义是3;指针语义是5
如果是值语义:一点不明白,为什么如果是侄语义,就必须保证所改动的那个Upoint对象不能同时被任何其他的Handle所引用?
Handle& Handle::x(int x))
{
if (up->u != 1) {
--up->u;
up = new UPoint(up->p);
}
up->p.x(x0);
return *this;
}
一点不明白?为什么上面需要在每个改变Upoint对象的成员函数中重复,为什么暗示我们设计一个private成员函数,以保证我们的Handle引用计数为1。
这个技术通常成为 copy on write (写时复制)。优点是绝对必要时才进行复制。
如果是指针语义
Handle& Handle::x(int x0)
{
up->p.x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
up->p.y(y0);
return *this;
}
6-8 讨论
程序员希望根据上下文和运用方式,分析清楚指针语义或者值语义。为什么:对象和值之间的差别只在要改变对象时才显示出来。换句话:值和不可变的对象是无法区分的。
通过比较原对象的地址和副本的地址,能够判断对象是否被复制过。只是一个很细微的差别,如果两个互为副本的对象之间只有地址不同,这种区别就不那么有用了。((:不明白)
调试的程序:再好的车,发动不起来也没有用.
#include
class Point{
public:
Point():xval(0),yval(0) {}
Point(int x, int y):xval(x),yval(y) {}
int x() const { return xval; }
int y() const { return yval; }
Point& x(int xv) { xval = xv; return *this; }
Point& y(int yv) { yval=yv; return *this; }
private:
int xval, yval;
};
class UPoint {
// 所有成员都是私有的
friend class Handle;
Point p;
int u;
UPoint() :u(1) {}
UPoint(int x, int y) :p(x, y), u(1) {}
UPoint(const Point& p0) :p(p0), u(1) {}
};
class Handle {
public:
//和以前一样
Handle();
Handle(int, int );
Handle(const Point&);
Handle(const Handle&);
Handle& operator=(const Handle&);
~Handle();
int x() const;
Handle& x(int);
int y() const;
Handle& y(int);
int u() {return up->u;};
private:
//添加的
UPoint *up;
};
Handle::Handle():up(new UPoint) {}
Handle::Handle(int x, int y):up(new UPoint(x, y)) {}
Handle::Handle(const Point& p) : up(new UPoint(p)) {}
Handle::~Handle()
{
if(--up->u ==0) //书本上错误,少u
delete up;
}
Handle::Handle(const Handle& h) : up(h.up) { ++up->u; }
Handle& Handle::operator=(const Handle& h)
{
++h.up->u;
if(--up->u ==0)
delete up;
up = h.up;
return *this;
}
int Handle::x() const { return up->p.x(); }
int Handle::y() const { return up->p.y(); }
Handle& Handle::x(int x)
{
if (up->u != 1) {
--up->u;
up = new UPoint(up->p);
}
up->p.x(x);
return *this;
}
Handle& Handle::y(int y)
{
if (up->u != 1) {
--up->u;
up = new UPoint(up->p);
}
up->p.y(y);
return *this;
}
int main()
{
Handle a(3, 4);
Handle b;
b=a;
Handle c=a;
Handle d=b;
// delete d;
cout<//3
cout<//3
cout<//输出为4;
}
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/409557/viewspace-891797/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/409557/viewspace-891797/