于是周日晚上就赶回学校打听选导师的事儿,事后证明,我的担心是多余的,好多人都没去,而且讲的东西邮件里也都写了。
- Nov 28 Fri 2008 19:29
选导师风波
于是周日晚上就赶回学校打听选导师的事儿,事后证明,我的担心是多余的,好多人都没去,而且讲的东西邮件里也都写了。
- Nov 25 Tue 2008 03:47
由wm开放到ytht
虽然只有不到三年的校内平台,但wm上仍然还是有很大的变化的。且不说本科生们拿着校内平台这种病态当常态,反而开始担心起一旦开放起来,长此以往该如何如何了;小圈子和封闭的心态已经到了很夸张的地步。比如在tri这种大版,也可以堂而皇之的骂人nc啊、sb啊而不会被封禁;P、T两大向来互相看不惯也一直是不可否认的事实,但以前至少照顾面子,也仍然会有“挑起争端”之类的封禁。所以我常常搞不清楚的是,现在的孩子还哪来的优越感,以及何以认为“校内平台”要比“公网论坛”“素质高”了。因为开放的消息传的很快,所以世界各地的ip也陆续出现在sysop的临时封禁公告中,起因理所当然的是错误密码记录过多。于是有小p孩儿嘴巴很贱的说,“这就是开放的结果啊。。。”说什么好呢,不惮以最坏的恶意揣测师兄师姐们么?至少在我当年eGust的好友名单中,已经有coincidence、iAlex、ninanina等01的老家伙被活活饿死了,这还不算因为一些变故。余不忍独生,是以eGust那个id也在差不多的时候跟着01的36x生命力的一批一起挂了。当然真正原因并不是我说的那么煽情,而是当时发生了一些不快的事情。具体是什么我记不清了,可见不是什么大事,于是我就索性与01的老家伙们同生死了,反正在wm上死掉了也不可惜;表达的即是,ytht都不在了,我还在乎你这太监站么?
- Nov 16 Sun 2008 19:12
又一个学期过去了
- Nov 07 Fri 2008 16:41
jj的《我还想她》mv
- Nov 07 Fri 2008 16:35
理解Delphi对象
Delphi语言在Pascal语言基础上增加了面向对象的支持,本文通过代码对Delphi对象的实现进行分析,希望能够对理解Delphi对象有所帮助。
首先对Windows程序使用内存的方式进行一个简单的介绍。每个Windows进程有独立的虚拟内存地址(32位为4GB寻址,其中低2GB为进程独立使用,高2GB位系统保留),无法直接访问其他进程的内存。每个进程可以使用的内存主要分为以下几个部分:
1. 程序空间:Delphi默认设置将程序代码加载到$00400000,可通过修改链接器选项中的Image Base修改。包括程序代码、数据段等,这部分由编译器决定,在一般情况下不需要关心。
2. 全局变量空间:包括全局变量与全局常量,其中后者所在区域被设为只读段,试图进行写访问将会引发异常;
3. 栈(Stack):默认的栈大小为1M,可通过修改链接器选项中的Stack Size来改变栈大小。在计算机科学中,栈是一种线性的后进先出(LIFO)的数据结构;而程序的内存栈用于分配局部变量、记录函数调用参数等功能,每次执行到一个函数/过程时,编译器首先会把参数压入栈空间(包括寄存器),然后记录栈的增加,以便创立独立的局部变量,执行完毕后再减回原来的栈大小。所以如果程序需要大量的函数调用的话(比如递归),就要很注意对栈的使用,以避免栈溢出引发程序错误。
4. 堆(Heap):以上几部分都是在程序加载后已经完成的,而堆则是对未分配的虚拟地址的使用。堆的分配与使用一般来说要通过内存管理器调用操作系统的内存分配器完成,Delphi对堆的使用可以看作最终都会通过GetMem/FreeMem来完成(实际上这两个也是调用Delphi程序中的内存管理器完成的,不过一般情况下我们不需要关心它)。
对于一般的局部变量来说,内存都是在栈中分配完成的,不过有几种类型比较特殊,它们在栈中记录的实际上只是个指针,实际内存会由编译器在堆中分配完成,主要包括字符串和动态数组(使用SetLength进行操作的)。当它们的生存期结束后,编译器会自动对栈中的字符串和动态数组进行清理,因此在多数情况下我们不需要担心它们产生内存泄露(包括函数执行完毕和对象的销毁)。除了我们人为对指针进行在堆中的内存分配外,Delphi中的对象也需要通过在堆中分配、销毁来使用,这一点不同于C++之类支持在栈中自动创建对象之类的语言。
现在,对于内存和变量的简单介绍已经完成,下面我们进入正题。首先,我们要区分类与对象:类(Class)是指一种类型,记录了对象的相关信息,其类型为TClass(class of TObject);而对象(Object)则是类的实例,即通过类中记录的信息在堆中的内存空间,其类型为TObject。在Delphi中,如果我们想要使用一个对象,则需要通过类创建实例后才能访问。一般来说,我们通过类的构造函数来完成:
TFoo = class
public
constructor Create;
end;
Foo:=TFoo.Create;
当编译器遇到一个类类型(TFoo)的构造函数时(即由constructor标识的),首先会调用类方法NewInstance(这是TObject的虚类方法)在堆中生成类的实例,再通过构造函数对该实例进行初始化。也就是说,前面的代码实际上为以下过程:
Foo:=TFoo(TFoo.NewInstance).Create;
而当一个对象遇到由destructor表示的析构函数时,则首先调用该函数进行必要的清理,再调用CleanupInstance清理由Delphi自动管理的对象变量(字符串等),最后通过FreeInstance释放由NewInstance分配的内存。
Delphi的类包含以下部分:类变量、类常量、类方法、类属性、类虚方法(virtual)、类动态方法(dynamic)、类静态方法(static);对象也同样包括类似的内容:变量、方法、属性、虚方法、动态方法;类和对象又有继承,可以继承父类的虚方法和动态方法。
前面已经说过,类记录了对象实例的信息,这也就意味着,对于所有的类及其实例来说,类的内容都是唯一的。那么这样看,在处理类变量时,是否可以和处理全局变量的方法一样呢?答案是肯定的,我们只需要一个普通的全局变量就可以了,不同的是,只不过通过类的作用域来限制对这个变量做出一个限制而已,这个限制只是针对程序员的。所以,我们可以放心大胆的把只与一种类相关的变量、常量放到类中,这样既保证了唯一性,又可以排除由于不小心在不同单元中相同的命名造成访问上的混淆。再来看一看类静态方法:当我们访问一个类方法时,编译器知道这个类是什么,所以我们访问类的内容时,完全可以由编译器完成,那么这和普通的函数/过程也就没什么差别了。所以和类变量一样,我们也可以把只针对类内容的函数变成类静态方法。这里需要指出的是,静态类方法和类方法唯一的差别就是不传递Self指针。这就意味着,普通方法/类方法的类型不是procedure/function of object,而静态类方法则不是。关于… of object,可参考System单元中TMethod类型的声明,普通方法的Data是对象的地址(TObject),而类方法的Data则是类的地址(TClass)。普通的类方法中,Self指针指向该类,所以当我们设计类方法时,要注意切勿在通过对象调用类方法时以为Self指针指向对象;同样这也意味着,许多时候当我们不需要类的Self指针时,应该使用静态类方法来提高效率。而属性则是对访问的保护,既避免把丑陋的过程暴露给程序员,又可以避免对其取地址而产生不必要的麻烦,所有的属性都会由编译器自动替代为Read/Write后面的内容,并且避免对起取地址;同时,Delphi又提供了对属性的RTTI访问机制,可以简化永久化信息的工作量。
把类的虚方法和动态方法暂时放一放,我们先来看看对象。前面说了,当创建对象的实例时,首先会通过类中记录的信息分配一块内存,先略过这个信息记录在那里,等一下回头再来谈。现在先假设我们这个类不包括对象的虚方法等,那么需要分配多大的内存呢?普通的方法和一般的过程没什么实质差别,除了要传入一个Self指针指向实例的地址用于访问其它内容。所以只要实例内部需要的变量大小就够了,这样我们就可以轻松访问其它内容了。且慢,但是我们还可以通过实例来访问类变量,这要怎么做到呢?你也许会回答,编译器知道。可是编译器真的知道么?假如现在有个TFoofoo继承自TFoo,但是我们声明的变量Foo的类型是TFoo(FooFoo:TFoo=TFoofoo.Create),那么这个时候我们访问Foo.ClassName时得到的结果应该是 'TFoo’ 还是 'TFoofoo’ 呢,编译器真的能分清楚么?再来一个记录类型的指针,这样一切都完美了,实际上Delphi也是这么做的:实例中首先记录了类指针,接下来的内容则是变量。这样,在调用实例时,我们通过传入对象的指针即Self(有趣的是,Delphi中Self指针是个变量,也就是说你可以把它改成其它值),就可以完美的知道与它相关的一切内容了。
好了,现在我们再考虑引入实例的虚方法之后还需要做些什么。如果我们需要可继承的方法,可以通过怎样的方式来实现呢?首先一种可能想到的是,我们可以在实例中留出一个指针,记录该方法调用的地址,但是这样每个实例都会多出这么一个指针,如果实例很多但这个方法又不常用的话,这种开销有点儿不值得;由于对于每种类的实例来说,虚方法是唯一的,所以也可以通过在类的信息中记录这个指针来减少内存的开销,但是这样的话,找到这个指针又比前面麻烦。Delphi中virtual和dynamic也正是对应着这两种方法,前者用空间换速度,后者用速度换空间。虚方法会增加实例的大小,在给实例分配好内存后一次性把虚方法的地址放入实例的指针中。而动态方法对CPU的开销更大一些,只是类中记录了信息而已。说了这么多类中记录的信息,看来类里面记录的东西还不少,那么它们究竟是怎么记录的呢?
对于每个类来说,都有一个VMT(Virtual Method Table,虚方法列表),这里面记录了大量Delphi的RTTI(Run-Time Type Info)机制所需要的信息,包括虚方法列表(这个是名符其实的,只记录跟虚方法相关的信息)、动态方法列表、变量类型、类型信息、实例大小、类名等等。在同一版本的Delphi中,VMT的大小是固定的,里面包括各种列表信息的指针等等,这样只需要通过同一个管理器就可以实现对类、对象的访问。Delphi在编译时自动生成VMT中的信息,而在类、实例的初始化过程中通过VMT中的信息初始化内容。明白了这一点后,前面说的类虚方法和类动态方法的区别就容易说明了:类的虚方法同样会由编译器通过VMT中的信息进行初始化,而类动体方法则是在运行期间查询VMT中的信息进行访问。而Delphi的RTTI机制则是通过管理器访问VMT中的信息完成,VCL的永久化机制也正是通过VMT巧妙的实现的。
下面这个图示反映了Delphi中类和对象的关系:
- Sep 27 Sat 2008 18:47
愿爷爷在天主的国里得平安
刚刚得到的消息……最后一次见爷爷还是今年暑假的时候,那时还没见爷爷的身子出什么大问题,今天却突然传来噩耗。作为孙子,能陪在爷爷身边不多,算是遗憾吧;爷爷奶奶还念叨着想抱从孙的愿望,我是没来得及在爷爷走之前实现了。。。sigh
爷爷是信天主教的,愿天主的国能接纳爷爷,让爷爷得见主面,在神国里得平安。
我们在天上的父,愿人都尊你的名为圣。
愿你的国降临。
- Sep 27 Sat 2008 12:13
GSM版《师兄欢迎你》(看看07级师弟们的创意)
- Sep 17 Wed 2008 00:24
AVL Tree (AVL树) 的设计原理与实现
by eGust (转载请注明)
AVL-Tree,又称高度平衡树(High-Balanced Tree),是一种自平衡二叉查找树(Self-Balancing Binary Search Tree),最初由 G.M. Adelson-Velsky 和 E.M. Landis 于1962年表,并因此得名。这里不打算做细致的概念性介绍,直接分析数据结构的原理与实现。
本文内容假设读者对二叉树(Binary Tree)这种数据结构有所了解,知道树的高度(Height)等概念,并且对二叉查找树(Binary Search Tree)在搜索、插入、删除中的优点和不足已有所认识。
AVL Tree的原理实际上只有一条原则:任何一个节点的左右两个子树的高度差不大于1。对于一般的二叉查找树来说,除非输入数据受到严格控制,否则插入操作很容易产生不平衡。为解决插入过程中的不平衡,AVL Tree通过旋转(Rotations)操作来完成树的自动平衡工作。如何确定一个节点的平衡性呢?AVL Tree需要一个平衡因子(Balance Factor)来记录节点的平衡性,用以标志一个节点是处于平衡状态、左子树更高或者右子树更高。当一个节点处于平衡、左子树比右子树高1或者右子树比左子树高1这三种情况之一时,这个节点被认为处于稳定状态,不需要调整。
【约定】
- 节点(Node):用A、B、C、D、E等字母来表示;
- 节点包括:左子树(Left Child)、右子树(Right Child)、平衡因子(Balance Factor)、数据(Data),共四个部分;
- [Node].l、[Node].r 分别表示节点 [Node] 的左子树、右子树;
- ≮、⊥、≯ 分别表示稳定状态中的 左子树高1、平衡、右子树高1;
- ≤、≥ 表示不稳定状态,即左边过高、右边过高;
- h([Node]) 表示 [Node] 节点的高度;
- b([Node]) 表示 [Node] 节点的平衡性;
- = 表示两个 值/表达式 相等。
由于BST在左右两个方向上是对称的,所以后面的讨论都只针对一种情况进行,另一个方向上的则可同理得出。
插入(Insertion)
- Aug 26 Tue 2008 11:53
关于xiaonei
- Aug 25 Mon 2008 21:58
Delphi 2009测试版的新语法特性
Delphi 2009测试版的新语法特性
结束了对到Delphi 2007的语法回顾,终于正式进入到最激动人心的2009新语法部分了。
String的变化与增强了的Exit
为全面支持Unicode,Delphi 2009中的所有跟String相关的部分都由原来的AnsiString变成了UnicodeString。这自然也就意味着,原来一些用String声明的函数现在可能会有一些问题(好在我都不厌其烦的用AnsiString和WideString)。这同时意味着Char已经是WideChar而不再是AnsiChar、PChar也是PWideChar而不再是PAnsiChar了。在使用D2009编程时一定要时刻小心和注意这个地方。
而Exit则变成了类似C的return的作用,不过退出参数是可选的,这样才能兼容以前的代码和Result关键字。虽然说这是一个小改进,但是减少了每次不厌其烦的
if(not True)then
begin