第六章的句柄,通過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;
}