• 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

     
     
  • 相关阅读:
    CSS强制换行
    Android 软件开发的盈利模式
    比较Collection 和Collections的区别
    Jsp 中taglib标签的妙用
    常用搜索引擎大全
    Jsp 中登陆界面的实现方法
    Jsp 中JavaScript 和 Java代码的异步执行特点
    Jsp struts 标准配置测试版
    div demo
    多线程模拟银行业务调度系统
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/2612550.html
Copyright © 2020-2023  润新知