Ir para o conteúdo
  • Revista PROGRAMAR: Já está disponível a edição #60 da revista programar. Faz já o download aqui!

thoga31

Problemas com Property's

Mensagens Recomendadas

thoga31

Ora, agora sou eu que tenho uma dúvida... Já fazia tempo... :D

Ando nas minhas experiências com classes, e estou a entrar na história das propriedades. Acostumado ao POO do VB.NET, o POO do Delphi é-me diferente aka estranho e a little bit stupid (ai o @nuno que me excomunga :knuppel2:).

Consegui que o seguinte código fosse compilado,m mas dá-me o erro de execução nº 217 (whatever it means...).

Mais, suponho que haja forma de definir a propriedade Angle sem ter de recorrer ao InAng mas fazendo Angle := valor (nas linhas realçadas).

{$mode DELPHI}

PROGRAM EX_CLASSES;
USES CRT, MATH;

TYPE CAngle = class
             private
               //Ang : real;
               procedure InAng(value : real);
               function OutAng : real;

             public
               function Rad2Deg(const rad : real) : real;
               function Deg2Rad(const deg : real) : real;
               property Angle : real read OutAng write InAng;
               constructor Create;
     end;

VAR Angulo : CAngle;
    Num: real;


FUNCTION CAngle.OutAng : real;
BEGIN
     result := Angle;
END;


CONSTRUCTOR CAngle.Create;
BEGIN
     Angle := 0;
END;


PROCEDURE CAngle.InAng(value : real);
BEGIN
     Angle := value;
END;


FUNCTION CAngle.Rad2Deg(const rad : real) : real;
BEGIN
     result := (rad*180)/pi;
END;


FUNCTION CAngle.Deg2Rad(const deg : real) : real;
BEGIN
     result := (deg*pi)/180;
END;


BEGIN
     write('Angulo em graus? ');
     readln(num);
     with Angulo do begin
          Create;
          InAng(Num);
          writeln(Angle:0:5,' graus em radianos: ',Deg2Rad(Angle):0:5);
          writeln('Teste da conversÆo de cancelamento: ',Rad2Deg(Deg2Rad(Angle)):0:5);
     end;
     readln; // pausa
END.

Cumprimentos. ;)

P.S. - yah yah, não comentei este programa :cheesygrin:


Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nunopicado

Olha o tau-tau miudo...  :cheesygrin:

Então vai assim:

O constructor não pode estar dentro do WITH, pelo facto de que a variavel Angulo é nil antes de ser chamado o create, pelo que não tem ainda método algum.

Pelo mesmo motivo, não podes chamar simplesmente assim:

Angulo.create;  // Isto dá erro

A maneira correcta de inicializar uma classe em Delphi é:

Angulo:=CAngle.create;  // atribuis à variável Angulo o resultado do CONSTRUCTOR da classe.

A partir daqui, já podes colocar o resto dentro do WITH, assim:

Angulo:=CAngle.Create;
     with Angulo do begin

          InAng(Num);

          writeln(Angle:0:5,' graus em radianos: ',Deg2Rad(Angle):0:5);
          writeln('Teste da conversão de cancelamento: ',Rad2Deg(Deg2Rad(Angle)):0:5);
     end;
     Angulo.Free;  // Deves sempre correr o DESTRUCTOR da classe quando deixas de a usar, para libertar a memoria...
     // Aqui já podes correr directamente da variável Angulo, pois a classe já está atribuida nesta variável

Isto tras-nos agora a outro problema...

Não estou a ver para que queres neste caso a propriedade... Se as funções conversoras são publicas... Ou estou a ver mal a coisa?


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nunopicado

Estava a ver melhor o teu código e reparei que me passaram alguns... pormenores!  :cheesygrin:

Os procedimentos InAng e OutAng realmente não são necessários. Primeiro porque os estás a usar mal, segundo o conceito de classes:

Visto que é um procedimento PRIVATE, nunca o poderias usar no corpo principal. No caso, só o conseguiste, porque "dobraste" as leis de Object Pascal, e criaste a classe dentro do programa.

NOTA1: 1 Classe = 1 Unit - É esta a regra para poder haver segurança

Independentemente disto, para um teste, é quanto basta, desde que tenhas a noção de que todos os elementos da classe, sejam private ou public, ficam disponiveis para todo o programa, pois essa segurança refere-não se aplica ao namespace à qual a classe pertence, ou seja, a propria unit ou programa.

Ponto 2:

As funções Deg2Rad e Rad2Deg, sendo publicas e aceitando parametros, tornam inuteis as propriedades. Aliás, tornam inútil até o uso da classe.

