关于句柄的基础知识
什么是对象的句柄?
句柄术语一般用来指获取另一个对象的方法——一个广义的假指针。这个术语是(故意的)含糊不清的。
含糊不清在实际中的某些情况下是有用的。例如,在早期设计时,你可能不准备用句柄来表示。你可能不确定是否将一个简单的指针或者引用或者指向指针的指针或者指向引用的指针或者整型标识符放在一个数组或者字符串(或其它键)以便能够以哈希表(hash-table)(或其他数据结构)或数据库键或者一些其它的技巧来查询。如果你只知道你会需要一些唯一标识的东西来获取对象,那么这些东西就被称为句柄。
因此,如果你的最终目标是要让代码唯一的标识/查询一个Fred类的指定的对象的话,你需要传递一个Fred句柄给这些代码。句柄可以是一个能被作为众所周知的查询表中的键(key)来使用的字符串(比如,在std::map<std::string,Fred>或std::map<std::string,Fred*>中的键),或者它可以是一个作为数组中的索引的整数(比如,Fred* array = new Fred[maxNumFreds]),或者它可以是一个简单的 Fred*,或者它可以是其它的一些东西。
初学者常常考虑指针,但实际上使用未初始化的指针有底层的风险。例如,如果Fred对象需要移动怎么办?当Fred对象可以被安全删除时我们如何获知?如果Fred对象需要(临时的)连续的从磁盘获得怎么办?等等。这些时候的大多数,我们增加一个间接层来管理位置。例如,句柄可以是Fred**,指向Fred*的指针可以保证不会被移动。当Fred对象需要移动时,你只要更新指向Fred*的指针就可以了。或者让用一个整数作为句柄,然后在表或数组或其他地方查询Fred的对象(或者指向Fred对象的指针)。
重点是当我们不知道要做的事情的细节时,使用句柄。
使用句柄的另一个时机是想要将已经完成的东西含糊化的时候(有时用术语magic cookie也一样,就像这样,“软件传递一个magic cookie来唯一标识并定位适当的Fred对象”)。将已经完成的东西含糊化的原因是使得句柄的特殊细节或表示物改变时所产生的连锁反应最小化。举例来说,当将一个句柄从用来在表中查询的字符串变为在数组中查询的整数时,我们可不想更新大量的代码。
当句柄的细节或表示物改变时,维护工作更为简单(或者说阅读和书写代码更容易),因此常常将句柄封装到类中。这样的类常重载operator->和operator*算符(既然句柄的效果象指针,那么它可能看起来也象指针)。
内存句柄与指针的区别
指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据。Windows并不希望一般程序修改其内部数据结构,因为这样太不安全。所以Windows给每个使用GlobalAlloc等函数声明的内存区域指定一个句柄(本质上仍是一个指针,但不要直接操作它),平时你只是在调用API函数时利用这个句柄来说明要操作哪段内存。当你需要对某个内存进行直接操作时,可以使用GlobalLock锁住这段内存并获得指针来直接进行操作。
意见1:
句柄是指针的“指针”,使用句柄主要是为了利于windows在进程内存地址空间移动分配的内存块,以防止进程的内存空间被撕的四分五裂而存在过多的碎片。
意见2:
句柄是一些表的索引也就是指向指针的指针。间接的引用对象,windows可以修改对象的"物理"地址和描述器的值,但是句柄的值是不变的。
意见3:
句柄和指针都是地址,不同在于:
1,句柄所指的可以是一个很复杂的结构,并且很有可以是与系统有关的,比如说上面所说的线程的句柄,它指向的就是一个很类或者结构,他和系统有很密切的关系,当一个线程由于不可预料的原因,而终止时在系统就可以回它所占用的资料,如CPU,内存等等,反过来想可以知道,这个句柄中的某一些项,是与系统进行交互的。由于Windows系统,是一个多任务的系统,它随时都可能要分配内存,回收内存,重组内存。
2,指针它也可以指向一个复杂的结构,但是通常是用户定义的,所以的必需的工作都要用户完成,特别是在删除的时候。
但在VC++6.0中也有一些指针,它们都是处理一些小问题才用的,如最常见的字符的指针,它也是要用户处理的如果你动态分配了内存;但是Cstring就不要用户处理了,它其实是VC++中的一个类,所以的操作都由成员函数完成,产生(分配)由构造函数,删除(回收)由析构函数完成。
问:
你好,我在学习用vc++6.0编译多线程程序中遇到了很多句柄,但是不明白他的具体作用以及如何使用句柄,希望您能给我举几个具体实例,不甚感激!
比如说: HANDLE hThread,它是怎样具体使用的?
答:你使用CreateThead后函数会返回一个句柄,它代表这个线程。你可能会调用SetThreadPriority去修改线程的优先级,使用ResumeThread去重新开始一个线程的运行,在调用这些函数时你都需要告诉系统你到底要操作哪个线程,而刚才返回的句柄派上用处了,这些函数的第一个参数就是线程的句柄。
句柄和实例怎样区别?
从Visual C++的头文件来看,HANDLE被typedef为void的指针,那是指向未确定数据结构的指针:typedef void* HANDLE;
但是这并不说明任何问题,因为句柄远远不只是指向任意数据类型的指针。它是指向数据对象指针的指针。句柄的使用来源于早期的Windows,当时它只能在有限内存的机器中允许(因为当时内存昂贵也存储小)。为了留出足够的空间内存以运行其他程序,Windows经常将对象在内存中移动。但是如果进程已经有了指向该对象的指针,移动该对象就将使得指针无效。
为了处理这一问题,Microsoft使用系统指针以跟踪对象。程序不是直接使用指针,而是使用句柄,让它引用包含真实对象的地址。通过这一方法,Windows可以安全按照需要移动对象,然后更新参考中的指针,从而允许进程安全地访问对象。
当进程需要使用句柄访问对象时,它调用GlobalLocl()以锁定内存的对象。这时Windows将返回对象的实际地址,从而进程可以安全读写该对象。只要对象被进程锁定,Windows就不会去移动它。句柄包含的不只是对象的地址;比如,它还包含锁计数器,标识着多少进程已经请求过对象的地址。只要这个锁计数器大于0,Windows就不会移动该对象。
这个思想扩展到大部分对象上,包括文件,也由其句柄标识。
这时,句柄就是指向对象的指针,而该对象包含指向另一个对象的指针。这对于现在的机器有些奇怪。当Windows从单个分时共享程序向多任务操作系统转变时,有如此之多的遗留代码需要在新的操作系统上运行,于是句柄在这一转变中保留下来了。
句柄和指针的区别
看VC中总是出现这个句柄的概念,以前一直以为就是指指针,但是越看越觉得不是这么简单,于是本着有问题百度一下的原则,看到如下解释,很是经典:
csdn上有人说过:牧童遥指杏花村。
牧童的手为指针,杏花村的牌子为句柄,杏花村酒店为对象的实例.
句柄就是烤叉,用烤炉烤过鸭,鸡,牛,羊,狗么?
炉子里的东西是看不见,摸不到的,但你能用叉子去控制,至于叉子上的是什么,你放进去前应该记住。呵呵
句柄有时是指针,有时是索引,但他绝对是一把钥匙,内核句柄110的钥匙,GDI句柄是您的钥匙,只对您有效。
单从概念上讲,句柄指一个对象的标识,而指针是一个对象的首地址。从实际处理的角度讲,即可以把句柄定义为指针,又可以把它定义为同类对象数组的索引,这两种处理方法都有优缺点,至于选用哪种方式,完全应该看实际需要,这可以说是一种程序设计上的技巧。那种单纯认为句柄是指针或索引的想法都是机械的、不确切的。其实,在Windows中类似的处理是很多的、很灵活的。再具个相似的例子:
我们知道,在Windows中有个函数叫做CallWindowProc。
故名思义,它的作用就是向指定的窗口过程传递一个消息。你也许会想,既然我已经有了窗口过程的指针,为什么我不可以直接通过这个指针调用该函数(这是C语言的内建功能)?事实上,在Win16中确实可以这么做,因为GetWindowLong返回的确实是该函数的指针。但在Win32下,GetWindowLong返回的并不是该函数的指针,而是一个包含函数指针的数据结构的指针(MSDN上说返回的是一个窗口函数地址或它的句柄,就是指的这种情况)。该数据结构是可变的,但只要你使用CallWindowProc来调用的话是不会出错的。这里我们又看到使用句柄处理带来的好处。(补充说明一点:微软在这里之所以这么处理,是为了解决16位/32位以及ANSI/UNICODE的转化问题)
看来,句柄很多时候是一个用于描述和标记一个资源的数据结构的指针,而不是资源本身的指针,句柄中可能包含资源的指针,但是根多时候不仅仅是这样。