句柄类的实现:分割引用计数和原数据

句柄类的实现:分割引用计数和原数据

         ——《C++沉思录》第7章 句柄:第二部分

         《句柄类》中,介绍了一种句柄的简单实现,其基本结构就是定义了三个类:Point类、Handle类、UPoint类。其中Point为实际的原数据类,Handle为句柄类,UPoint为实际操作的数据类,其成员除了Point的一个对象外还有个int型的技术。

         这里我们先将UPoint中的两个成员分割,之后再将int型技术进行封装成一个标准的引用技术类UseCount。

       1.Pointint

         删除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.总结

         上文《句柄类》和本文,我们一共介绍了三种句柄类的实现方式,第一种是将原数据和引用计数封装为一个类,进而组合为句柄类;第二种是将原数据和引用计数分离,各自为政,其中引用计数的赋值、拷贝操作是在句柄类中完成的;第三种方式是将引用计数进行抽象化,引用计数的赋值、拷贝操作都在其自身实现中完成,继而被句柄类调用。

原文地址:https://www.cnblogs.com/unixfy/p/3460262.html