C++ 句柄java 对象句柄 搞混了

C++ primer plus 基础(27)
对象(object):
是一件事、一个实体、一个名词,可以获得的东西,可以想象有自己的标识的任何东西。对象是类的实例化。一些对象是活的,一些对象不是。比如这辆汽车、这个人、这间房子、这张桌子、这株植物、这张支票、这件雨衣。 概括来说就是:万物皆对象。
一个标识符,是拿来标识对象或者项目的。它就像我们的车牌号一样,每一辆注册过的车都会有一个确定的号码,不同的车号码各不相同,但是也可能会在不同的时期出现两辆号码相同的车,只不过它们不会同时处于使用之中罢了
对象句柄 就是一个对象变量。
&&& 每种编程语言都有自己的数据处理方式。有些时候,程序员必须时刻留意准备处理的是什么类型。您曾利用一些特殊语法直接操作过对象,或处理过一些间接表示的对象吗(C或C++里的指针)?
所有这些在Java里都得到了简化,任何东西都可看作对象。因此,我们可采用一种统一的语法,任何地方均可照搬不误。但要注意,尽管将一切都“看作”对象,但操纵的标识符实际是指向一个对象的“句柄”(Handle)。在其他Java参考书里,还可看到有的人将其称作一个“引用”,甚至一个“指针”。可将这一情形想象成用遥控板(句柄)操纵电视机(对象)。只要握住这个遥控板,就相当于掌握了与电视机连接的通道。但一旦需要“换频道”或者“关小声音”,我们实际操纵的是遥控板(句柄),再由遥控板自己操纵电视机(对象)。如果要在房间里四处走走,并想保持对电视机的控制,那么手上拿着的是遥控板,而非电视机。
此外,即使没有电视机,遥控板亦可独立存在。也就是说,只是由于拥有一个句柄,并不表示必须有一个对象同它连接。所以如果想容纳一个词或句子,可创建一个String句柄:
但这里创建的只是句柄,并不是对象。若此时向s发送一条消息,就会获得一个错误(运行期)。这是由于s实际并未与任何东西连接(即“没有电视机”)。因此,一种更安全的做法是:创建一个句柄时,记住无论如何都进行初始化:
String s = &asdf&;
然而,这里采用的是一种特殊类型:字串可用加引号的文字初始化。通常,必须为对象使用一种更通用的初始化类型。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:219553次
积分:5158
积分:5158
排名:第3905名
原创:167篇
转载:14篇
译文:16篇
评论:114条
难度:高级
类型:实战教学
难度:高级
类型:实战教学
难度:高级
类型:实战教学
阅读:6273
文章:11篇
阅读:17494
文章:16篇
阅读:20778C++句柄类解析
  引题:在C++中,对于运行时类型识别问题。在程序中使用引用或者指针在运行时动态识别对象类型。然而使用指针或者引用却增加了用户负担(在继承体系中,没有明确的基类到派生类的转换,必须用户显示转换并将结果对象加入容器中。但是这样的做法结果却是派生对象部分成员是未初始化的)。
对于这一问题,可以将对象指针 保存在容器中来解决。但此时,用户必须明确容器中指针和 对象的同步性(不能只有指针而对象不存在或者收指针不存在,对象存在)。
更好的解决方案就是句柄类了:
  C++ 中一个通用的技术是定义包装(cover)类或句柄类。句柄类存储和管 理基类指针。指针所指对象的类型可以变化,它既可以指向基类类型对象又可以 指向派生类型对象。用户通过句柄类访问继承层次的操作。因为句柄类使用指针 执行操作,虚成员的行为将在运行时根据句柄实际绑定的对象的类型而变化。因 此,句柄的用户可以获得动态行为但无须操心指针的管理。
  包装了继承层次的句柄有两个重要的设计考虑因素:
对任何保存指针的类一样,必须确定对复制控制做些什 么。包装了继承层次的句柄通常表现得像一个智能指针 或者像一个值。
句柄类决定句柄接口屏蔽还是不屏蔽继承层次,如果不屏蔽继承层次,用 户必须了解和使用基本层次中的对象。
①指针型句柄:
  句柄包装指针,用户可以将该句柄类当作指针使用,却不用去管理指针指向的对象。(句柄类更像是一个中介控制者)
