VB备忘录(7)类与对象---接口

来源:互联网 发布:大气环境防护距离软件 编辑:程序博客网 时间:2024/06/03 11:46

VB不能继承,于是就会用一个接口来绕个弯路,达到继承的效果。

接口:  Implements  

接口并没有实际的代码,是一个“空壳”,有点象虚拟类(不存在的类的)


接口相当于是一个模板,一个空壳的方法和属性。也可以说是抽象的方法和抽象的属性。接口通过类模板定义,又称接口类

其它类通过接口(这个公共的模板)来把“空壳”来完善,实现其中的数据或方法。


先熟悉一个例子,认识一下:

从CShape类(空壳类,作为接口),产生两个类CPoint和CCircle,每个类都是各自的一个类模块:


1、空壳类:Cshape

Option Explicit'***********************************'这是一个形状接口'***********************************Public Function Area() As Double   '面积,空壳,无代码End FunctionPublic Function Name() As String    '名称,空壳,无代码End FunctionPublic Function ToString() As String '输出,空壳,无代码End Function




2、CPoint类(注意接口声明)

Option ExplicitImplements IShape  '接口声明,I前缀开始表明接口定义Private mX As IntegerPrivate mY As IntegerPrivate Function IShape_Area() As Double    IShape_Area = 0End FunctionPrivate Function IShape_Name() As String    IShape_Name = "Point"End FunctionPrivate Function IShape_ToString() As String    IShape_ToString = "[" & mX & "," & mY & "]"End FunctionPublic Property Let X(ByVal newX As Integer)    mX = newXEnd PropertyPublic Property Get X() As Integer    X = mXEnd PropertyPublic Property Let Y(ByVal newY As Integer)    mY = newYEnd PropertyPublic Property Get Y() As Integer    Y = mYEnd Property


3、CCircle类(注意接口,都是IShape)

Option ExplicitImplements IShapePrivate mX As Integer '不是从CPoint中继承Private mY As IntegerPrivate mRadius As DoublePrivate Function IShape_Area() As Double    IShape_Area = 3.14159 * mRadius ^ 2End FunctionPrivate Function IShape_Name() As String    IShape_Name = "Circle"End FunctionPrivate Function IShape_ToString() As String    IShape_ToString = "[" & mX & "," & mY & "," & mRadius & "]"End FunctionPublic Property Let X(ByVal newX As Integer)    mX = newXEnd PropertyPublic Property Get X() As Integer    X = mXEnd PropertyPublic Property Let Y(ByVal newY As Integer)    mY = newYEnd PropertyPublic Property Get Y() As Integer    Y = mYEnd PropertyPublic Property Let Radius(ByVal newR As Double)    mRadius = newREnd PropertyPublic Property Get Radius() As Double    Radius = mRadiusEnd Property

这样就把三个类定义好了,接口类IShape什么代码都没有。CPoint和CCircle都来自于IShape

下面看一下,它是怎么达到“继承”的效果。


窗体模块(标准模块)中:

Option ExplicitPrivate Sub Command1_Click()    Dim p As New CPoint    Dim c As New CCircle    Dim iRef As IShape  '创建引用    p.X = 500    p.Y = 777    Set iRef = p        '引用连接到CPoint类上    Print iRef.Name & " Area:" & iRef.Area & vbCrLf & iRef.ToString    c.Radius = 4    c.X = 11    c.Y = 812    Set iRef = c    Print iRef.Name & " Area:" & iRef.Area & vbCrLf & iRef.ToStringEnd Sub

看完了例子,感觉有点多余,直接使用相对就的对象(CPoint或CCircle)不就完了?

是的,可以这样做。但是。。。

很多情况,在调用一个对象时我们并不清楚这个对象是哪一个,也许是CPoint,也许是CCircle,

这种情况无法猜测是哪一个对象,也就无法手动指定它是哪一个对象,那我们更无法来确定成员了。怎么办?

这时,就可以巧妙地用一个对象CShape来代替,它就可以直接由Cpoint或CCircle来赋值。换到使用的场景时就可直接使用CShape了。

这就是    多态性。

简单的一句话就是:将父对象设置成为和一个或多个子对象相等的技术,就是多态。

表现形式即:    Set Parent= New Child




上面可以看接口的应用。但没理解其中的本质。

下面是别人的一个例子,但却很好地说明了内部的实质。

在看这个例子前熟悉几个概念:

指针:指向一个变量的变量,存储的是被指向变量的地址。

地址:每个对象或变量都在内存中存储,存储的内存位置都有一个编号,这个编号就是地址,计算内部都是根据编号来找变量或地址的。

地址的求法,VB中有三个常用的:(注意,地址是4个字节的长整形)

ValPtr       value pointer   即变量的地址

StrPtr       string pointer   即字串的地址

