代理类,句柄类, 来自于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; }
二、简单的实现,必须明确地选择让我们的handle类支持哪些Point操作,避免使用operator->隐藏Point对象的真实地址
通过无参构造,x,y参数构造,Point对象引用,Handle对象引用复制 (拷贝)构造,通过Handle对象对另一个Handle对象的赋值,使得多个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; }
代码测试

#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() "; //测试用 } 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; }
三、实现的改进,引用计数与Point对象本身分离,在句柄中额外加上一些信息 int * u
Handle类数据成员, 使用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; }
四、总结
句柄通过绑定类的指针与绑定的类对象或其子类对象进行绑定,之后,通过句柄的计数器,使多个句柄与之进行绑定,只要有一个句柄指向被绑定对象,该对象都不会释放,不因其他绑定句柄改变指向或销毁而释放,保证了被绑定对象的安全,也保证了与之绑定的句柄都有具体的指向;绑定对象为动态构造,句柄的成员函数包装绑定类对象的类成员函数,使得句柄对象控制绑定对象的行为,与绑定对象行为相似
句柄的实质,就是为了指针的安全

//测试代码 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;
五、句柄计数器的抽象