C++ 沉思录——Chap6:句柄2

上一回讨论的句柄技术有一个明显的缺点:为了将句柄捆绑到类T的对象上,必须要新定义一个具有类型为T的成员对象的新类。
这个毛病相当麻烦,如果想新设计一个类的句柄类,就需要新定义两个类。
 
C++之父提到过一种定义句柄类的技术可以弥补这一个缺点,主要思想就是将引用技术从数据中分离出来,把引用计数放到句柄类自己的对象之中。
class Handle
{
     public:
          // 和前面一样
     private:
          Point *p;
          int *u;
};
这里不再有指向UPoint的指针,我们使用指向Point的指针和指向一个int的指针表示引用计数。使用Point* 使得我们不仅能够将一个Handle绑定到一个Point,还能将其绑定到一个继承自Point 的类的对象。
 
此时我们的Handle类要在构造析构的时候要处理两个指针,比如:
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 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;
}
 
从这些实现可以看出,引用计数与Point类没有能够很好的协同,每次都需要单独处理它们。
 
如果想让引用计数和Point类更好的协同作用可以对引用计数进行抽象,用一个辅助类来实现引用计数。
 
class UseCount
{
     public:
          UseCount();
          UseCount(const UseCount &);
          UseCount & operator = (const UseCount &);
          ~UseCount();
 
     private:
          int *p;
};
UseCount成员函数的实现就比较简单了:
UseCount :: UseCount() : p(new int(1)) {}
UseCount :: UseCount(const UseCount &u) : p(u.p) { ++*p; }
UseCount :: ~UseCount {  if ( --*p == 0) delete p;  }
 
现在重写Handle类:
class Handle
{
     public:
          // 和之前一样
     private:
          UseCount u;
          Point *p;
};
 
现在我们来看成员函数的实现,就相对比之前简单了:
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) { }

 

在写析构函数的时候,我们需要判断引用计数是否为0,以便知道要不要删除句柄的数据。
我们可以让UseCount 类来提供这个数据,通过一个类成员方法来藐视UseCount 对象是不是唯一指向它的计数器对象:
class UseCount
{
     public:
          // 和前面一样
     private:
          bool only();
};
 
bool UseCount :: only() {     return *p == 1; }
 
当UseCount 有了 only() 成员方法,Handle类的析构函数就可以这样写了:
 
Handle :: ~Handle()
{
     if( u.only() )
          delete p;
} 
当Handle类进行复制操作的时候,我们需要对引用计数值增1,或者减1,可能还要删除一个计数器。
但是现阶段我们设计的UseCount 类和Hanlde 类都不支持上面的操作。注意到我们引入UseCount类的原因是使引用计数的处理和Point 协同起来。因此我们将这些操作放在UseCount中来实现,可以增加下面的成员函数:
 
bool UseCount :: reattach( const UseCount & u)
{
     ++*u.p;
     if ( --*p == 0)
     {
          delete p;
          p = u.p;
          return true;
     }
     p = u.p;
     return false;
}
现在有了reattach() 成员方法之后,Handle类的赋值操作可以这样写:
Handle & Handle :: operator = (const Handle & h)
{
     if(u.reattach(h.u))
          delete p;
     p = h.p;
     return *this;
} 
 
最后,如果我们要改变Point (注意前面的一切Handle、UseCount 类都是为了管理和控制Point类,我们真正需要操作的数据其实是Point 类 , 虽然都是在Handle类层进行操作以达到操作Point对象的目的),也就是对Point对象的单个元素进行读取和写入。当有多个句柄关联同一个Point对象时,我们对某个句柄操作需要改变该Point对象,但是其他句柄关联所持有的信息不需要改变,此时我们就需要对Point进行复制(记得前面引入Handle 的作用之一也就是避免不必要的复制,但是这里就需要进行复制了!)。
因此可以再UseCount类中在增加一个成员函数,用来对引用计数进行适当的控制。
bool UseCount :: makeonly()
{
     if ( *p == 1)
          return false;// 这里的意思就是说,只有一个Handle关联该对象,因此可以直接进行改变,不需要复制对象
     --*p;
     p = new int (1);
     return true;
} 
 
Handle 类的存取函数可以这样写:
int Handle :: x() const
{
     return p->x();
}
 
Handle & Handle :: x(int x0)
{
     if ( u.makeonly() )
          p = new Point( *p);
     p->x(x0);
     return *this;
}

 

现在列出全部代码:
/*
为了将句柄捆绑到类T的对象上,必须要新定义一个具有类型为T的成员对象的新类。
这个毛病相当麻烦,如果想新设计一个类的句柄类,就需要新定义两个类。
C++之父提到过一种定义句柄类的技术可以弥补这一个缺点,主要思想就是将引用技术从数据中分离出来,
把引用计数放到句柄类自己的对象之中。

这个策略的一个重要优势:UseCount类可以在不了解其使用者任何信息的情况下与之合为一体。这样一来,
我们就可以把这个类当成句柄实现的一部分,与各种不同的数据结构协同工作。 
*/

#include <iostream>
using namespace std;
//-----------------------------------------
class Point
{
private:
    int xval,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& operator=(const UseCount&);
    ~UseCount();
    bool only();
    bool reattach(const UseCount&);
    bool make_only();
};
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::make_only()
{
    if (*p==1)
        return false;
    --*p;
    p=new int(1);
    return true;
}
//-------------------------------------------

class Handle
{
private:
    Point* p;
    UseCount u;
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);
};
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)
{
    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();
}
Handle& Handle::x(int x0)
{
    if (u.make_only())
        p=new Point(*p);
    p->x(x0);
    return *this;
}
Handle& Handle::y(int y0)
{
    if (u.make_only())
        p=new Point(*p);
    p->y(y0);
    return *this;
 
}
//---------------------------------------------------

int main()
{
    Handle h(3,4);
    Handle h2 = h;
    h2.x(5); 
    int n = h.x();
    cout<<n<<endl;
    return 0;
}

运行结果为:3

 
 
原文地址:https://www.cnblogs.com/zhuyp1015/p/2612550.html