ObjPtr      object pointer  即对象的地址

CopyMemory把一块内存中的东西复制到另一块中去。


下面看一下这人例子,由接口类“人”,“继承”出两个类“大人”,“小孩”,然后再仔细看一下在标准模块中的应用 

1、接口类:人

Option ExplicitPublic Name As String    '属性Public Sub Eat()    '没有内容,空壳End SubPublic Sub birth()    '没有内容,空壳End Sub

2、“继承”类:大人

Option ExplicitImplements 人  '接口声明Dim 大人Name As StringPrivate Sub 人_Eat()    MsgBox "手艺不错"End SubPrivate Sub 人_Birth()    '空着(大人不可能出生)但必须要有这个过程End SubPublic Sub work()    MsgBox "tired"End SubPrivate Property Let 人_Name(ByVal RHS As String) '继承属性大人Name = RHSEnd PropertyPrivate Property Get 人_Name() As String人_Name = 大人NameEnd Property

3、“继承”类:小孩

Option ExplicitImplements 人   '实现接口Dim 小孩Name As StringPrivate Sub 人_Eat()    MsgBox "妈妈喂"End SubPrivate Sub 人_Birth()    MsgBox "哇…哇…"End SubPublic Sub play()    MsgBox "ha ha!"End SubPrivate Property Let 人_Name(ByVal RHS As String)    '继承属性    小孩Name = RHSEnd PropertyPrivate Property Get 人_Name() As String    人_Name = 小孩NameEnd Property

4、上面声明完后,开始应用 

Option ExplicitPrivate Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)Dim xh As New 小孩Dim dr As New 大人Dim r As New 人Dim TheObjPtr As LongDim TmpOBJ As 人        '声明类型,作引用,注意没有NewPrivate Sub Form_Load()    xh.play    '没有作为接口类使用时    dr.work    r.Eat    '    TmpOBJ=Nothing    '    方法一:    '    SetRealObj xh    '注意了,xh为小孩类,但由于接口,可以符合OBJ As 人    '    TmpObjEat    '方法二:    Set TmpOBJ = dr    '这里也是可以符合,使TmpPBJ设置成大人    TmpOBJ.Eat    TmpOBJ.Name = "小明"    '属性的继承    MsgBox TmpOBJ.Name    Set TmpOBJ = Nothing    '释放对象End SubFunction SetRealObj(OBJ As 人)    TheObjPtr = ObjPtr(OBJ)End FunctionSub TmpObjEat()    Dim BakPtr As Long    CopyMemory BakPtr, TmpOBJ, 4        '备份TmpOBJ的内存地址    CopyMemory TmpOBJ, TheObjPtr, 4     '使TmpPBJ“直接变成”小孩    TmpOBJ.Eat    CopyMemory TmpOBJ, BakPtr, 4        '因为改动内存地址,结束时会出错,必须恢复,而下面Set TmpOBJ = Nothing则不是必须的End Sub

分析:

上面用的方法二,实质上和第一个例子没有区别。

看一下方法一,前面的TmpOBJ也需注释掉,因为本身就是空的,后面再来释放会出错

方法一中,第一句是把小孩类的对象xh的地址送给TheObjPtr,为下面一句的过程作准备。

下面过程TempObjEat()过程,第一句定义一个临时长整形,用来存放(TempOBJ地址,以便最后恢复TmpOBJ的地址)

第二句,提出TmpOBJ的地址存储到BackPtr中

第三句,把“继承”类对象的地址TheObjPtr,放到超类TempOBJ中,

第四句,用超类来显示值(实际是xh的值)

第五句,还原。

上面可以看到,继承的本质东西在里面了,第三句因为“继承”,同类的指针可以复制过来,并在第四句中得以正确的显示。

总之一句:父类引用指向子类。


理解上面后,再看接口的注意点:

1、超类的定义过程需是一个空壳

      而且后面要被继承的成员(过程或数据)必须是Public,才能通过它来引用后面继承对象的成员(因为后面都要通过这个公共接口去访问)

     一般把继承类中对应的成员设置为private(如果是过程就用function,数据用property)

2、子类的定义过程(重复超类的定义)需是以超类名开头,加上下划线,再是同名,比如:

           IShape_ToString     有三部分:第一部分IShape,空壳类名不能变;第二:下划线不能省略;第三:名称ToString不能变化。

       对于超类没有的可以自己定义。

                

MSDN上面关于implements也有一例子。

首先form2里面定义了一个属性PD,它存储的是父类(接口类)的对象。为下一步向里面存储继承的对象作准备。

然后form1里,两个按键,根据不同按键存储分别存储不同的子类对象。

这样按不同的按键时,接口类就会根据指向的对象,以多态的方式,指向对应不同的对象,尽管看起来都使用同一个对象,但它们指向不同。