关于vb的public变量与property的问题

神马都能聊 2009-05-14 02:48:39
加精
//发现每加一个Public普通变量,偏移量就会加8

定义一个Public普通变量,VB会自动为你实现PROPERTY GET,PROPERTY PUT,即:
public x as long后
vb会处理为:
Public Property Get x() As Long

End Property

Public Property Let x(ByVal vNewValue As Long)

End Property

这一点你用oleview看一下就知道了


我想知道这个是怎么看的,打开oleview之后..?

哪位老大给解释下,不胜感激!
...全文
2406 43 打赏 收藏 转发到动态 举报
写回复
用AI写文章
43 条回复
切换为时间正序
请发表友善的回复…
发表回复
嗷嗷叫的老马 2009-05-26
  • 打赏
  • 举报
回复
神马都能聊 2009-05-25
  • 打赏
  • 举报
回复
分数不多,导致各位分的不多,实在不好意思.

另外可能我分的也不太合理,还请见谅.
lyserver 2009-05-15
  • 打赏
  • 举报
回复
嘿,楼上的还没睡啊。
我准备根据楼主的问题写系列文章呢,内容包括变量的定义、变量的内存分配、变量地址的获得和使用。
舉杯邀明月 2009-05-15
  • 打赏
  • 举报
回复
等我把 33F 看完,34F 已经出来了..........
舉杯邀明月 2009-05-15
  • 打赏
  • 举报
回复
[Quote=引用 33 楼 lyserver 的回复:]
...........
为了理解VarPtr,我还在博客里写了一篇文章,内容如下:
[/Quote]

又忘记了写 URL ~~~~~
lyserver 2009-05-15
  • 打赏
  • 举报
回复
首先,我们来了解一下VB中的变量及其定义。
也许你会说我多此一举,如此简单的问题,还需要我来解释吗?
其实不然。
现在假设有以下语句:
Dim i As Long
它表示什么呢?它表示我们定义了一个名称为a且类型为Long的变量。然而对于内存来说,即没有名称,也没有类型,只有用数字数字表示的地址和连续所占用地址的数量。由于Long为4个Byte大小,因此,我们可以把这条语句看作程序对系统的内存管理器说,俺想在地址a上使用大小为4Byte这一段连续地址,然后准备在这段地址里放点东西,当然,俺的东西不会超过4Byte的。
再看一下这条语句:
Dim s As String
它又表示什么呢?直白地说,它表示我们定义了一个名称为s且类型为String的变量。现在麻烦的是,String的大小是不确定的。如果程序还像上一条语句一样直接告诉系统的内存管理器说:喂,俺想在地址s上放点东西。内存管理器听到后说:又来放东西啊,多大啊?程序说:不知道。内存管理器说:不知道你来干啥啊,我乍给你准备啊?简直是胡闹,一边去,其他人正等呢。这下程序犯了愁,问题不解决,没法给设计人员交代啊。想了想,又回去跟内存管理器说:这样吧,我在地址s上做一个标志吧,只有4Byte大小(32位操作系统的最大寻址能力),到时东西带来了,再将这个标志改为东西实际存放的地址,好吗?内存管理器说:这就对啦,别忘了把东西带来啊。
通过上述两条语句,我们可以了解到,变量定义实际上表示两个含义,一个是变量名称,一个是变量的值,它们都会占用空间的,只不过有些变量由于个子比较小,所以变量名称和变量值放在一块就行,如变量i,而有些变量由于块头比较大或不确定,需要分别进行存放,如变量s。
现在,我们站在内存管理器的角度来理解一下,假设变量a的地址为&H0013FBD0,a的值为100,那么很显然,从地址&H0013FBD0开始到&H0013FBD3这4个byte的空间里,存放的是100;而假设变量s的地址为&H0013FC50,s的值为“100”,那么,从地址&H0013FC50到&H0013FC53这4个Byte的空间里,存放的是指向实际存放“100”的值的地址。
——————————————
通过上述,我想大家应该理解了VarPtr,它实际上是取变量本身的地址,也就是变量在编译时所确定的栈地址。而StrPtr取的是字节变量中的值所表示的地址,它实际上是一个指针。
这篇文章是我写的文章中前面最最基础的部分,后面会继续深入下去的。
——————————————
由楼主的问题牵出我这么多废话,希望大家见谅!
lyserver 2009-05-15
  • 打赏
  • 举报
