《c++沉思录》学习笔记三——句柄类
来源:互联网 发布:阿里云服务器 知乎 编辑:程序博客网 时间:2024/05/17 22:24
地址:http://hi.baidu.com/zhanshaoxin/item/d43047cfb6ee5e3b99b4982b
前一篇文章提到了代理类,这个类能让我们在一个容器中存储类型不同但相互关联的对象。创建代理将会复制所代理的对象,如果想避免这些复制该怎么办?
通常参数和返回值是通过复制自动传递的,用引用传递参数可以避免这样的复制,但是这样对返回值来说不太容易(比如局部对象)。同样可以用指针来避免复制,但是使用指针有几个问题:
(1)使用对象指针比直接使用对象要困难。
(2)未初始化的指针非常危险,在c++实现中,只要对这样的指针实施复制操作就会出现问题。
(3)无论何时,只要有几个指针指向同一个对象,就必须考虑在什么时候删除这个对象。
为了能够在保持多态性的前提下避免复制对象,c++解决方法就是定义句柄类,将句柄类的对象绑定到它们所控制的类的对象上。
一 怎样绑定? 这里有两种选择:
(1)可以创建自己的对象并把它赋给一个handle去进行复制。
有对象p, 则 Handle h(p) 将创建p的一个副本,并将handle绑定到该副本。handle 可以控制对该对象副本的操作。
(2)可以把用于创建对象的参数传递给这个handle
有类Point ,创建一个对象方式为:Point p(123,456). 则Handle h(123, 456) 将创建一个句柄,该句柄绑定到point(123,456)。
从效果上说,handle就是一种只包含单个对象的容器。
二 已经将句柄绑定到对象,怎样获取对象?
要是一个handle在行为上类似一个指针,则可以使用operator->将handle的所有操作都转发给相应的Point操作来执行。Point* operator->(); Point * addr = h.operator->() 这样就可以获取对象地址了。如果我们希望handle能够对对象的分配和回收拥有完全的控制权,则最好能够阻止用户直接获得对象的实际地址。所以,如果想把真实的地址隐藏起来,就必须避免使用operator->,而且必须明确的选择让我们的handle类支持哪些Point操作。
三 句柄类已经设计好了,它到底怎样避免对象复制的?
考虑函数传参的问题,把一个句柄作为参数传给函数,我们希望的是复制句柄而不复制对象。为此,我们必须了解有多少个句柄绑定到同一个对象上,只有这样才能确定在何时删除对象,通常使用引用计数来达到这个目的。重新设计一个引用计数类,这个类包含一个引用计数和一个Point对象,称之为UPoint。个人感觉这个UPoint就是更高一层次的抽象,以后handle只要和UPoint打交道就行了,然后由UPoint和Point打交道。UPoint的全部构造方式和Point的一样。这个类纯粹是为了实现而设计的,实际运用中根本没有这个类。因此其所有成员函数都设为私有。以下是几个类的实现代码,有详细注释:
#include <iostream>using namespace std;class Point{private:int xval, yval;public:Point() : xval(0), yval(0){ cout << "Point" << endl;}Point(int x, int y) : xval(x), yval(y){ cout << "Point" << endl;}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(){ cout << "~Point" << endl;}};class UPoint //这个类对用户来说是不可见的, 就是一个间接层{friend class Handle;Point p; //Point对象int u; //计数器UPoint():u(1) //提供所有的point的构造方式{ cout << "UPoint::" << u << endl;}UPoint(int x, int y):p(x, y), u(1){ cout << "UPoint::" << u << endl;}UPoint(const Point &p0) : p(p0), u(1){ cout << "UPoint::" << u << endl;}~UPoint(){ cout << "~UPoint::" << u << endl;}};class Handle{private:UPoint *up; //和间接层UPoint打交道了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():up(new UPoint){cout << "Handle::" <<up->u << endl; }Handle::Handle(int x, int y):up(new UPoint(x, y)) //按创建Point的方式构造handle,handle->UPoint->Point{cout << "Handle::" <<up->u << endl;}Handle::Handle(const Point &p):up(new UPoint(p)) //创建Point的副本{cout << "Handle::" <<up->u << endl;}Handle::~Handle(){if(--up->u == 0){ cout << "~Handle::" <<up->u << endl; delete up;}else{ cout << "~Handle::" <<up->u << endl;}}Handle::Handle(const Handle &h):up(h.up){++up->u; //此处复制的是handle,但是底层的point对象并未复制,只是引用计数加1cout << "Handle::" <<up->u << endl;}Handle& Handle::operator=(const Handle &h){++h.up->u; //右边的对象引用计数加1,左边的减1if(--up->u == 0) delete up;up = h.up;cout << "Handle::" <<up->u << endl;return *this;}int Handle::x() const{return up->p.x();}int Handle::y() const{return up->p.y();}void f(Handle h) //测试函数,复制handle,但未复制Point{cout << h.x() << h.y() << endl;}int main(){Handle h(3,5);cout << h.x() << h.y() << endl;f(h);return 0;}
个人评价:句柄和指针有许多相同之处,这所以使用句柄而不是指针是因为指针具有安全性问题,直接把内存地址给用户使用是十分危险的,这里的句柄在实现是采用了指针加使用计数器,这样可以避免更多的内存复制。
- 《c++沉思录》学习笔记三——句柄类
- 《C++ 沉思录》阅读笔记——句柄类
- 《C++ 沉思录》阅读笔记——句柄类
- 《C++沉思录》——类设计核查表、代理类、句柄类
- 沉思录句柄类之二
- C++沉思录句柄类总结1
- C++ 沉思录 学习笔记
- <C++沉思录>学习笔记
- C++沉思录学习笔记
- 《C++ 沉思录》阅读笔记——代理类
- C++ 沉思录》阅读笔记——类的反思
- 《C++ 沉思录》阅读笔记——代理类
- c++沉思录中代理类和c++primer中句柄类的对比
- C++沉思录句柄C6
- C++沉思录句柄2
- C++沉思录句柄3
- C++沉思录(序幕)学习笔记
- 《c++沉思录》学习笔记 day1
- 百度地图的学习
- js数组操作
- WINCE下窗口的创建
- 2013-04-02 阶段性复习
- Apache中VirtualHost配置说明
- 《c++沉思录》学习笔记三——句柄类
- 一,VBO简介
- 采样函数
- 程序由源码->可执行文件 编译+链接 流程
- ChangeDisplaySettingsEx函数
- EditorGridPanel中控制每一单元格是否可编辑
- Web开发者应该掌握的Firebug技巧
- Zookeeper集群安装
- ListView项(Item)的三种布局使用例子