Mas como eu sei que aqui o que te interessa não é propriamente as funções, mas sim o uso da classe, sugiro-te o seguinte:

As funções passam a ser privadas, e deixas publico apenas as propriedades e o constructor.

Ainda sugiro outra coisa:

Usas o constructor e / ou uma propriedade para definir o angulo, e depois duas propriedades só de leitura para fazer o interface com as funções de conversão.

Do tipo:

Para o seguinte programa principal, desenvolve uma classe:

BEGIN
     Angulo:=CAngle.Create;
     with Angulo do
          repeat
                write('Angulo a converter? ');
                readln(num);
                Angle:=Num;
                writeln('Valor Inserido: ',Angle:0:5);
                writeln('      em Graus: ',Graus:0:5);
                writeln('   em Radianos: ',Radianos:0:5);
                writeln;
                writeln('         Teste: ',Test:0:5);
                writeln;
          until Num=0;
     Angulo.Free;
     delay(1000); // pausa
END.

NOTA: A classe deverá permitir que o angulo seja inserido também no momento da criação, e não apenas através da propriedade Angle...

(Esta é só para teres oportunidade de usar uma coisa que gostas muito... heheheh)


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
thoga31

Agora estou no PC de uma amiga minha, e não tenho o FPC aqui (claro) - só o youtube a cantar o Pintainho Piu... Enfim...

Obrigado desde já pela resposta. Eu já tinha falado um pouco com o @pedro-kun e já tinha corrigido algumas coisas, mas estou a ver que tenho MUITO para aprender... :cheesygrin:

E desculpa lá qualquer coisinha, mas em VB.NET as propriedades são very good nice sim sinhor :D

Public Class CAngulo

Public Sub New(Byval value As Double)
    Ang = value  ' ou ang ou angulo, só vendo e testando esta linha xD
End Sub

Dim Ang As Double
Public Property Angulo As Double
    Set(ByVal value As Double)
        Ang = value
    End Set
    Get
        Return Ang
    End Get
End Property

End Class

'...

Public Angulo As New CAngulo(0)

E não falha, eheh :)

Cumprimentos. ;)


Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nunopicado

Como eu te disse ontem, antes de saber, tudo é complicado... :cheesygrin:

Estava aqui a olhar para a versão VB.net, e a pensar: Eh pá, não precisavam complicar tanto! lol

É tudo uma questão de hábito!

Entretanto vou por só uma pequena alteração no "desafio", que é uma função para testar a conversão (ou seja, o resultado das duas contas tem de ser sempre igual ao angulo inserido) para simular o teu uso das duas funções no programa principal.

Já agora, um pormenor (que não é mais que isso, pois não é obrigatório para funcionar):

Em Delphi (e acho que não só) convencionou-se que as definições das classes comecem por T. No teu caso, em vez de CAngle, seria algo tipo TAngleConverter ou coisa parecida.

Mas lá está, isto é apenas uma convenção para facilitar a leitura por parte dos humanos, o compilador não passa cartão a isto...

Tenta lá então fazer uma classe para o programa acima, e depois mostra aí... ;)


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nunopicado

lol Pensei que o C de CAngle era algo tipo ConverterAngle abreviado... hehehehe

Amanhã também é dia! :cheesygrin:


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

Aqui fica um exemplo funcional e comentado, por falta de paciência para um post mais longo.

Acrescento apenas que as variáveis a e b no meu código são na verdade apontadores para instâncias da classe TAngle, daí a necessidade de fazer a := TAngle.Create;, uma vz que o construtor é na realidade uma função que aloca um bloco de memória e devolve o seu endereço. Sem isso, a ou b continuam nil (que é quase um sinónimo para "inexistentes").

{$mode objfpc}
program thoga;

uses
  Math;

type
  TAngle = class
  private
    (* Aqui guardamos o campo ao qual vamos mais tarde
       aceder através da propriedade 'Angle' *)
    FAngle: real;
    (* Só precisamos de setAngle, uma vez que a leitura
       da propriedade só nos devolve o valor de 'FAngle' *)
    procedure setAngle(v: real);
  public
    (* É-nos muito conveniente ter dois construtores *)
    constructor Create; overload;
    constructor Create(v: real); overload;
    (* Cá está: ler de 'FAngle' e escrever via 'setAngle' *)
    property Angle: real read FAngle write setAngle;
  end;

procedure TAngle.setAngle(v: real);
begin
  (* uma vez que não podemos fazer x mod 360 sendo
     x um real, fiz a conta de outra forma *)
  self.FAngle := v - (floor(v / 360) * 360)
end;