回复
[Quote=引用 30 楼 AisaC 的回复:]
发现定长的时候s和ss取变量地址居然一样,奇怪..
[/Quote]
首先说明一下,我没看过什么理论上的书,全是靠自己的经验,所以难免不当。
我在上面说了,内存有静态数据区、栈和堆,对于定长的字符串,由于它是在栈中申请的,所以,无论你如何修改它的值,它的地址都是不变的(请使用StrPtr测试)。不仅如此,一旦编译之后,它的地址就完全确定了,你什么时侯运行这个程序,它的地址永远不变,大家可以试验一下,其实,在VB里使用Const 定义的常也是这样,在编译时确定内存地址,只不过常量使用的是静态数据区,而Dim ss As String*100使用的是栈(二者的区别在于除了栈在函数调用时多了个先进先出的功能外,还有数据能不能被修改);而对于不定长的字符串,由于它的内容不确定,所以只能在运行时分配,这时是在堆中分配的,所以它的地址是不断变化的(请使用StrPtr测试)。
而使用VarPtr函数,只能获得变量在编译时以直接嵌入程序中静态数据区和栈中的地址,这个地址也是编译时确定的,所以一旦编译后就不再改变,由于VB中地址默认以按4个字节对齐的,所以,一般情况下,相邻的变量之间,其地址都是相隔4字节,对于相邻的两个Byte变量,则是2字节对齐。
至于说到虚函数表,这是因为使用VarPtr获得的地址,应该是某个函数在表中的项的地址,而地址可以用4字节来表示,所以两个公共变量之间,可能相隔4字节,通过oleview,我们已经证实了这两个公共变量最后被转换成了属性,按照PctGL的说法,应该是间隔8字节才对,但为什么还是只间隔了4字节(可以测试),所以我说不能使用VarPtr来获得类中变量的地址。对于一般的变量,大家可以使用CopyMemory进行赋值,而对于类中的公共变量,尽管我没测试过,但觉得肯定不能用CopyMemory来获得或设置变量的值,不信大家可以试一试。

为了理解VarPtr,我还在博客里写了一篇文章,内容如下:
舉杯邀明月 2009-05-15
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 PctGL 的回复:]

..........

早期绑定,通过编译器支持,能够将对象中的函数地址以直接的形式表现出来
一个例子,就是
dim a as new class1
call a.方法1(参数)
......
[/Quote]

完全赞同~~~~~
这才是真正的早期绑定.......
lyserver 2009-05-15
  • 打赏
  • 举报
回复
再对上面的最后一句补充说明一下。
实际上对类的公共变量使用VarPtr(m),获得的并不是变量m的本身(也就是我前面打比方所说的名称)地址,而是获得类中对应此变量的一个私有变量的幅本。当类每被实例化一次,就为在内存中产生一个数据幅本(成员函数则没有)。
先看一看一个完整的属性,代码如下:
dim m_v as long
public property get m() as long
m=m_v
end property
public property let m(byval NewVal as long)
m_v=NewVal
end property
对于这个属性,在前期绑定中,由于副本地址已确定,故使用o1.m=100这样的语句进行赋值,根本不会调用public property let m(byval NewVal as long)这个函数,而是直接执行了m_v=100,从而减少了由函数调用带来的栈平衡操作。在后期绑定中,由于不知道m_v幅本的地址,所以不能实现m_v=100这样直接赋值,而是先在函数表中查询成员函数public property let m(byval NewVal as long)地址,再调用这个地址实现对变量m_v的赋值。前面我还说过,类的成员函数地址是可以确定的,这是因为一个类只有一个成员函数表,又由于类可以被继承,这个表会在继承时被增加一些项目,所以叫它虚函数表。
综上所述,我们可以看出早期绑定为什么比后期绑定效率要高一些,也应该明白类中的公共变量与属性有什么区别了。至于我所说的变量本身地址编译时确定,在这儿一样有效。一方面对于类来说,成员变量m_v是编译时分配了空间的,而varptr(m)根本不是指向变量m,而是指向变量m运行时的一个幅本的地址,这个幅本的地址不是由类本身确定的,而是由类的调用入实现的,当对类实现早期绑定后,这个幅本也成了一个或几个变量(视实例化次数而定),会在调用者的空间里占用地址。