定义方案:
1. 使用类包装指针,包装计数器(每个对象都有各自的这两个成员)
2.使用类包装指针,包装计数器指针(资源和计数器共享)
句柄类除了定义构造,拷贝构造,赋值,还需要定义引用,解引用(使之看起来更像是指针)
对于构造函数:
1个默认构造函数初始化成员为0;
1个构造函数声明指定对象类型的句柄。
那么问题来了,如果用户并不知晓用 继承体系中具体哪个层次对象进行初始化,如何做呢。
解决这个问题的通用方法是定义虚操作进行复制,我们称将 该操作命名为 clone。(克隆):
  对于继承层次中的每个类,增加一个虚克隆函数eg:
class Item_base
virtual Item_base* clone() const
return new Item_base(*this);
有了克隆函数那么 句柄类的定义如下:
Sales_item::Sales_item(const Item_base &item):
p(item.clone()), use(new std::size_t(1))
对于继承层次中 &如果需要逻辑比较函数,一个好的做法是 定义内部比较,比较内部核心成员。
inline bool
compare(const Sales_item &lhs, const Sales_item &rhs)
return lhs-&book() & rhs-&book();
使用带关联容器的比较器
要有效地工作,关联容器需要对每个操作使用同一比较函数。然而,期望用 户每次记住比较函数是不合理的,尤其是,没有办法检查每个调用使用同一比较 函数。因此,容器记住比较函数是有意义的。通过将比较器存储在容器对象中, 可以保证比较元素的每个操作将一致地进行。
这种做法,实质上就是使用函数指针,函数回调实现真正的比较。
// type of the comparison function used to order the multiset
typedef bool (*Comp)(const Sales_item&, const Sales_item&);
关联容器的每个构造函数使我们能够提供比较函数的名 字。可以这样定义使用 compare 函数的空 multiset:
std::multiset&Sales_item, Comp& &items(compare);
multiset 是STL中的联合容器。。在头文件&set&中,具体用法请参看《STL源码剖析》
参考(C++primer)
阅读(...) 评论()7341人阅读
谈谈windows 核心编程系列(25)
2012、、29
转载请注明出处!
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&内核对象
&&&&本章讨论的是相对抽象的概念,不涉及任何具体的内核对象的细节而是讨论所有内核对象的共有特性。
首先让我们来了解一下什么是内核对象。内核对象通过来创建,每个内核对象是一个数据结构,它对应一块内存,由操作系统内核分配,并且只能由操作系统内核访问。在此数据结构中少数成员如安全描述符和使用计数是所有对象都有的,但其他大多数成员都是不同类型的对象特有的。内核对象的数据结构只能由操作系统提供的访问,应用程序在内存中不能访问。调用创建内核对象的函数后,该函数会返回一个句柄,它标识了所创建的对象。它可以由进程的任何线程使用。在位系统中,句柄是一个位值。位系统中则是位值。
很多人对句柄到底是什么东西很疑惑。有人说是指针有人说是索引。其实句柄仅仅是独立于每个进程的句柄表的一个索引。在每个进程中都存在一个句柄表,列出了所有本进程内可以使用的句柄
。它只是一个有数据结构组成的数组,每个结构都包含一个指向内核对象的指针、访问掩码、继承标识等,而句柄仅仅是句柄表数组的下标。由于每个进程都存在句柄表,因此句柄是独立于进程的,虽然将一个进程的句柄传给另一个进程不一定会失败,但是它引用的是另一个进程完全不同的内核对象。后面的跨进程边界共享内核对象将介绍如何跨界成共享内核对象。
&&&&内核对象的所有者是操作系统内核,而非进程。也就是说多个进程可以共享一个内核对象。内核对象数据结构内有一个使用计数成员,它是所有对象都有的一个成员,标识该内核对象被引用的次数。刚创建时使用计数被初始化为,如果有另一个进程获得对此内核对象的访问后,使用计数就会递增。一个使用此内核对象的进程终止后或是对此内核对象调用,操作系统内核会自动递减内核对象的使用计数。一旦计数变为,操作系统内核就会销毁对象。
&&&&安全描述符用以描述内核对象的安全性。它描述了内核对象的拥有者,那组用户可以访问此对象,那组用户无访问权限。安全描述符对应一个数据结构:结构,几乎内核对象在创建时都需要传入此结构,但是大部分情况下都是传入,表示使用默认的安全性。
&&&&除了使用内核对象,应用程序还需要使用其他类型的对象,如菜单、窗口、鼠标光标等,这些属于用户对象或对象,而非内核对象。要判断一个对象是不是内核对象,最简单的方法就是查看创建这个对象的函数,几乎所有的创建内核对象的函数都需要指定安全属性信息的参数,而用于创建用户对象的函数都不需要使用安全描述符。
&&&&一个进程在刚被创建时,它的句柄表是空的。当进程内的一个线程调用创建内核对象的函数时,内核将为这个对象分配并初始化一个内存块,然后扫描进程的句柄表,查找一个空白的记录项,并对其进行初始化。指针成员将会被初始化为内核对象的地址,继承标志也会被设置。
&&&&用于创建内核对象的函数都会返回一个与进程相关的句柄,此句柄可由属于该进程的所有线程引用。调用一个函数,如果它需要一个内核对象句柄的参数,就必须为它传递一个句柄。在内部,这个函数会查找进程的句柄表,获得目标内核对象的地址然后对此数据结构进行操作。如果我们直接使用其他进程的的句柄,那么实际引用的只是那个进程句柄表中位于同一索引的内核对象,它们仅仅是索引值相同而已。创建内核对象的函数在失败时会返回。但有时也有的函数会返回,如它返回的是而不是。失败的原因可能是内存不足或是没有权限。这在检查内核对象是否创建成功时要特别注意。
&&&&当进程不再使用某内核对象时应该调用来向系统表明我们已经结束使用此对象。在内部该函数会扫描进程的句柄表,如果句柄是有效的,系统就获得此内核对象的数据结构的地址,并将此结构的使用计数成员递减。如果使用计数变为,句柄表对应的记录项将会被清除,内核对象将被销毁,所占内存将会被释放。此后再在此进程内使用此句柄将会发生未知错误。因为调用后此内核对象不知是否已经被销毁,如果没有销毁那么此次对此句柄的使用将没有问题。如果此内核对象已被销毁,且句柄表对应项已经被其他项占据,此时操作的将是另一个内核对象,可能发生无法预知的错误。因此在调用后要最好将原来的变量赋值为。
&&&&即使在进程结束了对内核对象的访问后,没有调用进程终止时,操作系统也会确保进程所使用的所有资源都被释放。系统自动扫描进程句柄表,将所有内核对象的使用计数都减。同样如存在使用计数为的内核对象,它就会被释放。但是这毕竟不是个好习惯,如果我们开发的程序是长时间运行的程序,由于没有主动调用,进程已经不再使用的内核对象仍然得不到释放,越往后运行系统所占内存就越大。这跟内存泄露很类似,自己开辟的堆空间不再使用时要自己主动释放。对于内核对象这也同样适用。在任务管理器的列可以查看每个进程占用的内核对象数。
&&&&前面说到另一个进程不能直接使用一个进程的内核对象的句柄。注意这里使用直接二字,这并不意味着其他进程不能使用此内核对象,虽然句柄是独立于进程的,但是内核对象是归系统内核所有,各个进程都可以使用,只是要使用还需要费一番周折。接下来将介绍跨进程边界共享内核对象。
&&&&跨进程共享内核对象是必要的,:是利用文件映射对象可以在两个进程间共享数据块。:互斥量、信号量和事件允许不同进程的线程同步执行。
&&&&跨进程共享内核对象方法之一:使用对象句柄继承
&&&&只有进程之间属于父子关系时才可以使用对象句柄继承。当父进程创建一个内核对象时,父进程必须向系统指出它希望这个对象的句柄是可继承的。为了创建可继承句柄父进程必须分配并初始化一个结构,并将这个结构的地址传递给函数。如:
SECURITY_ATTRIBUTES&
sa.nLength=sizeof(sa);
sa.lpSecurityDescriptor=NULL;使用默认安全性。
sa.bInheritHandle=TRUE;是此句柄可以继承。
HANDLE&mutex=CeattMutex(&sa,FALSE,NULL);
&&&&以上代码初始化了一个结构,表明使用默认安全性来创建此对象,且返回的对象时可继承的。
&&&&句柄表的每个记录中还有一个指明该句柄是否可继承的标志位,如果在创建内核对象的时候将作为的参数传入,则返回的句柄是不可继承的,标志位为。
下一步是由父进程创建子进程,这是通过实现的,此函数第四章会详细介绍,此处仅仅注意参数。如果在创建进程时,此参数被设为,则表明不希望子进程继承父进程句柄表中的可继承句柄。如为,则表明希望子进程继承父进程句柄表中的可继承句柄。注意只有可继承句柄才可以被继承。
新创建的进程句柄表为空,由于我们希望它继承父进程句柄表,此时系统会遍历父进程句柄表,对它的每一个项进行检查,将所有的可继承的句柄的项全部复制到子进程的句柄表中。在子进程的句柄表中,复制项的位置与它在父进程句柄表中的位置是完全一样的,这是非常重要的。它意味着在父进程和子进程中,对一个内核对象进行标识的句柄是完全一样的。除了复制句柄表,系统还会递增每个可继承句柄的使用计数。为了销毁内核对象,父进程和子进程必须都不再使用才可以。这可以通过和进程终止来实现。注意:句柄进程仅仅发生在进程刚被创建时,如果此后父进程又创建了新的内核对象,那么此时子进程不会继承这些新创建的内核对象句柄。
&&&&如果父进程创建了一个内核对象,得到一个不可继承的句柄,但是后来父进程又希望后来创建的子进程继承它,这怎么办呢?这可以通过使用修改内核对象句柄的继承标志&。它需三个参数,第一个标识了一个有效句柄,第二个标识想更改哪些标识。第三个标识指出想把它设成什么。这个标识可以是
HANDLE_FLAG_INHERI,打开句柄继承标识。
HANDLE_FLAG_PROTECT_FROM_CLOSE//不允许关闭句柄。
&&&&GetHandleInformation可以用来返回指定句柄的当前标识。
&&&&跨进程共享内核对象方法之二:命名对象
&&&&许多对象都可以进行命名,但并不是全部。因此该方法有一定局限性。有些创建内核对象的函数都有一个指定内核对象名称的参数,如果传入,则会创建一个匿名的内核对象。如果不为,则应该传入一个一结尾的字符串。所有这些命名对象共享一个名字空间。即使它们类型不同,如果已存在同名对象,创建就会失败。
&&&&一旦一个命名的内核对象被创建,其他进程(不仅仅是子进程)可以通过调用或是函数来访问它。当使用函数时,系统会检查是否存在一个传给此函数的名字,如果确实存在一个这样的对象,内核执行安全检查,验证调用者是否有足够的安全权限。如果是,系统就会在此进程的句柄表中查找空白记录项,并将其初始化为指向已存在的命名的内核对象。两个进程的句柄不一定相同,这没有任何影响。由于内核对象被再一次引用,所以其引用计数会被递增。
&&&&为了防止在创建一个命名对象时,仅仅打开了一个现有的而不是新建的,可以在创建后调用获得详细信息。
&&&&使用函数可以打开已存在的命名内核对象,如果没有找到这个名称的内核对象将返回。如果找到这个名称的内核对象,但类型不同,函数仍返回。只有当名称相同且类型相同的情况下系统才会进一步检查访问权限。如果有权访问,系统就会更新此进程的句柄表,并递增内核对象的引用计数。在函数中也可以指定此句柄的继承性。
&&&&Open*和的区别:如果对象不存在,会创建它,将会调用失败。
&&&&我们经常使用命名的内核对象来防止运行一个程序的多个实例。可以在函数中建立一个命名对象,返回后调用如果返回表明此程序的另一个实例在运行。
&&&&&关于终端服务命名空间不再介绍,只需知道它是为了防止命名内核对象命名冲突而设计的。以后有需要的可以仔细研究下。
&&&&跨进程共享内核对象方法之三:复制对象句柄
&&&&实现该方法使用的是函数。
bool&DuplicateHandle(
&&&HANDLE&hSourceProcessHandle,
HANDLE&hSourceHandle,
HANDLE&hTargetProcessHandle,
PHANDLE&phTargethandle
DWORD&ddwDesiredAccess,
BOOL&bInheritHandle,
DWORD&dwOptions
这个函数的功能就是获得进程句柄表的一个记录项,然后在另一个进程中创建这个记录项的副本。第一个和第三个参数分别标识源进程和目标进程内核对象句柄。第二个参数标识要复制的内核对象句柄,它可以指向任何类型的内核对象。第四个参数是一个目标句柄的地址,用来接收复制到的值。
函数将源进程中的句柄信息复制到目标进程所标识的句柄表中。第五第六个参数用以指定此内核对象句柄在目标进程句柄表中应该使用何种访问掩码和继承标志。
dwOption参数可以是和任一个。如果是标志,将向函数表明我们希望目标句柄拥有与源进程句柄一样的访问掩码,此时会忽略。如果是标志,会关闭源进程的句柄,此时将一个内核对象从一个进程复制到另一个进程,但是内核对象的使用计数不受影响。
&&&&&GetCurrentProcess可以返回当前进程的句柄,但是它是一个伪句柄。其值为,返回的也是伪句柄其值为,它们并不在句柄表中而仅仅代表当前进程和当前线程。
&&&& 这一章很抽象,原来学习的时候读了很多遍也不是很明白,后来干脆跳过去了,一段时间的学习之后再回来看看,发现竟然非常简单。所以有时候学习不能钻牛角尖该跳过就跳过。随着学习的深入,你所站的高度、看问题的角度都会不一样。理解起来也会更容易!!!
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:818990次
积分:11778
积分:11778
排名:第869名
原创:149篇
转载:56篇
评论:504条
文章:24篇阅读:24796
文章:9篇阅读:10389
如果你对我的博客有任何的意见和建议或是想和我交流技术、学习和生活,请和我联系!!;QQ:; ;
(1)(1)(2)(1)(1)(1)(1)(4)(1)(1)(3)(8)(2)(2)(3)(7)(10)(15)(13)(5)(6)(5)(5)(4)(13)(37)(5)(1)(7)(6)(2)(2)(8)(4)(3)(4)(6)(1)(6)C++中的句柄
这个句柄只是从英文handle翻译过来的,只问句是什么意思难以解释,这个是我从别人的空间收集的信息,
功能上的理解:
& 什么是"句柄"(handle),handle的本意是把柄,把手的意思。是你与打交道的东东。
举个通俗的例子,比如你考上了大学,入学后,学校(操作系统)会给你一个学生证号。
注意,这个号码是学校指定的,你无法自选。
有了这个号码(学生证,假设一证多用)享受学校提供的服务:
如你就可以去图书馆借书,去食堂吃饭,去教室上课等等。
& 但你不能到食堂里买啤酒,因为学校不允许这种服务。
而在中系统提供的服务就是API调用,你有了HANDLE,就可以理直气壮地向系统提出调用API的服务。
而指针的权力就大多了,有了指针你可以到处去喝酒,打架,学校(操作系统)管不着,
所以句柄和指针的区别在于句柄指针调用系统提供的服务。
而句柄虽然是一个能相互区别的号码,但与我们普通的ID号又有区别,
普通的ID号是可以由程序员自己定义的,而句柄不行,它是对象生成是系统指定的,
是为了区别系统中存在的各个对象,这个句柄不是由程序员符给的。
概念上的理解
1。句柄,是整个windows编程的基础,一个句柄是指使用的一个唯一的整数值,
是指一个四字节长的数值,用于标志应用程序中的不同对象和同类对象中的不同的实例,
诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。
应用程序能够通过句柄访问相应的对象的信息。
2。句柄不是一个指针,程序不能利用它句柄来直接阅读文件中的信息。
如果句柄不用在I/O文件中,它是毫无用处的。
3。句柄是windows用来标志应用程序中建立的或是使用的唯一整数,
windows使用了大量的句柄来来标志很多对象。
机制上的理解
前面的分析很经典,但我认为有一点必须指出的。如果不对,请各位指证。
句柄是指针,一点不假,但是这个指针又与C中的指针有不同之处。
因为Windows是一个多任务的系统,其内存是可以移动的,
这样的话如果某一时刻有一个指针指向一块内存,之后的某个时刻却被系统移走了,
如果你再用这个指针的话就会出错。
为了解决这一问题,windows在系统专区开一块内存用于存放句柄,这个句柄的值就是一个地址,
当这一块内存被移走后,windows就修改这个句柄的值,再访问这块内存时,句柄的值总是有效的。
正因为这样当你使用GlobalAllo分配的内存时,如果你指定这块内存的属性是固定的,
那么它的返回值可以直接给一个指针,如果是可以移动的,
返回值就必须给一个句柄,你就必须先GlobalLock后才能使用。
这是我对句柄理解,不知道对不对?
其实,句柄是一个指向指针的指针。即:
在windows程序设计中,句柄仅是一个应用程序用来识别某些事情的数字
如果想更透彻一点地认识句柄,我可以告诉大家,句柄是一种指向指针的指针。
我们知&道,所谓指针是一种内存地址。
应用程序启动后,组成这个程序的各对象是住留在内的&。
如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。
但是,如果您真的这样认为,那么您就大错特错了。
我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,
Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。
对象被移动意味着它的地址变化&了。如果地址总是如此变化,我们该到哪里去找该对象呢?
为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,
用来专门&登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。
Wi ndows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。
这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。
这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统&。
句柄地址(稳定)→记载着对象在内存中的地址→对象在内存中的地址(不稳定) →实际对象
&&&&但是,必须注意的是程序每次从新启动,系统不能保证分配给这个程序的句柄还是
原来的那个句柄,而且绝大多数情况的确不一样的。假如我们把进入电影院看电影看成
是一个应用程序的启动运行,那么系统给应用程序分配的句柄总是不一样,这和每次电
影院售给我们的门票总是不同的一个座位是一样的道理。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 对象句柄 的文章

 

随机推荐