thoga31 610 Denunciar mensagem Publicado 15 de Janeiro de 2013 (editado) Considere-se o seguinte código: {$mode objfpc} program OOP_Heranca; type TA = class(TObject) function func : integer; end; TB = class(TA) function func : integer; end; TC = class(TB) end; function TA.func : integer; begin func := 10; end; function TB.func : integer; begin func := 20; end; var c : TC; begin c := TC.Create; writeln(c.func); c.free(); readln; // Pausa end. O seu output é 20, e não 10. Houve uma redefinição da função func de TA para TB, mas não para TC. Gostaria, a partir daqui, debater o que são, quando são utilizadas e qual a sua utilidade, das seguintes "etiquetas", "rótulos" ou "classificações" que se pode dar aos métodos das classes: overload override inherited virtual abstract deprecated Pretendo com isto reunir informação, até para eu consolidar estes conceitos. Achei melhor que seria mais interessante assim. Peguem no código que dei para explicarem cada uma destas coisas, acho que é excelente para isso - é simples e elucidativo. Cumprimentos pascalianos e delphianos, thoga31 Editado 15 de Janeiro de 2013 por thoga31 Knowledge is free! Partilhar esta mensagem Ligação para a mensagem Partilhar noutros sites
Kline777 97 Denunciar mensagem Publicado 15 de Janeiro de 2013 Desculpem vir complicar em vez de explicar mas agora deu-me um brain freeze... Tanto quanto tinha percebido, o override servia para dizer por exemplo, que uma funcao de uma classe filha (TB.Func) estava a substituir uma funcao com o mesmo nome na classe pai (TA.func). Ou seja, ao declarar a TB.func com ' function func : integer; override ;' é que iamos obter resultado que está a ser apresentado. Resumindo: Se como puseste neste exemplo, ao chamares a TC.func ele sabe que é a da definiçao da TB, para que serve o override então? Partilhar esta mensagem Ligação para a mensagem Partilhar noutros sites
pwseo 231 Denunciar mensagem Publicado 15 de Janeiro de 2013 (editado) Kline: se numa situação hipotética tivesses um conjunto de objectos que não sabias se seriam TA, TB ou TC apontados por um apontador (lol), ao chamar func irias obter sempre o resultado de TA.func porque os métodos não seriam "resolvidos" durante a execução mas sim durante a compilação (e aí o computador assumiria TA, a classe base). Mas pronto, cá vai uma tentativa de explicar algumas coisas de uma forma mais prática... Essencialmente tem a ver com o facto dos métodos serem estáticos (determinados na compilação) ou virtuais (dinâmicos, determinados durante a execução). Para simular isso fiz casting de tipos de objectos no seguinte programa... Leiam com tempo e interpretem os resultados com cuidado {$MODE OBJFPC} program oop; type (* Uma classe normal *) TParent = class (TObject) function method: integer; end; (* Uma subclasse típica, derivada de TParent *) TChild = class (TParent) function method: integer; end; (* Esta classe é basicamente igual à TParent mas a função 'method' foi declarada como 'virtual' *) TVirtualParent = class (TObject) function method: integer; virtual; end; (* A subclasse de TVirtualParent, onde declaramos 'method' como 'override' *) TOverriddenChild = class (TVirtualParent) function method: integer; override; end; (* É uma subclasse basicamente igual à anterior, mas não dissemos que 'method' é 'override' *) TNotOverriddenChild = class (TVirtualParent) function method: integer; end; (* Classe igual à TParent, mas com 'method' declarado como 'abstract' e 'virtual'. Isto faz com que além de virtual, o método só tenha que ser implementado nas subclasses desta classe. Nota: só métodos virtuais podem ser abstractos, pelo menos no FPC. *) TAbstractParent = class (TObject) function method: integer; virtual; abstract; end; TAbstractChild = class (TAbstractParent) function method: integer; override; end; (* A keyword 'overload' é utilizada essencialmente para compatibilidade com o Delphi, uma vez que no Free Pascal todos os métodos podem ser overloaded por defeito sem nenhuma keyword para isso. *) TOverloadClass = class (TObject) function method: integer; overload; function method (v: integer): integer; overload; end; TOtherParent = class (TObject) procedure method; virtual; end; TInheritedClass = class (TOtherParent) procedure method; override; end; function TParent.method: integer; begin method := 10; end; function TChild.method: integer; begin method := 11; end; function TVirtualParent.method: integer; begin method := 20; end; function TOverriddenChild.method: integer; begin method := 21; end; function TNotOverriddenChild.method: integer; begin method := 22; end; function TAbstractChild.method: integer; begin method := 31; end; function TOverloadClass.method: integer; begin method := 41; end; function TOverloadClass.method (v: integer): integer; begin method := 42 + v; end; procedure TOtherParent.method; begin writeln('TOtherParent.method()'); end; procedure TInheritedClass.method; begin (* Aqui chamamos TOtherParent.method() *) inherited; writeln('TInheritedClass.method()'); end; var Pp, Pc: TParent; C: TChild; Vp, Vca, Vcb: TVirtualParent; Cva: TOverriddenChild; Cvb: TNotOverriddenChild; Ac: TAbstractChild; Oc: TOverloadClass; Op: TOtherParent; Ic: TInheritedClass; begin (* O problema com estas 3 chamadas iniciais é o seguinte: 'method' foi declarado como um método estático (ie. não 'virtual'). O que isso significa é que se eu instanciar um TChild como sendo TParent, os métodos serão os de TParent e não os de TChild, como acontece com o objecto 'Pc' abaixo. *) Pp := TParent.Create(); writeln('TParent.method(): ', Pp.method()); C := TChild.Create(); writeln('TChild.method(): ', C.method()); Pc := TParent(TChild.Create()); writeln('TParent(TChild).method(): ', Pc.method()); (* As próximas 3 chamadas incluem classes onde 'method' foi declarado como 'virtual' na classe base e como 'override' na subclasse. O resultado é que se eu instanciar um TOverridenChild como sendo um TVirtualParent, os seus métodos serão os que foram definidos (overridden) pela subclasse, e não os da TVirtualParent (ver Vca). *) Vp := TVirtualParent.Create(); writeln('TVirtualParent.method(): ', Vp.method()); Cva := TOverriddenChild.Create(); writeln('TOverriddenChild.method(): ', Cva.method()); Vca := TVirtualParent(TOverriddenChild.Create()); writeln('TVirtualParent(TOverriddenChild).method(): ', Vca.method()); (* Aqui no caso da TNotOverridenChild o que acontece é que apesar da TVirtualParent ter 'method' como virtual, na TNotOverridenChild não está como 'override', e por isso, instanciando TNotOverridenChild como TVirtualParent, será o method de TVirtualParent a ser executado. *) Cvb := TNotOverriddenChild.Create(); writeln('TNotOverriddenChild.method(): ', Cvb.method()); Vcb := TVirtualParent(TNotOverriddenChild.Create()); writeln('TVirtualParent(TNotOverriddenChild).method(): ', Vcb.method()); (* Este objecto é um TAbstractChild, uma subclasse que implementa um método definido como 'abstract' na classe base TAbstractClass. Os métodos definidos como 'abstract' só precisam de ser implementados em subclasses. *) Ac := TAbstractChild.Create(); writeln('TAbstractChild.method(): ', Ac.method()); Oc := TOverloadClass.Create(); writeln('TOverloadClass.method(): ', Oc.method()); writeln('TOverloadClass.method(7): ', Oc.method(7)); (* Basicamente, TOtherParent é uma classe como outra qualquer *) Op := TOtherParent.Create(); writeln; writeln('TOtherParent.method():'); Op.method(); (* No entanto, quando chamamos TInheritedClass.method(), este irá chamar dentro de si TOtherParent.method() (ver acima na implementação) *) Ic := TInheritedClass.Create(); writeln; writeln('TInheritedClass.method():'); Ic.method(); Pp.Destroy(); Pc.Destroy(); C.Destroy(); Vp.Destroy(); Vca.Destroy(); Vcb.Destroy; Cva.Destroy(); Cvb.Destroy(); Ac.Destroy(); Oc.Destroy(); Op.Destroy(); Ic.Destroy(); end. Cá está o output: pwseo@laptop:~$ ./oop TParent.method(): 10 TChild.method(): 11 TParent(TChild).method(): 10 TVirtualParent.method(): 20 TOverriddenChild.method(): 21 TVirtualParent(TOverriddenChild).method(): 21 TNotOverriddenChild.method(): 22 TVirtualParent(TNotOverriddenChild).method(): 20 TAbstractChild.method(): 31 TOverloadClass.method(): 41 TOverloadClass.method(7): 49 TOtherParent.method(): TOtherParent.method() TInheritedClass.method(): TOtherParent.method() TInheritedClass.method() Penso não me ter enganado. Editado 15 de Janeiro de 2013 por pwseo Correcção da LP Partilhar esta mensagem Ligação para a mensagem Partilhar noutros sites
Kline777 97 Denunciar mensagem Publicado 16 de Janeiro de 2013 Nice... já aprendi umas coisitas hoje Thanks Partilhar esta mensagem Ligação para a mensagem Partilhar noutros sites