上述均针对进程内的类对象有效,对于需要在进程间进行调度的,还得需要进一步验证。
zhiyongtu 2009-05-15
  • 打赏
  • 举报
回复
[Quote=引用 37 楼 lyserver 的回复:]
我准备根据楼主的问题写系列文章呢,内容包括变量的定义、变量的内存分配、变量地址的获得和使用。
[/Quote]

严重支持一下!

其实,如果有汇编的基础的话,理解起来就容易得多。

学到不少。
lyserver 2009-05-15
  • 打赏
  • 举报
回复
续34楼。
在这楼里我已经过说,变量名称(也就是变量本身的地址)是编译时就确定了的,可以使用VarPtr进行获得。
而VB中的类,楼上的朋友们也已说了,是从IUnknow->IDispatch继承下来的。在公共类中所定义的公共成员变量,最终被转换成了属性。如果按照我前面所说的在编译时就确定了地址,显然行不通,因为同一个类可能会被多次实例化,而在多个实例中,数据是类实例所私有的。既然编译时无法确定变量的地址,又如何通过变量名称(也就是变量地址)得到变量的值呢?正是为了解决这个问题,所以微软才在COM中引入了虚函数表,并规定类的成员函数(方法和属性实质上也是函数)的地址是唯一的,而数据是动态决议的。
我们可以使用以下代码来验证类成员函数地址的唯一性(或者说确定性),新建一个类,类名为Class1,复制以下代码:
Private Property Get f() As Long
'
End Property

Public Sub PrintAddr()
Debug.Print VarPtr(f)
End Sub
然后模块里复制以下代码:
Sub main()
Dim o1 As New Class1
Dim o2 As New Class1

o1.PrintAddr
o2.PrintAddr
End Sub
大家会发现两个值是一样的。
我们再对Class1里的代码进行修改,在类Class1里复制以下代码:
Dim v As Long
Private Property Get f() As Long
'
End Property

Public Sub PrintAddr()
Debug.Print VarPtr(f), VarPtr(v)
End Sub
再运行一下程序,会发现VarPtr(v)的值会发生变化。
我们再次对Class1里的代码进行修改,在类Class1里复制以下代码:
Public m As Long
Dim v As Long
Private Property Get f() As Long
'
End Property

Public Sub PrintAddr()
Debug.Print VarPtr(f), VarPtr(v), VarPtr(m)
End Sub
还运行一下程序,会发现VarPtr(f)不变,VarPtr(v)和VarPtr(m)都会变化。
经测试,在公共类中,结果依然一样。
而我们一再反复强调并被验证过的是,Public m As Long会被自动转换为以下代码:
Public Property Get m() As Long
End Property
Public Property Let m(Byval NewValue as Long)
End Property
那么,VarPtr(m)应该与VarPtr(f)一样啊,否则就违背了COM的原则。
错在什么地方呢?
错就错在不能使用VarPtr来获得变量m的地址,因为,m根本不是一个变量,甚至,它也不是一个属性,它是一个指向这个属性的地址的一个变量。

不说啦,班门弄爷,贻笑大方!
slowgrace 2009-05-15
  • 打赏
  • 举报
回复
阿弥陀佛,今晚上这水真深啊
PctGL 2009-05-14
  • 打赏
  • 举报