constructor TAngle.Create;
begin
  (* Isto é importante; invoca o construtor da
     classe ascendente (TObject, neste caso) *)
  inherited Create;
  self.Angle := 0;
end;

constructor TAngle.Create(v: real);
begin
  inherited Create;
  (* Usamos a nossa propriedade para fazer wrap-around dos valores! *)
  self.Angle := v;
end;

var
  a, b: TAngle;
begin
  a := TAngle.Create;
  (* A parte bonita é que o nosso código vai transformar aquele 402
     em 42 porque definimos a property 'Angle' *)
  b := TAngle.Create(402); 
  writeln(a.angle:0:2);
  writeln(b.angle:0:2);
  a.Free;
  b.Free;
end.

Aqui fica compilado e executado: http://ideone.com/DOTVm

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

A variável self é essencialmente a instância do objecto com o qual estás a lidar.

Eu uso ali sem grande necessidade, mas é uma boa prática para evitar ambiguidades. Imagina que existia uma variável fora da classe com o nome FAngle, o compilador poderia referir-se a uma que não fosse a que tu queres; com a utilização de self sabes que te referes à variável do objecto.

É a mesma coisa que a variável this noutras linguagens.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
thoga31

A variável self é essencialmente a instância do objecto com o qual estás a lidar.

Eu uso ali sem grande necessidade, mas é uma boa prática para evitar ambiguidades. Imagina que existia uma variável fora da classe com o nome FAngle, o compilador poderia referir-se a uma que não fosse a que tu queres; com a utilização de self sabes que te referes à variável do objecto.

É a mesma coisa que a variável this noutras linguagens.

Java power e.g. ;)

Estou naquela de vou-me embora, mas aqui os mans estão à espera que as donzelas se arranjem todas, eheheh :cheesygrin:  (não, elas não estão a ler isto)


Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nunopicado

Então Thoga, já fizeste o exercício?  :thumbsup:


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
thoga31

Então Thoga, já fizeste o exercício?  B)

Calma lá que só agora começaram as férias :thumbsup:

E mesmo assim tenho já montes de trabalhos e relatórios para o Natal - é a única prenda que os profs sabem dar... Enfim...


Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nunopicado

Atão... Não queres ser médico?

Tens de estudar pá! hehehe


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
thoga31

Pronto, já aprendi como isto funciona! :thumbsup:

Obrigado a ambos! B)

{$mode delphi}

program OOP_Test;

type TAngle = class
              private
              (* Visível só para a classe *)
                     AngleValue : real;  // Valor do ângulo (a propriedade deve ter algo, por si só não é nada)
                     procedure SetAngle(const Ang : real); // Para escrever na Property

              public
              (* Visível para todo o programa *)
                    constructor Create(const Ang : real);  // "Criação" da classe
                    property Angle : real read AngleValue write SetAngle;  // Valor do ângulo (propriedade)

     end;

var Angulo : TAngle;  // classe de teste
    Num : real;       // valor introduzido pelo utilizador


constructor TAngle.Create(const Ang : real);
(*  CONSTRUTOR  *)
begin
     inherited Create;  // Invoca construtor da classe ascendente TObject
     self.Angle := Ang; // Define propriedade com o valor pretendido
end;

procedure TAngle.SetAngle(const Ang : real);
(*  LER PROPRIEDADE "ANGLE"  *)
begin
     self.AngleValue := Ang - (round(Ang / 360) * 360);
     // ângulo principal de um ângulo (>-180º e <=180º)
end;


begin
     write('Introduza angulo em graus: ');
     readln(Num);
     Angulo := TAngle.Create(Num);  // cria a classe logo com o valor pretendido
     writeln('Angulo principal: ', Angulo.Angle:0:3);  // devolve propriedade "angle"
     readln;  // pausa
end.

http://ideone.com/0OHkm

NOTA: no link está "uses math", mas tal é desnecessário! Foi falha da minha parte... B)


Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
pwseo

O estudante de Medicina sou eu :P no 6º ano.

Quando ao código, agora sim está bem.

É de reforçar o facto de que para utilizar propriedades não precisamos de ter métodos para definir e para ler a propriedade; Neste caso, como é  óbvio, basta-nos ter o método para definir, visto que a leitura não altera em nada o valor armazenado. (por vezes as pessoas fazem getters e setters desnecessários).

Outra coisa: boa ideia utilizar o ideone :)

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
thoga31

Outra coisa: boa ideia utilizar o ideone B)

Sempre se pode testar sem que o pessoal tenha de compilar. Testam-se vários inputs, e finito. B)

Obrigado, mais uma vez. Já fazia falta saber mais OOP em Object Pascal / Delphi. :thumbsup:


Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
nunopicado
:)   ;)

"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.