如题!还有那里有面向对象的好教材下载啊!先谢谢了!

解决方案 »

  1.   

    封装、继承、多态构成了OOP的三大基本核心,封装、继承在之前我们曾作过介绍,而多态没有进行详细的介绍,在此给以补充,以完成OOP的三大主题来结束OOP的那一块。关于多态在很多资料上都有介绍,而且介绍的非常精彩,那么就让这篇文章再来进行一次总绍吧。
    首先说点废话,以下的介绍以及实例中,我将以Object Pascal作为讲解的工具,而多态实质上是OOP的,不属于某一个工具的,它是一种信仰。在此处虽然以Objec Pascal来进行描述,其实可以扩展到任何的语言中。言归正转,继续我们的话题。
    什么是多态?多态的本质是什么?多态和封装、继承有什么关系?它在OO中站有什么样的地位?多态给我们的工作代来了什么?本文就对这些问题加以阐述,在阐述的过程中来理解多态,认识多态,应用多态。
    什么是多态?似乎没有一个统一的定义来规范多态,或许以我们自己的理解方式来解释多态更为贴切,在此我们不引用一些术语来进行定义,就初学者也可以理解的方式来形像这个定义:多态,顾名思议,就是多种形态,而这种多种形态又具体又体现在了什么地方?可以这样理解,我们可以用一种轮廓的物体来描述不至一种的多个物体,而至于我们到底要描述那个物体的状态,对于我而言不很重要。为什么要这样说呢?多态就是给我们体供了这样一种机制,我们对它仅仅是一个形式上的声明、定义,而具体的实现上的细节我们没有必要在此进行关心。或是具体的实现细节将交给别的东西去实现。我们所声明的这个基类从某种意义上而言只是一种定义。就如接口一样,没有实现部分,说到这儿,不得不说多态的本质了。多态的本质就是基类提供一系列的虚拟方法,如果试图去实现这个基类的话,将被编译器告之这样是行不通的。多态性的完整提体是交对象和他的子对象共同完成的一个思维方式。 多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。对于用户而言,他们所关心的只不过是那些没有实现细节的父类所提供的虚拟方法罢了。如果以接口的话来讲就是:我们不去关心,也没有必要去关心接口的实现细节,我们所关心的仅仅是接口为我们提供的方法来完成我们的功能,而至于接口以何种方式来实现我们所需要的不同功能甚至是一个特定方式的多个功能都是其内部的事。不知道这样的解释是否让大家感觉很模糊,在理解上是否感觉不能很好的接受。请继续往下看。
    谈到多态,我们首先要明白,对于多态而言,其父类提供了很多的虚拟方法,所以我们没有办法去直接实现这个父类,只能通过它的派生类去覆盖其提供的虚拟的方法,所以这就产生了多态。我们都知道,一个类可以被多个子类所继承,而每个子类所实现的方法又不一至,或是根据它们自己特有的特点对一个特定的方法的实现上因为类差别而存在着差别,但是我们完全可以通过父类去模糊化这些操作。对子类的方法的调用完全都可以通过基类去实现。由此而言,如果此基类没有被派生,那么这个类是完全没有存在的意义的!只有它被某一个子类派生了,那么它就正真的有存在的意义了。这种意义又提体在什么地方?或我们以什么手段来让这种意义成为焦点呢?这就是我们需要进一步走进多态的原因,如下所示:
    TA = Class
       Public
         Procedure A ; virtual ; abstract;
         Procedure B ; Virtual ; abstract;
         Procedure C ; virtual ; abstract;
       end;
    当我们声明了类TA时,可以看到它有三个方法,而且也注意到了这三个方法都是完全虚拟的,如果此时我们试图实例化这个TA,将会没有任何的意义的,因为我们无法去实现类TA给我们提供的各种方法,或者说编译器将不提供空间/机制来让我们实现这些虚拟的方法。只有通过其子类进行覆盖这些虚拟方法才可以正真的将它们提体出它的作用。而覆盖就是说子类重新的定义、实现了基类的方法。如:
    TB = Class(TA)
       Private
         B_Str : String;
       Public
         Constructor Create(Value : String);
         Destructor  Destroy();override;
    {这是覆盖。设么地方可以用override?当他积累相对应的方法是虚拟的或是动态的时候才可以用override}
         Procedure A ; override;
         Procedure B ; override;
         Procedure C ; override;
       end;
     
     TC = Class(TA)
       Private
        C_Str : String;
       Public
         Constructor Create(Value : String);
         Destructor Destroy();override;
         Procedure A ; override;
         Procedure B ; override;
         Procedure C ; override;
       end;
    这里有一个初学者或是不太注意语法的人经常混淆的概念。覆盖(override)和重载(overload)。上面说了,覆盖是指子类重新定义父类的虚函数的做法(inherted的用法是什么意思你真正的明白吗?)。而重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。如下:
        Procedure OpenSQL(Sender : TADOQuery;SQLStr : String); overload;
        {当重复时,返回为False,否则返回为True}
        Function OpenSQL(SQLStr : String) : Boolean ; overload;
     
    Function ifrepeat(TableName,FieldName,FieldValue : String;IntValue : Integer = 0):Boolean;overload;
        Function ifRepeate(TableName : String;FieldName , FieldValue :
          Array of String) : String ;overload;  
    可以很清晰的看到上边的代码就是一个子类覆盖了基类的虚拟方法的过程,而此时还不能很明确的提体出来多态的特性,或是说多态的本质问题还没有提体出来,并且您可能于晚期绑定也不是很明白,下边将给以解释。
    当我们从抽象类TA派生出了类TB和TC,并且,子类也覆盖了基类的虚拟方法,现在如何应用这些方法呢?我们将以什么样的形式去调用这些方法呢?这才是多态最值得焦点的时刻,也是最能体现多态的本质的地方,之前已经给您灌输了多态的本质就是将子类类型的指针赋值给父类类型的指针,只要这样的赋值发生了,多态也就产生了,因为实行了“向上映射”。何以这样说呢?请继续我们的实例,假设它们的实现方法分别如下:
    { TC }
     
    procedure TC.A;
    begin
      inherited;
    {inherted的意思就是把相应的消息交给其父类去处理,他不属于我们讨论的范畴}
      ShowMessage('TC' + C_Str);
    end;
    ……
    { TB }
     
    procedure TB.A;
    begin
      inherited;
      ShowMessage('TB' + B_Str);
    end;
    ……
    现在我们就可以通过父类来来对子类进行调用,而这个过程其实就是一个“向上映射“的过程,既:子类类型的指针赋值给父类类型的指针;我们可以在此定义一个全局的过程来验证我们上边的话:
    Procedure Test(Const pA : TA);
    begin
      pa.A;
    {请将您的注意力放在此处,我们说了,TA是一个抽像类,它所提供的方法都是虚拟的,只有被覆盖了才可以进行应用,那么此处的处理是否是正确的呢?答案是肯定的,于是,多态在此处便提体的淋漓尽止,同时我也给出了调用的过程}
    end;
    procedure Tform.ButtonClick(Sender: TObject);
    var
      vTC : TC;
    begin
      vTC := TC.Create('多态实例_vTC');
      Test(vTC);
      vTC.Free;
    end;
    {此时你是否可以看明白了这种“向上映射“的机制呢?vTC做为一个子类TC的引用值(对象指针),完全可以赋值给其基类TA,于是,此处就发生了子类类型的指针赋值给父类类型的指针,于是多态就产生了!!!}
    此处再一次的对晚期绑定或是动态进行说明:当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。
    我想,此时,你应该对多态很明白了吧,那么我们讲了多态是OOP的一个重中之重,那么它倒底有什么作用?它给我们的项目将会带来什么效率上的飞跃呢?仅仅是为了完成某种实现方法吗?仅仅是作为一个概念而存在吗?这些都是我们在理解了多态之后必须去思考的东西。那么下边就多态将会如何的影响我们的效率进行阐述。我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!
    如果还没有很明白多态,不妨参考下边的例子。
    在这个问题上可以这样说:大炮可以打鸟,火枪也可以打鸟,你用那个?我不管是大炮还是火枪,我只知道他们都可以打鸟!向恐龙一样大的鸟我用大炮(就是一个例子,哪有那么大的鸟啊?打什么鸟对于长官来说就是一句话:打死它们,用什么?他不管),小鸟我用火枪。敌人来了我还可以打敌人,这就是动态的最终解释和一种接口重用!
    有很多地方我是应用前辈的话。
      

  2.   

    重载:相同函数名,不同入口参数的实现方法。overload
    覆盖:替代基类中的同名函数,不需要关键字。
    多态:父类中的方法在子类中的不同体现。相当于遗传中的变异(:)个人观点)。也属于一种覆盖。多态是通过关键字override在子类中体现的。在父类中,该方法可以是动态的或是虚方法。也就是关键字dynamic和virtual。着两种的主要区别在于编译器的处理方式上一个使用了DMT另一个使用了VMT。因此执行的速度和内存的消耗量也各有不同。还有就是静态的虚方法了。下面摘自《高手突破》这里有一个初学者经常混淆的概念。
    覆盖(override)和重载(overload)。
    覆盖是指子类重新定义父类的虚函数的做法。
    而重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
    其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!
      
        引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”
      
        那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了--代码重用。而多态则是为了实现另一个目的--接口重用!
      

  3.   

    overload指两个函数的名字相同但参数不同,程序通过参数来判断是哪个函数。
    override指重载父类的同名的函数
      

  4.   

    overload如wweijie(林黛玉) 所述,即可以用在类中,也可以用在全局函数。
    override如wweijie(林黛玉) 所述,与基类virtual对应,基类的虚方法可以定义为抽象(abstract)和非抽象。
      

  5.   

    《高手突破》的内容快成了讲解重载和覆盖的经典了!其实很简单,你这样想,作为类的三大特点之一,而且也是最重要的特点的多态,无非就是要实现同一类事物的不同表现!所有的事物的类型都一样,因为他们有共同的来源,但他们的表现可能千差万别,因为他们有自身额外的发展。Delphi实现多态是借助于Override指示字!而Oerload是和类特性没有任何关系的,这个关键字仅仅是为了解决类中方法同名的冲突!至于向上映射,楼上节选的文章中已经说的很明白了,其实就一句话:A中派生了B,B完全继承了A的特性,所以试图通过B的指针去访问A是可能出错的(因为B有的可能A根本不具有,所以虽然量C用A来实现,但申明毕竟是B,所以编译器还认为是对B的访问,所以会AV错误),为了避免这种可能,Delphi干脆就限制了这种可能发生的情况!