句柄类的实现:分割引用计数和原数据
——《C++沉思录》第7章 句柄:第二部分
《句柄类》中,介绍了一种句柄的简单实现,其基本结构就是定义了三个类:Point类、Handle类、UPoint类。其中Point为实际的原数据类,Handle为句柄类,UPoint为实际操作的数据类,其成员除了Point的一个对象外还有个int型的技术。
这里我们先将UPoint中的两个成员分割,之后再将int型技术进行封装成一个标准的引用技术类UseCount。
1.Point和int
删除UPoint类,并将Handle中的UPoint* up指针删除,添加Point和int指针。
#include <iostream> using namespace std; class Point { private: int xval; int yval; 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; } }; // 句柄类 class Handle { private: // 更改:删除原来的 UPoint* up 指针 // 添加指向Point的指针和执行int的指针 Point* p; // 指向原数据 int* u; // 指向引用计数 public: Handle(); Handle(int, int); Handle(const Point&); Handle(const Handle&); Handle& operator = (const Handle&); ~Handle(); int x() const; int y() const; Handle& x(int); Handle& y(int); void test() const; }; void Handle::test() const { cout << *u << endl; } 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(const Handle& h) : u(h.u), p(h.p) { ++*u; } Handle::~Handle() { if (--*u == 0) { // 两个delete delete u; delete p; } } Handle& Handle::operator = (const Handle& h) { ++*h.u; if (--*u == 0) { delete u; delete p; } u = h.u; p = h.p; return *this; } int Handle::x() const { return p->x(); } int Handle::y() const { return p->y(); } // 对Handle& Handle::x(int)和Handle& Handle::y(int)的实现有两种方式: // 指针语义和值语义 // 其中值语义的实现方式称作为写时赋值(copy on write) // 指针语义 Handle& Handle::x(int x0) { p->x(x0); return *this; } Handle& Handle::y(int y0) { p->y(y0); return *this; } //// 值语义 //Handle& Handle::x(int x0) //{ // if (*u > 1) // { // --*u; // u = new int(1); // p = new Point(*p); // } // p->x(x0); // return *this; //} // //Handle& Handle::y(int y0) //{ // if (*u > 1) // { // --*u; // u = new int(1); // p = new Point(*p); // } // p->y(y0); // return *this; //} int main() { Point p1; Handle* ph1 = new Handle(p1); ph1->test(); Handle* ph2 = new Handle(*ph1); ph1->test(); ph2->test(); delete ph1; ph2->test(); return 0; }
2.抽象引用计数——UseCount
将int型的计数抽象化,使其具备自身赋值、拷贝功能。将一些实现从Handle中抽离出来。
具体说明可以详见代码中的注释。
#include <iostream> using namespace std; class Point { private: int xval; int yval; 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; } }; class UseCount { private: int* p; public: UseCount(); UseCount(const UseCount&); ~UseCount(); // 返回p指向的计数值,用于测试 int val() const; // 检测原数据是否指服务于一个句柄 bool only() const; // 增加一个函数用来代替operator=函数 // 该函数在Handle的operator=中调用 bool reattach(const UseCount&); private: // 将operator=私有化 UseCount& operator = (const UseCount&); public: // 如果引用计数为1,则直接修改 // 如果大于1,则new一个新的引用计数,在Handle写入时new一个新的原数据 // 该函数用于实现值语义,写时复制 bool makeonly(); }; UseCount::UseCount() : p(new int(1)) {} UseCount::UseCount(const UseCount& u) : p(u.p) { ++*p; } UseCount::~UseCount() { if (--*p == 0) { delete p; } } int UseCount::val() const { return *p; } bool UseCount::only() const { return *p == 1; } bool UseCount::reattach(const UseCount& u) { ++*u.p; if (--*p == 0) { delete p; p = u.p; return true; } p = u.p; return false; } bool UseCount::makeonly() { if (*p == 1) { return false; } --*p; p = new int(1); return true; } // 句柄类 class Handle { private: Point* p; // 指向原数据 // 将原来的int* u抽象化为UseCount u UseCount u; public: Handle(); Handle(int, int); Handle(const Point&); Handle(const Handle&); Handle& operator = (const Handle&); ~Handle(); int x() const; int y() const; Handle& x(int); Handle& y(int); void test() const; }; void Handle::test() const { cout << u.val() << endl; } // 由于UseCount类有自身的构造函数,所以Handle构造函数的实现部分可以不用涉及u的构造 Handle::Handle() : p(new Point) {} Handle::Handle(int x, int y) : p(new Point(x, y)) {} Handle::Handle(const Point& p0) : p(new Point(p0)) {} Handle::Handle(const Handle& h) : u(h.u), p(h.p) {} Handle::~Handle() { if (u.only()) { delete p; } } Handle& Handle::operator = (const Handle& h) { // 这里忽略对引用计数的操作,因为已经在UseCount的reattach中操作完毕 if (u.reattach(h.u)) { // 如果reattach返回true,说明u这个引用计数值原来为1,所以赋值需要将原数据*p删除 delete p; } p = h.p; return *this; } int Handle::x() const { return p->x(); } int Handle::y() const { return p->y(); } // 对Handle& Handle::x(int)和Handle& Handle::y(int)的实现有两种方式: // 指针语义和值语义 // 其中值语义的实现方式称作为写时赋值(copy on write) //// 指针语义 //Handle& Handle::x(int x0) //{ // p->x(x0); // return *this; //} // //Handle& Handle::y(int y0) //{ // p->y(y0); // return *this; //} // 值语义 Handle& Handle::x(int x0) { if (u.makeonly()) { p = new Point(*p); } p->x(x0); return *this; } Handle& Handle::y(int y0) { if (u.makeonly()) { p = new Point(*p); } p->y(y0); return *this; } int main() { Point p1; Handle* ph1 = new Handle(p1); ph1->test(); Handle* ph2 = new Handle(*ph1); ph1->test(); ph2->test(); delete ph1; ph2->test(); return 0; }
3.总结
上文《句柄类》和本文,我们一共介绍了三种句柄类的实现方式,第一种是将原数据和引用计数封装为一个类,进而组合为句柄类;第二种是将原数据和引用计数分离,各自为政,其中引用计数的赋值、拷贝操作是在句柄类中完成的;第三种方式是将引用计数进行抽象化,引用计数的赋值、拷贝操作都在其自身实现中完成,继而被句柄类调用。