句柄类, 来自于C++ 沉思录的概念
百度百科:
句柄(handle)是C++程序设计中经常提及的一个术语,它并不是一种具体的、固定不变的数据类型或实体,而是代表了程序设计中的一个广义的概念,句柄一般是指获取另一个对象的方法 —— 一个广义的指针,它的具体形式可能是一个整数、一个对象或就是一个真实的指针,而它的目的就是建立起与被访问对象之间的唯一的联系
句柄(handle)类,它允许在 保持代理的多态行为 的同时,还可以避免进行不必要的复制。
问题的提出
通常参数和返回值是通过复制传递的;
只要几个指针指向同一个对象,就必须考虑在什么时候删除这个对象;
在保持多态性的前提下避免复制对象;
handle类的对象 通常被绑定到 它们所控制的类的对象上,这些 handle的行为类似指针,所以人们有时也叫它们智能指针;
将handle直接绑定到对象p上,则我们的handle最好与p的内存分配和释放无关;
#include <iostream>
using namespace std;
class Point{
public:
Point():xval(0),yval(0) { } //缺省/无参构造
Point(int x, int y): xval(x),yval(y) { } //有参构造
int x() const { return xval; } //返回xval
int y() const { return yval; } //返回yval
Point& x(int xv) { xval = xv; return *this; } //更改xval值返回对象
Point& y(int yv) { yval = yv; return *this; } //更改yval值返回对象
private:
int xval, yval;
};
/* handle可以控制对Point副本的操作,
是一种只包含单个Point对象的容器
*/
class Handle{
public:
Handle(int x, int y){
p = new Point(x,y);
}
Handle(Point& p){
this->p = new Point(p.x(),p.y()); //创建一个p的副本
}
Point* operator->(){
return p;
}; //Point* add = h.operator->();
~Handle(){ delete p; }
private:
Point* p;
};
int main()
{
Point p(1,2);
/* 将handle直接绑定到对象p上,则我们的handle与p的内存分配和释放无关 */
Handle h0(123,456); //h0绑定到坐标123,456的Point
Handle h(p); //h创建一个p的副本,并将handle绑定到该副本,控制对副本的操作
cout << h->x() << endl; //使用 operator-> 将Handle的所有操作转发给相应的Point操作来执行
cout << h.operator->()->y() << endl; //h.operator->() 返回值Point*
cout << h0->x() << endl;
cout << h0.operator->()->y() << endl;
return 0;
}
View Code
简单的实现,必须明确地选择让我们的handle类支持哪些Point操作,避免使用operator->隐藏Point对象的真实地址
多个Handle对象指向同一个Point对象,而不复制Point对象,Point对象在没有Handle对象指向的时候释放
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;
};
//为了避免不必要的对象复制,得允许多个句柄绑定到单个对象上
//我们必须定义一个新的类来容纳一个引用计数和一个Point对象,称之为UPoint
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;//添加的
};
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)
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(); }
//指针语义, 通过任一指向Point对象的Handle对象,可以修改Point对象的值
Handle& Handle::x(int x0)
{
up->p.x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
up->p.y(y0);
return *this;
}
View Code
代码测试
#include <iostream>
using namespace std;
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;
};
//为了避免不必要的对象复制,得允许多个句柄绑定到单个对象上
//我们必须定义一个新的类来容纳一个引用计数和一个Point对象,称之为UPoint
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{
/*private:
class UPoint{ //可以设置为内部类,所有成员Handle均可见,外不可见
public:
int u;
Point p;
UPoint():u(1) { }
UPoint(int x, int y): p(x,y), u(1) { }
UPoint(const Point& p0):p(p0), u(1) { }
}; */
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 UPoint_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) //原来的Point对象一旦没有Handle对象指, 即释放
delete up;
//cout << "~Handle()\n"; //测试用
}
Handle::Handle(const Handle& h):up(h.up) { ++up->u; }
Handle& Handle::operator=(const Handle& h)
{
++h.up->u; // h指向的Point对象的指针数增一
if(--up->u == 0) //Handle对象原来指向的Point对象的指针数减一
delete up; //原来的Point对象一旦没有Handle对象指, 即释放
up = h.up; //Handle对象的指针, 指向h指向的Point对象
return *this;
}
int Handle::x() const { return up->p.x(); }
int Handle::y() const { return up->p.y(); }
//指针语义, 通过任一指向Point对象的Handle对象,可以修改Point对象的值
Handle& Handle::x(int x0)
{
up->p.x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
up->p.y(y0);
return *this;
}
//测试用
int Handle::UPoint_u(){
return up->u;
}
//多个Handle对象, 指向同一个Point对象p, 一样的坐标不一定是同一个对象
int main()
{
Handle h1(123,456);
Handle h2 = h1;
Handle h3;
h3 = h2;
h3.y(789);
cout << h3.UPoint_u() << endl;
cout << h2.y() << endl;
Handle h4(123,789);
cout << h4.UPoint_u() << endl;
return 0;
}
View Code
实现的改进,引用计数与Point对象本身分离,在句柄中额外加上一些信息 int * u
Handle类数据成员, 使用Point* ,不仅能够将一个Handle绑定到一个Point,还能将其绑定到一个继承自Point类的对象
Handle类,参数为 Point引用的复制构造函数,复制一个副本,即一个Point 对象副本,即参数的副本,该副本为动态构造
#include <iostream>
using namespace std;
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 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 handle_u(){ return *u; } //测试用
private:
Point* p;
int* u;
};
Handle::Handle():u(new int(1)), p(new Point) { }
Handle::Handle(int x,int y):u(new int(1)), p(new Point(x,y)) { }
Handle::Handle(const Point& p0):u(new int(1)), p(new Point(p0)) { }
Handle::~Handle()
{
if(--*u == 0){
delete u;
delete p;
}
}
Handle::Handle(const Handle& h):u(h.u), p(h.p) { ++*u; }
Handle& Handle::operator=(const Handle& h)
{
++*h.u; // h指向的Point对象的指针数增一
if(--*u == 0){ //Handle对象原来指向的Point对象的指针数减一
delete u;
delete p; //原来的Point对象一旦没有Handle对象指, 即释放
}
u = h.u;
p = h.p;
return *this;
}
int Handle::x() const { return p->x(); }
int Handle::y() const { return p->y(); }
//指针语义, 通过任一指向Point对象的Handle对象,可以修改Point对象的值
Handle& Handle::x(int x0)
{
p->x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
p->y(y0);
return *this;
}
//多个Handle对象, 指向同一个Point对象
int main()
{
Handle h1(123,456);
Handle h2 = h1;
Handle h3;
h3 = h2;
h3.y(789);
cout << h3.handle_u() << endl;
cout << h2.y() << endl;
Handle h4(123,789);
cout << h4.handle_u() << endl;
return 0;
}
View Code
总结
句柄通过绑定类的指针与绑定的类对象或其子类对象进行绑定,之后,通过句柄的计数器,使多个句柄与之进行绑定,只要有一个句柄指向被绑定对象,该对象都不会释放,不因其他绑定句柄改变指向或销毁而释放,保证了被绑定对象的安全,也保证了与之绑定的句柄都有具体的指向;绑定对象为动态构造 ,句柄的成员函数包装绑定类对象的类成员函数,使得句柄对象控制绑定对象的行为,与绑定对象行为相似
句柄的实质,就是为了指针的安全
句柄的 参数为类对象(非句柄类对象)的 复制构造函数,会复制类对象,然后绑定复制的类对象
绑定同一个类对象的句柄,其计数器空间、绑定的类对象的空间是一样的
成员变量会释放,只要计数器值不为0,句柄对象成员变量指向的空间不会释放(包括计数器空间,绑定类对象空间)
//测试代码
Handle h1(123,456);
Handle h2(123,456); //与h1绑定的不是同一个对象
{
Point p(8,9); // p出了作用域销毁
Handle h3(p); //用p的引用创建绑定对象, h3出了作用域销毁
h2 = h3; //改变h2指向
cout << h2.handle_u() << endl; //h2,h3绑定同一个对象
}
cout << h2.handle_u() << endl; //大括号作用域里绑定的对象依然存在
cout << h2.x() << "," << h2.y() << endl;
View Code
五、句柄计数器的抽象