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

前端之家收集整理的这篇文章主要介绍了VB备忘录(7)类与对象---接口前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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

接口: Implements

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


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

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


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

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


1、空壳类:Cshape

Option Explicit

'***********************************
'这是一个形状接口
'***********************************

Public Function Area() As Double   '面积,空壳,无代码
End Function

Public Function Name() As String    '名称,空壳,无代码
End Function

Public Function ToString() As String '输出,空壳,无代码
End Function




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

Implements IShape  '接口声明,I前缀开始表明接口定义

Private mX As Integer
Private mY As Integer

Private Function IShape_Area() As Double
    IShape_Area = 0
End Function

Private Function IShape_Name() As String
    IShape_Name = "Point"
End Function

Private Function IShape_ToString() As String
    IShape_ToString = "[" & mX & "," & mY & "]"
End Function

Public Property Let X(ByVal newX As Integer)
    mX = newX
End Property
Public Property Get X() As Integer
    X = mX
End Property

Public Property Let Y(ByVal newY As Integer)
    mY = newY
End Property
Public Property Get Y() As Integer
    Y = mY
End Property


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

Option Explicit

Implements IShape

Private mX As Integer '不是从CPoint中继承
Private mY As Integer
Private mRadius As Double



Private Function IShape_Area() As Double
    IShape_Area = 3.14159 * mRadius ^ 2
End Function

Private Function IShape_Name() As String
    IShape_Name = "Circle"
End Function

Private Function IShape_ToString() As String
    IShape_ToString = "[" & mX & "," & mY & "," & mRadius & "]"
End Function

Public Property Let X(ByVal newX As Integer)
    mX = newX
End Property
Public Property Get X() As Integer
    X = mX
End Property
Public Property Let Y(ByVal newY As Integer)
    mY = newY
End Property
Public Property Get Y() As Integer
    Y = mY
End Property
Public Property Let Radius(ByVal newR As Double)
    mRadius = newR
End Property
Public Property Get Radius() As Double
    Radius = mRadius
End Property

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

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


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

Option Explicit

Private 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.ToString
End 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 Explicit

Public Name As String    '属性

Public Sub Eat()
    '没有内容,空壳
End Sub

Public Sub birth()
    '没有内容,空壳
End Sub

2、“继承”类:大人
Option Explicit

Implements 人  '接口声明


Dim 大人Name As String

Private Sub 人_Eat()
    MsgBox "手艺不错"
End Sub
Private Sub 人_Birth()
    '空着(大人不可能出生)但必须要有这个过程
End Sub
Public Sub work()
    MsgBox "tired"
End Sub


Private Property Let 人_Name(ByVal RHS As String) '继承属性
大人Name = RHS
End Property
Private Property Get 人_Name() As String
人_Name = 大人Name
End Property

3、“继承”类:小孩
Option Explicit

Implements 人   '实现接口

Dim 小孩Name As String

Private Sub 人_Eat()
    MsgBox "妈妈喂"
End Sub
Private Sub 人_Birth()
    MsgBox "哇…哇…"
End Sub
Public Sub play()
    MsgBox "ha ha!"
End Sub


Private Property Let 人_Name(ByVal RHS As String)    '继承属性
    小孩Name = RHS
End Property
Private Property Get 人_Name() As String
    人_Name = 小孩Name
End Property

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

Private 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 Long
Dim TmpOBJ As 人        '声明类型,作引用,注意没有New

Private 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 Sub

Function SetRealObj(OBJ As 人)
    TheObjPtr = ObjPtr(OBJ)
End Function

Sub 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里,两个按键,根据不同按键存储分别存储不同的子类对象。

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

猜你在找的VB相关文章