Jump to content
thoga31

OOP, debate sobre a alteração de métodos herdados

Recommended Posts

thoga31

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 ;)

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
Kline777

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?

Share this post


Link to post
Share on other sites
pwseo

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.

Edited by pwseo
Correcção da LP

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.