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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

实现效果:

before h2.x(5):h.x=3
after h2.x(5): h.x=5, h2.x=5
 
 
  • 1
  • 2

定义文件

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;
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

实现文件

#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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

值语义句柄

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

#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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

实现效果:

before h2.x(5):h.x=3
after h2.x(5): h.x=3, h2.x=5
 
 
  • 1
  • 2

如果需要值语义,就必须保证所改动的那个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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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

优化

由于代码

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

面要在每一个改变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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

附最终的代码
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;
};
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

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;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
					<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-8cccb36679.css" rel="stylesheet">
            </div>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值