真想不到之五:高效字串指针类

发表于:2007-07-01来源:作者:点击数: 标签:
我的文章可见: 真想不到之五:高效字串指针类 关键字: VB 、HCAK、字串指针、BSTR、效率、内存共享 难度:中级或高级 参考文章: 1、2000年7月VBPJ Black Belt专栏文章《Modify a Varialbes Pointer》 作者:Bill McCarthy 2、1998年4月VBPJ Black Belt专

我的文章可见:

               真想不到之五:高效字串指针类
关键字:VB、HCAK、字串指针、BSTR、效率、内存共享
难度:中级或高级
参考文章:
1、2000年7月VBPJ Black Belt专栏文章《Modify a Varialbe´s Pointer》
   作者:Bill McCarthy
2、1998年4月VBPJ Black Belt专栏文章《Play VB´s Strings》
   作者:Francesco Balena

引言:
    本想以内存共享做为VB指针专题的最后一篇,写着写着发现字串的问题应该单独谈谈。在内存共享的问题上,我尤其关心的是字串的共享,因为在我一个多月前发布的源码里用的是《HardCore VB》里Bruce Mckinney提供的CShareStr类,它实现了字串的内存共享。但是Bruce也没有突破局限,对字串的处理依然是CopyMemory的乾坤大挪移,尤其是还要进行讨厌的ANSI/DBCS和Unicode的转换。我在readme里说过它效率极低,应该采用Variant或Byte数组来实现,才能避免转换。后来又想到可以用StrPtr来做,并在VC里用DLL共享节实现了可以不进行转换的字串内存共享。不过在VC里我仍然需要用SysAllocString来建立VB能使用的BSTR。这都不是我想要的,我想要的东西要象VC里的CString的一样,只要字串够大,对其赋值就不用重新分配内存,还要象VC里CComBSTR类一样可以Attach到一个特定BSTR。
    知道该怎么做,是在看了VBPJ上Bill McCarthy和Francesco Balena的两篇文章之后。Bill用修改SafeArray描述结构实现了数组的内存共享,而Francesco则对字串指针进行深入的探讨。但是Bill和Francesco的东西都没有实现我想要的字串类。
    方法知道了,实现并不难,所以我决定自己来包装一个这样的东西。
       


正文:
    使用VB里的字串类型String有两大不足:第一、它的分配是由VB运行时控制,我们不能将其分配在指定内存处;第二,任何一次对字串的赋值操作都要进行内存重新分配。要实现高效、灵活的字串处理,我们必须克服这两大不足。
    对于第一个问题,通过修改String变量里放着的BSTR描述符指针可以实现;对于第二个问题,可以用Mid语句(注意是语句而不是函数)来赋值。不详细讲了,直接看下面的这个类:

    Option Explicit
   
    ´********************************************************
    ´clsBSTR.cls
    ´作者: 熊超         ID: AdamBear        2002年3月18日
    ´http://www.csdn.net/Author/AdamBear
    ´    你可以自由使用本类模块,不过请保留本声明
    ´********************************************************
   
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
   
    ´不要直接对sString赋值(可以用MID语句),将其设为公有仅为提高效率。
    Public sString As String                ´BSTR描述符指针
   
    Private pStr As Long                    ´BSTR地址
    Private nMaxLen As Long                 ´BSTR最大字节数
   
   
   
    ´让本字串指向特定地址
    Public Sub Attach(Addr As Long, Optional nLen As Long)
        pStr = Addr
        ´修改BSTR描述符指针,使其指向Addr
        CopyMemory ByVal VarPtr(sString), Addr, 4
       
        If IsMissing(nLen) Then Exit Sub
        ´设定最大字串字节数
        nMaxLen = nLen
       
    End Sub
   
    ´还原本字串原BSTR描述符
    Public Sub Detach()
        CopyMemory ByVal VarPtr(sString), 0&, 4
    End Sub
   
    ´让本字串指向源字串
    Public Sub AttachStr(sStr As String)
        Attach StrPtr(sStr), LenB(sStr)
    End Sub
   
    ´data为缺省属性
    Public Property Let data(sVal As String)
        Dim c As Long
        c = LenB(sVal)
 ´超过最大字串数,抛出错误。
        If c > nMaxLen Then Err.Raise vbObjectError + 3000, _
                                  "CString::Let Data", "溢出"
        ´写字串长度
        CopyMemory ByVal (pStr - 4), c, 4
        ´写字串
        Mid(sString, 1) = sVal
    End Property
   
    ´可以通过公有变量sString来读字串,效率更高
    Public Property Get data() As String
        data = sString
    End Property

    Private Sub Class_Terminate()
        Call Detach
    End Sub
   
    用法如下,假设我们已通过VitualAlloc,HeapAlloc,MapViewOfFile这样的内存管理API得到了一个4k个字节的可读写的内存地址baseAddr:
    Dim sShare As New clsBSTR
    ´留下前4个字节用于BSTR保存字串字节数
    sShare.Attach(baseAddr+4, 4096-4)
    ´下面的字串"Test"会直接写到baseAddr+4字节处
    sShare = "Test"   
    Dim y As String
    ´读字串时可以用sString属性或缺省属性
    y = sShare.sString
    ´用AttachStr方法Attach到一个字串。
    ´必须要先Detach
    sShare.Detach
    sShare.AttachStr(y)
    sShare = "Hahaha"
    Debug.Print y
    ´一旦AttachStr到字串y后,对sShare的修改就相当于对y的修改。
    ´并且以后对y的修改也只能用Mid语句
    Mid(y, 1) = "xxxxx"
    ´不能直接赋值,这样VB会将原来y所指(也是sShare所指)内存释放,
    ´    重新分配y。这样在访问sShare时会出错。
    ´y = "Test"
       
   
    我也不在这里讲这个类的详细原理,可以参考我前面说的两篇文章。
    使用这个类有几个需要注意的地方。
    1、读字串时可以用sString属性来读,更快。 
    读sShare有两种方法,一种是用缺省属性Data来读,一种是直接用sString属性来读。用sString属性不重新分配内存,要快得多。
    2、不要直接给sString赋值,应使用缺省的data属性来赋值。
    之所以把sString属性暴露出来,是为了效率和方便。我们可以用Mid语句对其进行修改,但不要直接用"="来赋值。
    3、注意Attach的第二个参数,表示字串的最大字节数,不要让它超过已经分配的内存。
    4、用AttachStr将本字串对象Attach到某个字串(比如上面的y)上后,不能再对这个字串y重新赋值,也不能将其传递到会对其重新赋值的过程。
      
    哇,这么多需要注意的问题,用起来岂不是更不方便。的确,用它的之前要考虑是不是必须的。因为建立这个类也一样有开销。所以还有一个需要注意的问题:

    5、它主要的应用还是在于将字串安放在指定内存处。虽然它也可以让同一个进程内几个的字串达到共享的目的,但是如果只是两三个很小的字串这样时做反而慢了。
   

后计:
    数组指针和字串指针我们已经谈过了,对于普通的数值类型变量的指针没有什么Hack的必要,但是它关系到一个有用的技术,下篇文章再谈。
    本文和下篇文章的代码,以及用这个类来实现的共享内存的代码,我会发布到CSDN共享软件上,名字是《内存共享和指针》。


原文转自:http://www.ltesting.net