C++沉思錄-第7章 句柄二 引用計數的分離

第六章的句柄,通過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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章