第六章的句柄,通过UPoint类封Point实现句柄的绑定。
UPoint类的使用只是为了实现引用计数,因此,本章的任务是不再实现额外的UPoint,只将引用计数进行单独考量。
简单的引用计数
句柄:第二部分,分离引用计数
将引用计数从数据中分离出来,把引用计数放入它自己的对象中。
引用计数的分离
指针语义实现
先上Point类代码:
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; }
~Point() {}
private:
int xval;
int yval;
};
把引用计数从Point中分离出来的实现方法:
class Handle {
public:
// 和前面一样
private:
Point* p;
int* u; //指向引用计数的指针
}
大家发现,类UPoint消失了,不再有指向UPoint类的指针了。我们用一个int的指针表示引用计数。
使用Point*而不是UPoint*是很重要的,因为正是Point*使我们不仅能够将一个Handle绑定到一个Point,还能将其绑定到一个继承自Point的类的对象。
贴出完整的代码实现:
头文件.h
#pragma once
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; }
~Point() {}
private:
int xval;
int 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);
private:
Point * p;
int * u;
};
实现文件.cpp
#include "stdafx.h"
#include "Point.h"
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& p) : u(new int(1)), p(new Point()) {}
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;
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 x0)
{
p->x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
p->y(y0);
return *this;
}
最后附上测试代码:
// ConsoleApplication2.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>
#include "Point.h"
using namespace std;
int main()
{
Handle h(3, 4);
Handle h2 = h;
cout << "before h2.x(5):h.x=" << h.x() << endl;
h2.x(5);
cout << "after h2.x(5): h.x=" << h.x() << ", h2.x=" << h2.x() << endl;
return 0;
}
/* 运行结果:
before h2.x(5):h.x=3
after h2.x(5): h.x=5, h2.x=5
请按任意键继续. . .
*/
值语义的实现
由运行结果可以看出,以上实现的是指针语义的句柄,如果要实现值语义的句柄,就需要用到copy_on_write技术,实现起来也很简单
首先实现copy_on_write函数
void Handle::copy_on_write()
{
if (*u != 1) {
--*u;
p = new Point();
u = new int(1);
}
}
然后在需要改变x或y的地方增加对copy_on_write的调用
Handle& Handle::x(int x0)
{
copy_on_write(); // 新增调用
p->x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
copy_on_write(); // 新增调用
p->y(y0);
return *this;
}
最后附测试代码和运行结果
// ConsoleApplication2.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>
#include "Point.h"
using namespace std;
int main()
{
Handle h(3, 4);
Handle h2 = h;
cout << "before h2.x(5):h.x=" << h.x() << endl;
h2.x(5);
cout << "after h2.x(5): h.x=" << h.x() << ", h2.x=" << h2.x() << endl;
return 0;
}
/* 运行结果:
before h2.x(5):h.x=3
after h2.x(5): h.x=3, h2.x=5
请按任意键继续. . .
*/
对引用计数的抽象
我们已经知道引用计数通常从1开始,这就告诉我们缺省构造函数应该为:
UseCount::UseCount() : p(new int(1)) {}
我们也知道从一个UseCount构造另一个会使两者都指向相同的计数器,并递增计数器的值:
UseCount::UseCount(const UseCount& u) : p(u.p) { ++*p; }
销毁一个UseCount会使计数器的值减1,删除计数器则会返回0
UseCount::~UseCount() {
if (--*p == 0)
delete p;
}
现在我们可以重写Handle类了:
class Handle {
pulbic:
// 和前面的一样
private:
Point* p;
UseCount u;
}
由于抽象的逻辑稍微复杂,这儿不细描述,稍后补上,直接上抽象后的代码:
#pragma once
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; }
~Point() {}
private:
int xval;
int yval;
};
class UseCount {
public:
UseCount();
UseCount(const UseCount&);
~UseCount();
public:
bool only();
bool makeonly();
bool reattach(const UseCount&);
private:
UseCount& operator=(const UseCount&);
private:
int* p;
};
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:
void copy_on_write();
private:
Point * p;
UseCount u;
};
实现文件.cpp
#include "stdafx.h"
#include "Point.h"
UseCount::UseCount() : p(new int(1)) {}
UseCount::UseCount(const UseCount& u) : p(u.p) { ++*p; }
UseCount::~UseCount() {
if (--*p == 0)
delete p;
}
bool UseCount::only() { 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;
}
Handle::Handle() : p(new Point()) {}
Handle::Handle(int x, int y) : p(new Point(x, y)) {}
Handle::Handle(const Point& p) : p(new Point()) {}
Handle::~Handle()
{
if (u.only()) {
delete p;
}
}
Handle::Handle(const Handle& h) :u(h.u), p(h.p) { }
Handle& Handle::operator=(const Handle& h)
{
if (u.reattach(h.u))
delete p;
p = h.p;
return *this;
}
int Handle::x() const { return p->x(); }
int Handle::y() const { return p->y(); }
void Handle::copy_on_write()
{
if (u.makeonly())
p = new Point(*p);
}
Handle& Handle::x(int x0)
{
copy_on_write();
p->x(x0);
return *this;
}
Handle& Handle::y(int y0)
{
copy_on_write();
p->y(y0);
return *this;
}