C++沉思录-第6章 句柄

前面说的代理类,这个类能让我们在一个容器中存储类型不同但相互关联的对象。这种方法需要为每一个对象创建一个代理,并要将代理存储在容器中。创建代理将会复制所代理的对象,就像复制代理一样。
如果想避免这些复制该怎么做呢,通常采用一种叫做句柄的类(handle)

指针语义句柄

语义句柄拥有指针类似的功能。即一处改了,另一处也会被修改。
测试文件:

#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

定义文件

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 UPoint {
    friend class Handle;
    Point p;
    int u;
    UPoint() : u(1) {}
    UPoint(int x, int y) : p(x, y), u(1) {}
    UPoint(const Point& p0) : p(p0), u(1) {}

};

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:
    UPoint * up;
};

实现文件

#include "stdafx.h"
#include "Point.h"


Handle::Handle() : up(new UPoint()) {}
Handle::Handle(int x, int y) : up(new UPoint(x, y)) {}
Handle::Handle(const Point& p) : up(new UPoint(p)) {}
Handle::~Handle()
{
    if (--up->u == 0)
        delete up;
}
Handle::Handle(const Handle& h) :up(h.up) { ++up->u; }
Handle& Handle::operator=(const Handle& h)
{
    ++h.up->u;
    if (--up->u == 0)
        delete up;
    up = h.up;
    return *this;
}

int Handle::x() const { return up->p.x(); }
int Handle::y() const { return up->p.y(); }

Handle& Handle::x(int x0)
{
    up->p.x(x0);
    return *this;
}

Handle& Handle::y(int y0)
{
    up->p.y(y0);
    return *this;
}

值语义句柄

值语义句柄拥有值类似的功能。即一处改了,另一处不会被修改。
测试文件:

#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

如果需要值语义,就必须保证所改动的那个UPoint对象不能同时被任何其他的Handle所引用。这倒不难,只要看看引用计数即可。如果是1,则说明handle是唯一一个使用该UPoint对象的句柄;其他情况下,就必须复制UPoint对象,使得引用计数变成1.
修改代码:

Handle& Handle::x(int x0)
{
    if (up->u != 1) {
        --up->u;
        up = new UPoint(up->p);
    }
    up->p.x(x0);
    return *this;
}

Handle& Handle::y(int y0)
{
    if (up->u != 1) {
        --up->u;
        up = new UPoint(up->p);
    }
    up->p.y(y0);
    return *this;
}

这一技术通常称为copy on write(写时复制)。其优点是只有在绝对必要时复制,从而避免不必要的复制,而且额外开销也只有一丁点儿。在涉及值语义的句柄的类库中,这一技术经常用到。

优化

由于代码

if (up->u != 1) {
        --up->u;
        up = new UPoint(up->p);
    }

面要在每一个改变UPoint对象的函数中重复使用,这暗示我们可以设计一个private成员函数对前面进行改写

void Handle::copy_on_write()
{
    if (up->u != 1) {
        --up->u;
        up = new UPoint(up->p);
    }
}

Handle& Handle::x(int x0)
{
    copy_on_write();
    up->p.x(x0);
    return *this;
}

Handle& Handle::y(int y0)
{
    copy_on_write();
    up->p.y(y0);
    return *this;
}

附最终的代码
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 UPoint {
    friend class Handle;
    Point p;
    int u;
    UPoint() : u(1) {}
    UPoint(int x, int y) : p(x, y), u(1) {}
    UPoint(const Point& p0) : p(p0), u(1) {}

};

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:
    UPoint * up;
};

Point.cpp

#include "stdafx.h"
#include "Point.h"


Handle::Handle() : up(new UPoint()) {}
Handle::Handle(int x, int y) : up(new UPoint(x, y)) {}
Handle::Handle(const Point& p) : up(new UPoint(p)) {}
Handle::~Handle()
{
    if (--up->u == 0)
        delete up;
}
Handle::Handle(const Handle& h) :up(h.up) { ++up->u; }
Handle& Handle::operator=(const Handle& h)
{
    ++h.up->u;
    if (--up->u == 0)
        delete up;
    up = h.up;
    return *this;
}

int Handle::x() const { return up->p.x(); }
int Handle::y() const { return up->p.y(); }


void Handle::copy_on_write()
{
    if (up->u != 1) {
        --up->u;
        up = new UPoint(up->p);
    }
}

Handle& Handle::x(int x0)
{
    copy_on_write();
    up->p.x(x0);
    return *this;
}

Handle& Handle::y(int y0)
{
    copy_on_write();
    up->p.y(y0);
    return *this;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永远的麦田

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值