回复

我再接一帖吧, 我是很崇拜超级绿豆的,他是豆颜不改,我也是崇拜依然啊
做为给偶像的补充,如果不对,请指正.

早期绑定,通过编译器支持,能够将对象中的函数地址以直接的形式表现出来
一个例子,就是
dim a as new class1
call a.方法1(参数)

编译后为,这里只是个比方
push 参数
call [eax + 方法1的vtable结构偏移量] ; 其中 eax = 实例地址(lea eax,vtable)


后期绑定就象自己利用安全数组的结构构建了一个安全数组,然后需要访问这个数组中的某个元素
这时必须遵循的方法就是查询这个数组的信息,然后再通过计算得到元素位置
对象其实也是一个结构,他的结构具体内容不固定,如果在事先得不到结构信息的时候,
就必须按照标准的对象编程方法,依次访问3个固定的基本接口,查询对象的信息,对于>vb的对象还要多一步就是查询IDispatch,但对于其他语言实现的非标准对象,或例如轻量级对象上就要特事特办了. 仅从描述这两个的实现过程上来看,也能体现出一些效率上的差距.

其实对于对象的了解,我更多的是通过一些汇编下实现对象的资料和代码理解的.
说来惭愧,我不会c++,对于c++或者说COM的很多名词都说不上来,叫不上口,也许能理解到,但也只能意会了
神马都能聊 2009-05-14
  • 打赏
  • 举报
回复

Sub main()
Dim s As String * 100
Dim ss As String * 20
s = "123"
ss = "1234"

Debug.Print VarPtr(ss), StrPtr(ss)
Debug.Print VarPtr(s), StrPtr(s)
s = "45677"
Debug.Print VarPtr(s), StrPtr(s)


Dim newfrm As New Form1

Form1.Show

Set newfrm = Nothing
End Sub


发现定长的时候s和ss取变量地址居然一样,奇怪..


[Quote=引用 13 楼 PctGL 的回复:]
一个函数属性占用4字节空间存储地址,get/let是两个属性函数,就要 +8 的空间存储地址
[/Quote]
谢谢,这让我明白了为什么是8个字节了.

dt168 2009-05-14
  • 打赏
  • 举报
回复
学习
神马都能聊 2009-05-14
  • 打赏
  • 举报
回复


继续受教
东方之珠 2009-05-14
  • 打赏
  • 举报
回复
继续学习,巩固基础!
supergreenbean 2009-05-14
  • 打赏
  • 举报
回复
呵呵,继续解释一些内容

1、COM对象都必须实现IUnkown接口
2、实现了IDispatch接口的COM对象也就是常说的自动化对象,调用者可以通过这个接口查询对象的属性和方法信息(包括名称和id),并调用其属性和方法
3、VB中所用的对象都是自动化对象
4、在VB中是可以使用未实现IDispatch的非自动化COM对象,前提是要有描述这个对象接口的类型库,也就是描述其接口的VTable结构
5、VB中的对象有两种绑定方式

A.前期绑定,即通过类型库所描述的VTable结构来调用对象的属性和方法,效率高,例如:
Dim rst As ADODB.Recordset
Set rst = New ADODB.Recordset
rst.Open



B.后期绑定,即通过IDispatch接口来使用对象,效率低
Dim rst As Object
Set rst = CreateObject("ADODB.Recordset")
rst.Open

clear_zero 2009-05-14
  • 打赏
  • 举报
回复
我基础不好,纯粹来学习的

sonic_andy 2009-05-14
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 slowgrace 的回复:]
另外,太气人了。大家居然都知道COM。还都知道IUnknown、IDispatch等等
[/Quote]
<COM原理与应用>比较经典,嗯..
加载更多回复(23)

863

社区成员

发帖
与我相关
我的任务
社区描述
VB COM/DCOM/COM+
c++ 技术论坛(原bbs)
社区管理员
  • COM/DCOM/COM+社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