You are on page 1of 76

1

CARACTERSTICAS E RECURSOS DA LINGUAGEM OBJECT PASCAL. AGOSTO/2002 COMENTRIOS NOVAS CARACTERSTICAS DE PROCEDIMENTOS E FUNES VARIVEIS CONSTANTES OPERADORES TIPOS DO OBJECT PASCAL TIPOS DEFINIDOS PELO USURIO TYPECASTING E CONVERSO DE TIPOS RESOURCES TESTANDO CONDIES LOOPS PROCEDIMENTOS E FUNES ESCOPO UNITS PACKAGES PROGRAMAO ORIENTADA A OBJETOS USANDO OS OBJETOS DO DELPHI TRATAMENTO ESTRUTURADO DE EXCEES RUNTIME TYPE INFORMATION

-------------------------------------------------------------------------------Para comear, voc receber uma introduo aos fundamentos da linguagem Object Pascal tal como regras da linguagem e construes. Mais tarde, voc aprender sobre alguns dos mais avanados aspectos de Object Pascal tais como classes e tratamento de excees. Por no ser um tutorial para iniciantes, assumimos que voc j tem alguma experincia com outra linguagem de programao de alto nvel tal como, C, C++ ou Visual Basic, e comparamos a estrutura da linguagem Object Pascal com essas outras linguagens. Ao terminar de ler este tutorial, voc entender como conceitos de programao tais como variveis, tipos, operadores, loops, casos, excees e objetos funcionam no Pascal. Comentrios Como ponto de partida, voc ver como fazer comentrios no seu cdigo Pascal. Object Pascal suporta trs tipos de comentrios: comentrios com chaves, comentrios com parnteses/asterisco; e comentrios com barras duplas no estilo do C++. Abaixo, exemplos dos trs tipos de comentrios: { Comment using curly braces } (* Comment using paren and asterisk *) // C++-style comment Os dois tipos de comentrios do Pascal so praticamente idnticos no comportamento. O compilador considera o comentrio como sendo tudo entre os delimitadores de abertura e fechamento de comentrios. Para comentrios no estilo do C++, tudo que segue as barras duplas at o fim da linha considerado um comentrio.

Novas Caractersticas de Procedimentos e Funes Parnteses Uma das pequenas caractersticas do Object Pascal que parnteses so opcionais quando chamamos um procedimento ou funo que no pega parmetros. Assim, os seguintes exemplos de sintaxe so ambos vlidos: Form1.Show; Form1.Show(); Esta no uma das coisas com as quais voc deve esquentar a cabea, mas particularmente bom para aqueles que dividem seu tempo entre Delphi e linguagens como C++ ou Java, em que parnteses so requeridos. Se voc no gasta 100% do seu tempo com Delphi, esta caracterstica significa que voc no tem que lembrar de usar diferentes sintaxes de chamada de funes para linguagens diferentes. Sobrecarga Delphi 4 introduz o conceito de sobrecarga de funo, isto , a capacidade de ter mltiplos procedimentos ou funes de mesmo nome com lista de parmetros diferentes. Todos os mtodos sobrecarregados no devem ser declarados com a diretiva overload como mostrado a seguir: procedure Ola(I: Integer); overload; procedure Ola(I: string); overload; procedure Ola(D: Double); overload; Note que as regras para sobrecarga de mtodos de uma classe so levemente diferentes e so explicados na seo Sobrecarga de Mtodos. Parmetros de Valor Default Tambm novo para Delphi 4 so os parmetros de valor default, isto , a habilidade de fornecer um valor default para uma funo ou procedimento e no ter que passar aquele parmetro quando chamar a rotina. Para declarar um procedimento ou funo que contm parmetros de valor default, siga o tipo do parmetro com um sinal de igual e o valor default como mostrado no exemplo seguinte: procedure TemValDef(S: string; I: Integer = 0); O procedimento TemValDef() pode ser chamado de uma das formas. Primeiro, voc pode chamar especificando os dois parmetros: TemValDef('ola', 26); Segundo, voc pode especificar apenas o parmetro S e usar o valor default para I: TemValDef('ola'); // valor default usado para I Voc deve seguir vrias regras quando usar parmetros de valor default:

Parmetro que tem valor default devem aparecer no fim da lista de parmetros. Parmetros sem valores default no podem seguir parmetros com valores default em uma lista de parmetros de uma funo ou procedimento. Parmetros de valor default devem ser de um tipo ordinal, ponteiro ou set. Parmetros de valor default devem ser passados por valor ou como constante. Eles no devem ser parmetros de referncia, out ou sem tipo. Um dos maiores benefcios dos parmetros de valor default adicionar funcionalidade a funes e procedimentos existentes sem sacrificar compatibilidades anteriores. Por exemplo, suponha que voc venda uma unidade que contenha uma funo revolucionria chamada AddInts() que some dois nmeros: function AddInts(I1, I2: Integer): Integer; begin Result := I1 + I2; end; Para fugir da concorrncia, voc sente que deve atualizar esta funo de forma que ela tenha a capacidade de somar trs nmeros. Contudo, voc est detestanto fazer isso porque adicionar um parmetro far com que cdigos existentes que chamam esta funo no compilem. Graas aos parmetros default, voc pode aumentar a funcionalidade de AddInts() sem comprometer a compatibilidade: function AddInts(I1, I2: Integer; I3: Integer = 0); begin Result := I1 + I2 + I3; end; Variveis Voc pode ter o costume de declarar variveis instintivamente: "Eu preciso de um outro inteiro, ento s declarar um bem aqui no meio deste bloco de cdigo". Se esta tem sido a sua prtica, voc vai ter que se retreinar um pouco para usar variveis em Object Pascal. Object Pascal requer que voc declare todas as variveis no incio de sua prpria seo antes de comear um procedimento, funo ou programa. Talvez voc escreva cdigos como este: void foo(void) { int x = 1; x++; int y = 2; float f; //... etc ... } Em Object Pascal, tal cdigo deve ser ajeitado e estruturado um pouco mais para se paracer com este: Procedure Foo; var x, y: Integer; f: Double; begin

x := 1; inc(x); y := 2; //... etc ... end; Note como Object Pascal permite a voc agrupar mais do que uma varivel do mesmo tipo juntamente na mesma linha com a seguinte sintaxe: Var1, Var2 : AlgumTipo; Lembre que quando voc est declarando uma varivel em Object Pascal, o nome da varivel precede o tipo, e h uma vrgula entre as variveis e tipos. Note que a inicializao da varivel sempre separada da declarao da varivel. Uma caracterstica da linguagem introduzida no Delphi 2.0 permite que voc inicialize variveis globais dentro de um bloco var. Exemplos demonstrando a sintaxe para fazer isso so mostrados a seguir: var i: Integer = 10; S: string = 'Ol mundo'; D: Double = 3.141579; Nota: Pr-inicializao de variveis apenas permitida para variveis globais e no para variveis locais a um procedimento ou funo. Dica: O compilador Delphi entende que todos os dados globais so automaticamente inicializados com zero. Quando sua aplicao iniciada, todo tipo inteiro possui 0, tipos ponto-flutuante possuem 0.0, ponteiros sero nil, strings estaro vazias, e assim em diante. Ento, no necessrio inicializar com zero, dados globais em seu cdigo fonte. Constantes Constantes em Pascal so definidas na clusula const, que comportam-se similarmente a palavra reservada const do C. Aqui est um exemplo de trs declaraes de constantes em C: const float ANumeroDecimal = 3.14; const int i = 10; const char * ErrorString = 'Perigo, Perigo, Perigo'; A maior diferena entre constantes do C e do Object Pascal que Object Pascal, assim como Visual Basic, no requer que voc declare o tipo constante junto com o valor na declarao. O compilador do Delphi automaticamente aloca espao apropriado para a constante baseado nos seus valores, ou, no caso de constantes escalares tais como Inteiros, o compilador fiscaliza os valores que ele manipula, e espaos nunca so alocados: const ADecimalNumber = 3.14; i = 10; ErrorString = 'Danger, Danger, Danger!';

Nota: Espao alocado para constantes como segue: Valores Inteiros so ajustados dentro do menor tipo alocvel (10 dentro de um ShortInt, 32000 dentro de um SmallInt, e assim em diante). Valores alfanumricos so colocados em char ou no tipo string definido atualmente (por $H). Valores de ponto-flutuante so mapeados para o tipo de dado extendido, a menos que o valor contenha 4 ou menos casas decimais explicidamente, neste caso ele mapeado para um tipo Comp. Conjuntos de inteiros e char so claramente armazenados neles mesmos. Opcionalmente, voc pode tambm especificar um tipo de constante na declarao. Isto fornece a voc controle sobre como o compilador trata suas constantes: const ADecimalNumber: Double = 3.14; I: Integer = 10; ErrorString: string = 'Danger, Danger, Danger!'; Object Pascal permite o uso de funes em tempo de compilao em declaraes const e var. Essas rotinas incluem: Ord(), Chr(), Trunc(), Round(), High(), Low(), e SizeOf(). Por exemplo, todos os cdigos seguintes so vlidos: type A = array[1..2] of Integer; const w: Word = SizeOf(Byte); var i: Integer = 8; j: SmallInt = Ord(a); L: Longint = Trunc(3.14159); x: ShortInt = Round(2.71828); B1: Byte = High(A); B2: Byte = Low(A); C: char = Chr(46); Cuidado: O comportamento das constantes de tipo especificado do Delphi 32-bit diferente daquele no Delphi 1.0 16-bit. No Delphi 1.0, o identificador declarado no era tratado como uma constante mas como uma varivel pr-inicializada chamada de typed constant . Contudo, a partir do Delphi 2, constantes de tipo especificado tem a capacidade de serem verdadeiramente constante. Delphi fornece uma compatibilidade s verses anteriores ao acionar a pgina Compiler do dilogo Project | Options, ou ento voc pode usar a diretiva de compilao $J. Por default, esse dispositivo est habilitado para compatibilidade com cdigo do Delphi 1.0, mas melhor voc no confiar nessa capacidade. Se voc tentar alterar o valor de qualquer dessas constantes, o compilador Delphi emite um erro explicando que contra as regras alterar o valor de uma constante. Pelo fato das constantes serem somente-leitura, Object Pascal otimiza seu espao de dados armazenando aquelas constantes que merecem armazenagem nessas pginas de cdigo da aplicao. Se para voc no est claro as noes de pginas de cdigo e dados, veja "A API Win32".

Nota: Object Pascal no tem um pr-processador como C e C++ tem. No h o conceito de uma macro em Object Pascal e, assim, nenhum equivalente para #define do C para declarao de constantes. Embora voc possa usar a diretiva de compilao $define do Object Pascal para compilao condicional similarmente a diretiva #define do C, voc no pode us-la para definir constantes. Use const em Object Pascal onde voc usaria #define para declarar uma constate em C ou C++. Operadores Operadores so os smbolos no seu cdigo que habilitam voc a manipular todos os tipos de dados. Por exemplo, h operadores para somar, subtrair, multiplicar e dividir dados numricos. H tambm operadores para enderear um elemento particular de um array. Esta seo explica alguns dos operadores do Pascal e descreve algumas das diferenas entre as suas contrapartes no C e no Visual Basic.

Operadores de Atribuio Se voc iniciante em Pascal, o operador de atribuio do Delphi ser uma das coisas mais duras para se acostumar. Para atribuir um valor a uma varivel, use o operador := como voc usaria o operador = do C ou do Visual Basic. Exemplo: Number1 := 5; Operadores de Comparao Se voc j programou em Visual Basic, voc dever se sentir confortvel com os operadores de comparao do Delphi porque eles so praticamente idnticos. Esses operadores so claramente um padro em todas as linguagens de programao, ento eles sero vistos brevemente nesta seo. Object Pascal usa o operador = para executar comparaes lgicas entre duas expresses ou valores. O operador = do Object Pascal anlogo ao operador == do C, assim uma expresso C que seria escrita como: if (x == y) Ser escrita dessa forma no Object Pascal: if x = y Note: Lembre que em Object Pascal, o operador := usado para atribuir um valor a uma varivel, e o operador = compara os valores de dois operandos. O no-igual-a do Delphi <>, e seu propsito idntico ao operador != do C. Para determinar se duas expresses no so iguais, use este cdigo:

if x <> y then FazerAlgo Operadores Lgicos Pascal usa as palavras and e or como operadores e e ou lgicos, enquanto que C usa os smbolos && e || , respectivamente, para esses operadores. O uso mais comum dos operadores and e or como parte de uma expresso if ou loops, tais como os dois exemplos seguintes: if (Condio 1) and (Condio 2) then FazerAlgo; while (Condio 1) or (Condio 2) do FazerAlgo; O operador no lgico do Pascal not, que usado para checar a comparao para uma condio falsa. anlogo ao operador ! do C. tambm usado freqentemente como parte de uma expresso if: // Se condio falsa ento... if not (condio) then (fazer algo); A Tabela 2.1 fornece uma fcil referncia de como os operadores do Pascal mapeiam os operadores correspondentes do C e do Visual Basic. Tabela 2.1. Atribuio, comparao e operadores lgicos. -------------------------------------------------------------------------------Operador Pascal C Visual Basic -------------------------------------------------------------------------------Atribuio Comparao No igual a Menor que Maior que Menor ou igual a Maior ou igual a E lgico OU Lgico No Lgico := = <> < > <= >= and or not = == != < > <= >= && || ! = = <> < > <= >= and or not

-------------------------------------------------------------------------------Operadores Aritmticos Voc j deve estar familiarizado com muitos dos operadores aritmticos do Object Pascal porque eles geralmente so similares queles usados em C, C++ e Visual Basic. A Tabela 2.2 ilustra todos os operadores aritmticos dos Pascal e suas contrapartes em C e Visual Basic.

Tabela 2.2. Operadores Aritmticos. -------------------------------------------------------------------------------Operador Pascal C Visual Basic -------------------------------------------------------------------------------Adio + + + Subtrao Multiplicao * * * Diviso de ponto-flutuante / / / Diviso inteira div / / Mdulo mod % Mod -------------------------------------------------------------------------------Voc pode notar que a principal diferena entre Pascal e outras linguagens que Pascal tem diferentes operadores de diviso para a matemtica de inteiros e de pontos-flutuante. O operador div trunca automaticamente qualquer resto quando divide duas expresses inteiras. Nota: Lembre-se de usar o operador de diviso correto para os tipos de expresses com os quais voc est trabalhando. O compilador Object Pascal d a voc um erro se voc tentar dividir dois nmeros de ponto-flutuante com o operador de inteiros div ou dois inteiros com o operador de ponto-flutuante / , como os seguintes cdigos ilustrativos: var i: Integer; r: Real; begin // Esta linha causar um erro de compilao i := 4 / 3; // Esta linha tambm causar um erro f := 3.4 div 2.3; end; Muitas outras linguagens de programao no distinguem entre as divises inteiras e de ponto-flutuante. Em vez disso, elas sempre efetuam diviso de ponto-flutuante e ento convertem o resultado para um inteiro quando necessrio. Isto pode ser um pouco caro em termos de performance. O operador div do Pascal mais rpido e mais especfico. Operadores Bit-a-Bit Operadores Bit-a-bit so operadores que permitem que voc modifique bits individuais de uma determinada varivel. Os operadores bit-a-bit comuns permitem que voc desloque para a esquerda, para a direita ou efetuar as operaes e, no, ou e ou exclusivo (xor) bit-a-bit com dois nmeros. Os operadores Shift+left e Shift+right so shl e shr, respectivamente, e eles so no C os operadores << e >>. Os demais operadores bit-a-bit do Pascal so fceis de se lembrar: and, not, or, e xor. A Tabela 2.3 lista os operadores bit-a-bit.

Tabela 2.3. Operadores bit-a-bit -------------------------------------------------------------------------------Operador Pascal C Visual Basic -------------------------------------------------------------------------------E and & And No not ~ Not Ou or | Or Xor xor ^ Xor Shift+left shl << Nenhum Shift+right shr >> Nenhum --------------------------------------------------------------------------------

Procedimentos de Incrementar e Decrementar Procedimentos para incrementar e decrementar geram cdigo otimizado para somar um ou subtrair um de uma determinada varivel inteira. Pascal na verdade no fornece operadores de incremento e decremento similares aos operadores ++ e -- do C, mas os procedimentos Inc() e Dec() do Pascal compilam de forma mais otimizada para uma instruo de mquina. Voc pode chamar Inc() ou Dec() com um ou dois parmetros. Por exemplo, as duas linhas seguintes de cdigo incrementam e decrementam varivel, respectivamente, de um, usando as instrues assembly inc e dec: Inc(variable); Dec(variable); Compare as duas linhas seguintes, que incrementam ou decrementam varivel de 3 usando as instrues assembly add e sub: Inc(variable, 3); Dec(variable, 3); A Tabela 2.4 compara os operadores de incremento e decremento de diferentes linguagens. Note: Com a otimizao de compilao habilitada, os procedimentos Inc() e Dec() freqentemente produzem o mesmo cdigo de mquina que a sintaxe variavel := variavel + 1 , assim use qualquer um que voc sentir mais confortvel para incrementar e decrementar variveis. Tabela 2.4. Operadores de incremento e decremento. -------------------------------------------------------------------------------Operador Pascal C Visual Basic -------------------------------------------------------------------------------Incremento Inc() ++ Nenhum Decremento Dec() -Nenhum --------------------------------------------------------------------------------

10

Tipos do Object Pascal Uma das grandes caractersticas do Object Pascal que ele fortemente tipado, ou typesafe. Isto significa que variveis atuais passadas para procedimentos ou funes devem ser do mesmo tipo que o parmetro formal identificado na definio do procedimento ou funo. Voc no ver nenhum compilador famoso alertando sobre converses de ponteiro suspeitas que os programadores de C esto acostumados. Isto porque o compilador Object Pascal no permite que voc chame uma funo com um tipo de ponteiro quando um outro tipo especificado nos parmetros formais da funo (ainda que funes que tomem tipos Pointer no tipados aceitem qualquer tipo de ponteiro). Basicamente, a natureza fortemente tipada do Pascal permite que ele efetue uma verificao de sanidade do seu cdigo - para garantir que voc no esteja tentando por algo em um lugar errado. Comparao de Tipos Os tipos base do Delphi so similares queles do C e do Visual Basic. A Tabela 2.5 compara e constata os tipos bsicos de Object Pascal com aqueles de C/C++ e Visual Basic. Voc pode querer imprimir esta tabela porque ela fornece uma excelente referncia para combinar tipos quando chamar funes em Dynamic Link Libraries (DLLs) que no sejam do Delphi ou arquivos objetos (OBJs) do Delphi ou vice-versa. Table 2.5. Uma comparao dos tipos de 32-bits Pascal-para-C/C++-para-Visual Basic. Type of Variable Pascal C/C++ Visual Basic inteiro sinalizado de 8-bit ShortInt char Nenhum inteiro no-sinalizado de 8-bit Byte BYTE, Nenhum unsigned short inteiro sinalizado 16-bit SmallInt short Short inteiro no-sinalizado 16-bit Word unsigned short Nenhum inteiro sinalizado 32-bit Integer, int, long Integer Longint inteiro no-sinalizado 32-bit Cardinal, unsigned long Nenhum LongWord inteiro sinalizado 64-bit Int64 __int64 Nenhum ponto-flutuante de 4-byte Single float Single ponto-flutuante de 6-byte Real48 Nenhum Nenhum ponto-flutuante de 8-byte Double double Double ponto-flutuante de 10-byte Extended long double Nenhum Dinheiro de 64-bit currency Nenhum Currency Variante de 16-byte Variant, Variant (Default) OleVariant, Variant , TVarData OleVariant, Caracter de 1-byte Char char Nenhum Caracter de 2-byte WideChar WCHAR String de tamanho em bytes fixo ShortString Nenhum Nenhum String dinmica AnsiString AnsiString $ String terminada com Null PChar char * Nenhum Wide String terminada com Null PWideChar LPCWSTR Nenhum String dinmica de 2-byte WideString WideString Nenhum Booleano de 1-byte Boolean, (Qualquer 1-byte) Nenhum ByteBool Booleano de 2-byte WordBool (Qualquer 2-byte) Boolean Booleano de 4-byte BOOL, BOOL Nenhum

11

LongBool --------------------------------------------------------------------------------

Classe do Borland C++Builder que emula o tipo correspondente em Object Pascal.

Nota: Se voc possui um cdigo de 16-bit do Delphi 1.0, esteja avisado de que o tamanho dos tipos Integer e Cardinal aumentou de 16 para 32 bits. Na verdade, isto pouco preciso: no Delphi 2.0 e 3 o tipo Cardinal foi tratado como um inteiro no sinalizado de 31-bit para preservar a preciso aritmtica (porque Delphi 2 e 3 careciam de um verdadeiro inteiro no sinalizado de 32-bit). No Delphi 4, Cardinal um verdadeiro inteiro no sinalizado de 32-bit. Cuidado: Nos Delphis 1, 2 e 3 o identificador de tipo Real especificava um nmero de ponto-flutuante de 6 bytes, que um tipo nico para o Pascal e geralmente incompatvel com outras linguagens. No Delphi 4, Real um alias para o tipo Double. O antigo nmero de pontoflutuante de 6 bytes ainda existe, mas agora ele identificado por Real48. Voc pode tambm forar o identificador Real para referir ao nmero de ponto-flutuante de 6 bytes usando a diretiva {$REALCOMPATIBILITY ON}. Caracteres Delphi fornece trs tipos de caracteres: AnsiChar Este o caracter padro de 1 byte ANSI que programadores j esto acostumados. WideChar Este caracter de 2 bytes de tamanho e representa um caracter Unicode. Char Este atualmente idntico ao AnsiChar, mas a Borland previne que a definio pode mudar nas prximas verses do Delphi para um WideChar. Tenha conscincia de que pelo fato de um caracter no garantir ser de um byte de tamanho, voc no deve codificar o tamanho em suas aplicaes. Em vez disso, voc deve usar a funo SizeOf() onde for apropriado. Nota: O procedimento padro SizeOf() retorna o tamanho, em bytes, de um tipo ou instncia. Um Grande Nmero de Strings Strings so tipos de variveis usadas para representar grupos de caracteres. Toda linguagem tem seu prprio jeito de como os tipos strings so armazenados e usados. Pascal tem muitos tipos strings diferentes para satisfazer as necessidades do programador:

12

AnsiString, o tipo string default para Object Pascal, compreendido de caracteres AnsiChar e permite tamanhos virtualmente ilimitados. tambm compatvel com strings terminadas em null. ShortString permanece na linguagem basicamente para manter a compatibidade com Delphi 1.0. Sua capacidade limitada a 255 caracteres. WideString similar em funcionalidade a AnsiString exceto que formada por caracteres WideChar. PChar um ponteiro para uma string de Char terminada em null como o char * do C ou os tipos lpstr. PAnsiChar um ponteiro para uma string de AnsiChar terminada em null. PWideChar um ponteiro para uma string de WideChar terminada em null. Por default, quando voc declara uma varivel string no seu cdigo como mostrado no exemplo seguinte, o compilador assume que voc est criando um AnsiString: var S: string; // S uma AnsiString Alternativamente, voc pode induzir variveis declaradas com strings ser do tipo ShortString usando a diretiva de compilao $H. Quando o valor da diretiva de compilao $H negativo, variveis string so ShortStrings, e quando o valor da diretiva positivo (o default), variveis string so AnsiStrings. O cdigo seguinte demonstra este comportamento: var {$H-} S1: string; // S1 uma ShortString {$H+} S2: string; // S2 uma AnsiString A exceo regra do $H que uma string declarada com um tamanho explcito (limitado para o mximo de 255 caracteres) sempre uma ShortString: var // Uma ShortString de no mximo 63 caracteres S: string[63]; O Tipo AnsiString O AnsiString, ou tipo long string foi introduzido na linguagem no Delphi 2.0. Ele existe antes de mais nada como um resultado de pedido dos programadores do Delphi 1.0 por um tipo string fcil de usar sem a chata limitao de 255 caracteres. AnsiString isto e mais. Embora AnsiStrings mantenha uma interface quase idntica com seus predecessores, eles so alocados dinamicamente e com garbage-collected (coleta de lixo). Object Pascal tambm gerencia automaticamente a alocao de strings temporrias quando necessrio. Assim voc no precisa se preocupar em alocar buffers para resultados intermedirios como voc faz em C/C++. Adicionalmente, AnsiStrings so sempre terminadas com null, o que as torna compatveis com as strings terminadas com null usadas pela API do Win32. O tipo AnsiString na verdade implementado como um ponteiro para uma estrutura string na memria heap.

13

Cuidado: O formato interno completo de um tipo long string no documentado pela Borland, e a Borland reserva-se o direito de mudar o formato interno de long strings nas distribuies futuras do Delphi. A informao aqui pretende principalmente ajud-lo a entender como trabalham AnsiStrings, e voc deve evitar ficar dependendo da estrutura de uma AnsiString no seu cdigo. Desenvolvedores que migraram do Delphi 1 para o Delphi 2 e que evitaram a implementao de detalhes de string foram capazes de migrar seu cdigo sem nenhum problema. Desenvolvedores que escreveram cdigo que dependia do formato interno (tal como o 0simo elemento na string sendo o tamanho) tiveram que modificar seu cdigo para o Delphi 2. AnsiStrings so referncias, o que significa que muitas strings podem apontar para a mesma memria fsica. Cpias de string, ento, so muito mais rpidas porque ela meramente copia um ponteiro ao invs de copiar o contedo real da string. Quando duas ou mais AnsiStrings dividem uma referncia para a mesma string fsica, o gerenciador de memria do Delphi usa uma tcnica de cpia-sobre-escrita, que permite que ele espere at que uma string seja modificada para liberar uma referncia e alocar uma nova string fsica. O exemplo seguinte ilustra esses conceitos: var S1, S2: string; begin // armazena string em S1, a contagem de referncia de S1 1 S1 := 'E agora para algo... '; S2 := S1; // S2 agora referencia S1. Contagem de referncia de S1 2. // S2 alterado, assim ele copiado para seu prprio // espao de memria, e a contagem de referncia de S1 decrementado. S2 := S2 + 'Completamente diferente!'; Tipos Dinmicos Alm de AnsiString, Delphi fornece muitos outros tipos que so dinmicos. Esses tipos incluem arrays dinmicos, WideString, Variant, OleVariant, interface, e dispinterface. Voc aprender mais sobre cada um desses tipos depois. Tipos dinmicos, algumas vezes chamados de tipos com "coleta de lixo", so tipos que consomem potencialmente algum recurso particular enquanto o usam e liberam esse recurso automaticamente quando ele sai do escopo. claro, a variedade de recursos usados depende do tipo envolvido. Por exemplo, uma AnsiString consome memria para uma string de caracteres enquanto os usa e a memria ocupada pela string de caracteres liberada quando ela sai do escopo. Para variveis globais, este processo claramente direto: como uma parte da finalizao do cdigo gerado por sua aplicao, o compilador insere cdigo para garantir que cada varivel global dinmica est limpa. Como todos os dados globais so inicializados com zero quando sua aplicao carregada, cada varivel global dinmica sempre inicializar contendo um zero, vazia, ou com outro valor indicando que a varivel "no est em uso". Desta forma, a finalizao do cdigo no se esforar em liberar recursos a menos que eles sejam realmente usados na sua aplicao.

14

Sempre que voc declarar uma varivel dinmica local, o processo levemente mais complexo: primeiro o compilador insere cdigo para garantir que a varivel inicializada com zero quando a funo ou procedimento inicia. Em seguida, o compilador gera um bloco de manipulao de exceo try..finally para limpar a varivel dinmica (manipulao de excees so explicados em mais detalhes na seo Manipulao Estruturada de Excees). Com isto em mente, considere o seguinte procedimento: procedure Foo; var S: string; begin // corpo do procedimento // use S aqui end; Embora este procedimento parea simples, se voc levar em conta o cdigo gerado pelo compilador por trs das cenas, ele na verdade se parecer com isto: procedure Foo; var S: string; begin S := ''; try // corpo do procedimento // use S aqui finally // limpe S aqui end; end;

Operaes sobre String Voc pode concatenar duas strings usando o operador + ou a funo Concat(). O melhor mtodo de concatenao de string o operador + porque a funo Concat() existe primeiramente para manter a compatibilidade com verses anteriores. O exemplo seguinte demonstra o uso de + e Concat(): { usando + } var S, S2: string begin S:= 'Cookie ': S2 := 'Monstro'; S := S + S2; { Cookie Monstro } end. { usando Concat() }

15

var S, S2: string; begin S:= 'Cookie '; S2 := 'Monstro'; S := Concat(S, S2); { Cookie Monstro } end. Nota: Sempre use aspas simples (Uma String) quando trabalhar com strings literais em Object Pascal. Dica: Concat() uma das muitas funes e procedimentos "mgicas do compilador" (como ReadLn() e WriteLn(), por exemplo) que no tem uma definio em Object Pascal. Como tais funes e procedimentos so destinados a aceitar um nmero indeterminado de parmetros ou parmetros opcionais, eles no podem ser definidos em termos da linguagem Object Pascal. Por causa disto, o compilador fornece um caso especial para cada uma dessas funes e gera uma chamada para uma das funes de ajuda "mgicas do compilador" definidas na unit System. Essas funes de ajuda so geralmente implementadas na linguagem assembly para contornar as regras da linguagem Object Pascal. Alm dos procedimentos e funes "mgicas do compilador" de suporte strings, h uma variedade de funes e procedimentos na unit SysUtils destinadas a tornar o trabalho com strings mais fcil. Procure por String-handling routines (Pascal-style) no sistema de help online do Delphi. Comprimento e Alocao Quando declaramos, uma AnsiString no tem comprimento e portanto nenhum espao alocado para caracteres na string. Para fazer com que espao seja alocado para a string, voc pode atribuir string um literal ou uma outra string, ou voc pode usar o procedimento SetLength(), como mostrado aqui: var S: string; // string inicialmente no tem tamanho begin // Aloca o mnimo de espao suficiente // para a string literal S := 'Doh!'; { ou } // adiciona a ref count da OutraString (assume que // OutraString j aponta para uma string vlida) S := OutraString { ou } // aloca espao suficiente para no mnimo // 4 caracteres.

16

SetLength(S, 4); end; Voc pode indexar os caracteres de uma AnsiString como um array, mas tenha cuidado de no indexar alm do tamanho da string. Por exemplo, o fragmento de cdigo seguinte causa um erro: var S: string; begin S[1] := 'a'; // No funcionar porque S no foi alocado! end; Este cdigo, contudo, funciona adequadamente: var S: string; begin SetLength(S, 1); // Agora S tem espao suficiente para guardar o caracter S[1] := 'a'; end; Win32 Compatibility Como mencionado inicialmente, AnsiStrings so sempre finalizadas com null, assim elas so compatveis com strings finalizadas com null. Isto possibilita chamar funes API Win32 ou outras funes que requerem strings do tipo PChar. Tudo que requerido que voc faa um typecast de string para PChar (typecasting explicado em mais detalhes na seo "Typecasting e Converso de Tipos"). O cdigo seguinte demonstra como chamar a funo Win32 GetWindowsDirectory(), que aceita um PChar e o tamanho do buffer como parmetros: var S: string; begin // importante! obtenha tamanho da string primeiro SetLength(S, 256); // chame a funo, S agora guarda a // string do diretrio GetWindowsDirectory(PChar(S), 256); end; Depois de usar uma AnsiString onde a funo ou procedimento espera um PChar, voc deve manualmente setar o comprimento da varivel string para seu tamanho terminado com null. A funo RealizeLength(), que tambm vem da unit STRUTILS, efetua essa tarefa: procedure RealizeLength(var S: string); begin SetLength(S, StrLen(PChar(S))); end; Chamar RealizeLength() completa a substituio de uma long string por um PChar:

17

var S: string; begin // importante! obtenha tamanho da string primeiro SetLength(S, 256); // chame a funo GetWindowsDirectory, // S agora guarda a string do diretrio GetWindowsDirectory(PChar(S), 256); // Altere o comprimento de S para o // comprimento com null RealizeLength(S); end;

O Tipo ShortString Se voc um veterano de Delphi, voc reconhecer o tipo ShortString como o tipo string do Delphi 1.0. ShortStrings so algumas vezes referidos como strings do Pascal. Para reiterar, lembre que o valor da diretiva $H determina se variveis declaradas com string so tratadas pelo compilador como AnsiString ou ShortString. Na memria, a string lembra um array de caracteres onde o caracter 0 na string contm o tamanho da string, e a string em si contm contida nos caracteres seguintes. O tamanho de armazenagem de uma ShortString default de no mximo de 256 bytes. Isto significa que voc nunca pode ter mais do que 255 caracteres em uma ShortString (255 characters + 1 tamanho em byte = 256). Assim como com AnsiStrings, trabalhar com ShortString muito fcil porque o compilador aloca strings temporrias quando necessrio, assim voc no tem que se preocupar em alocar buffers para resultados intermedirios. Uma varivel ShortString declarada e inicializada com a seguinte sintaxe: var S: ShortString; begin S := 'Bob o gato.'; end. Opcionalmente, voc pode alocar menos do que 256 bytes para uma ShortString usando apenas o identificador do tipo string e um tamanho especificador, como no exemplo seguinte: var S: string[45]; { uma ShortString de 45 caracteres } begin S := 'Esta string deve ser de 45 ou menos caracteres.'; end. O cdigo anterior faz com que uma ShortString seja criada apesar da configurao da diretiva $H. O comprimento mximo que voc pode especificar 255 caracteres.

18

Nunca armazene mais caracteres em uma ShortString do que a memria alocada para ela. Se voc declara uma varivel como uma string[8], por exemplo, e tenta atribuir 'uma_pequena_longa_string' varivel, a string ser truncada para apenas oito caracteres, e voc perder dados. Quando usar um array indexado para enderear um caracter particular em uma ShortString, voc pode obter falsos resultados ou invadir memria se voc tentar usar um indce que maior do que o tamanho declarado da ShortString. Por exemplo, suponha que voc declara uma varivel como a seguir: var Str: string[8]; Se voc ento tentar escrever o dcimo elemento da string como a seguir, voc provavelmente invadir memria usada por outras variveis: var Str: string[8]; i: Integer; begin i := 10; Str[i] := 's'; // invadir memria Voc pode ativar uma opo do compilador para verificar esses tipos de erros em tempo de execuo selecionando Range Checking na caixa de dilogo Options | Project . Dica: Embora incluir a checagem de limites em seu programa ajude-o a encontrar erros de strings, a checagem de limites atrasar levemente a performance de sua aplicao. prtica comun usar a checagem de limites durante o desenvolvimento e fases de depurao de seu programa, mas remover a checagem depois que voc estiver confiante na estabilidade de seu programa. Diferente de AnsiStrings, ShortStrings no so inerentemente compatveis com strings terminadas com null. Por causa disso, um pouco de trabalho necessrio para que seja possvel passar uma ShortString para uma funo API. Veja a funo seguinte, ShortStringAsPChar(): function ShortStringAsPChar(var S: ShortString): PChar; { Esta funo no finaliza com null uma string de modo que ela possa ser passada para funes } { que requerem tipos PChar. Se a string maior do que 254 chars, ento ela ser truncada para 254. } begin if Length(S) = High(S) then Dec(S[0]); { Trunca S se ela for muito longa } S[Ord(Length(S)) + 1] := #0; { Coloca null no fim da string } Result := @S[1]; { Retorna a string em em "PChar" } end; Cuidado: As funes e procedimentos na API requerem strings terminadas com null. No tente passar um tipo ShortString para uma funo API porque seu programa no compilar. Sua vida ser mais fcil se voc usar long strings quando trabalhar com a API.

19

O Tipo WideString O tipo WideString tipo similar a AnsiString; e ambos so alocados dinamicamente, possuem coleta de lixo e so compatveis em tamanho um com o outro. Contudo, WideString difere de AnsiString em trs aspectos: WideStrings so compreendidos de caracteres WideChar e no de caracteres AnsiChar, o que o torna compatvel com strings Unicode. WideStrings so alocados usando a funo API SysAllocStrLen(), o que o torna compatvel com strings BSTR de OLE. WideStrings no so contados por referncia, assim atribuir uma WideString outra requer que toda a string seja copiada de um local na memria para outro. Isto torna WideStrings menos eficiente do que AnsiStrings em termos de velocidade e memria usada. Como mencionado anteriormente, o compilador sabe automaticamente como fazer a converso entre variveis do tipo AnsiString e WideString como mostrado a seguir: var W: WideString; S: string; begin W := 'Uma String'; S := W; // Wide convertido para Ansi S := 'Outra String'; W := S; // Ansi convertido para Wide end; Para que o trabalho com WideStrings seja natural, Object Pascal sobrecarrega as rotinas Concat(), Copy(), Insert(), Length(), Pos(), e SetLength() e os operadores +, =, e <> para serem usados com WideStrings. Ento, o cdigo seguinte est sintaticamente correto: var W1, W2: WideString; P: Integer; begin W1 := 'Envolver'; W2 := 'campo'; if W1 <> W2 then P := Pos(W1, W2); end; Assim como nos tipos AnsiString e ShortString, voc pode usar colchetes de arrays para referenciar caracteres individuais de uma WideString: var W: WideString; C: WideChar; begin W := 'bano e Marfim vivem em perfeita harmonia'; C := W[Length(W)]; // C contm o ltimo caracter em W end;

20

Strings Terminadas com Null Inicialmente, este captulo mencionou que Delphi tem trs diferentes tipos de strings terminadas com null: PChar, PAnsiChar, e PWideChar. Como seus nomes indicam, cada um desses representa uma string terminada com um null de cada um dos trs tipos de caracteres do Delphi. Neste captulo, nos referimos a cada um desses tipos de strings genericamente como PChar. O tipo PChar no Delphi existe principalmente para manter a compatibilidade com Delphi 1.0 e com a API Win32, que faz uso extensivo de strings terminadas com null. Um PChar definido como um ponteiro para uma string seguida por um valor nulo (zero) (se voc no sabe exatamente o que um ponteiro, leia sobre; ponteiros so discutidos em maiores detalhes no fim desta seo). Diferentemente da memria para AnsiStrings e WideStrings, a memria para PChars no alocada automaticamente e gerenciada pelo Object Pascal. Ento, voc com freqncia necessitar alocar memria para a string para a qual ele aponta, usando uma das funes de alocao de memria do Object Pascal. O terico tamanho mximo de uma string PChar um pouco menos de 4 gigabytes. Dica: Como o tipo AnsiString do Object Pascal pode ser usado como um PChar na maioria das situaes, voc deve usar este tipo no lugar de PChars sempre que possvel. Como o gerenciamento de memrai para strings ocorre automaticamente, voc reduzir drasticamente a chance de ocorrerem bugs por invaso de memria em sua aplicao se, onde possvel, voc evitar PChars e a alocao de memra associada a eles. Usando PChars Como mencionado anteriormente, variveis PChar requerem que voc aloque e libere manualmente os buffers de memria que contm suas strings. Normalmente, voc aloca memria para um buffer de PChar usando a funo StrAlloc(), mas muitas outras funes podem ser usadas para alocar memria para PChars, incluindo AllocMem(), GetMem(), StrNew(), ou at mesmo a funo da API VirtualAlloc(). Funes correspondentes tambm existem para muitas dessas funes, as quais devem ser usadas para desalocar a memria. A Tabela 2.6 lista vrias funes de alocao e suas funes de desalocao correspondentes. Tabela 2.6. Funes de alocao e desalocao de memra Memria Alocada com ... AllocMem() GlobalAlloc() GetMem() New() StrAlloc() StrNew() VirtualAlloc() Deve Ser Liberada com... FreeMem() GlobalFree() FreeMem() Dispose() StrDispose() StrDispose() VirtualFree()

O exemplo seguinte demonstra tcnicas de alocao de memria quando se trabalha com PChars e strings: var P1, P2: PChar; S1, S2: string; begin // P1 aponta para uma alocao de 63 Chars

21

P1 := StrAlloc(64 * SizeOf(Char)); // Copia a string literal string para P1 StrPCopy(P1, 'Delphi 4 '); // Pe algum texto na string S1 S1 := 'Developer'; // P1 aponta para uma cpia de S1 P2 := StrNew(PChar(S1)); // concatena P1 e P2 StrCat(P1, P2); // S2 agora contm 'Delphi 4 Developer' S2 := P1; // limpa os buffers P1 e P2 StrDispose(P1); StrDispose(P2); end. Note, antes de tudo, o uso de SizeOf(Char) com StrAlloc() quando aloca-se memria para P1. Lembre que o tamanho de um Char pode mudar de um byte para dois nas futuras verses de Delphi, assim voc no pode assumir o valor de Char para sempre como sendo de um byte. SizeOf() garante que a alocao funcionar adequadamente no importando quantos bytes um caracter ocupe. StrCat() usado para concatenar duas strings de PChar. Note aqui que voc no pode usar + para concatenar da mesma forma que voc pode com long strings e ShortStrings. A funo StrNew() usada para copiar o valor contido na string S1 para dentro do PChar P2. Tenha cuidado quando usar esta funo. comun erros de sobrescrita de memra qando usase StrNew() porque ele aloca apenas a memria suficiente para guardar a string. Considere o seguinte exemplo: var P1, P2: Pchar; begin // Aloca apenas a memria suficiente para P1 e P2 P1 := StrNew('Hello '); P2 := StrNew('World'); // TENHA CUIDADO! Corrupo de memria. StrCat(P1, P2); . . . end;

22

Dica: Assim como com outros tipos de strings, Object Pascal fornece uma biblioteca decente de funes teis e procedimentos para operar sobre PChars. Procure por "String-handling routines (null-terminated)" no help online do Delphi. Tipos Variant Delphi 2.0 introduziu um poderoso tipo de dado chamado Variant. Variants foram produzidos primeiramente a fim de suportar Automao OLE, que usa o tipo Variant pesadamente. De fato, o tipo de dado Variant do Delphi uma encapsulao do variant usado com OLE. A implementao de Delphi de variants tem tambm a vantagem de ser til em outras reas de programao Delphi, como voc aprender. Object Pascal a nica linguagem compilada que integra completamente variantes como um tipo de dado dinmico em tempo de execuo, e como um tipo esttico em tempo de compilao, com isso o compilador sempre sabe o que um variant.. Delphi 3 introduziu um novo tipo chamado OleVariant, o qual idntico a Variant exceto que ele pode apenas conter tipos compatveis com Automao. Nesta seo inicialmente enfocaremos sobre o tipo Variant, e ento discutiremos OleVariant e contrastaremos ele com Variant. Variants Mudam de Tipos Dinamicamente Um dos principais propsitos de varitants ter uma varivel cujo tipo de dado base no possa ser determinado em tempo de compilao. Isto significa que um variant pode mudar o tipo para o qual ele referir em tempo de execuo. Por exemplo, o cdigo seguinte compilar e rodar adequadamente: var V: Variant; begin V := 'Delphi 4 is Great!'; // Variant contm uma string V := 1; // Variant agora contm um Integer V := 123.34; // Variant agora contm um ponto flutuante V := True; // Variant agora contm um booleano V := CreateOleObject('Word.Basic'); // Variant agora contm um objeto OLE end; Variants podem suportar todos os tipos de dados simples, tais como Inteiros, valores de ponto-flutuante, strings, Booleanos, tempo e data, currency e tambm objetos de Automao OLE. Note que variants no podem referir-se a Objetos do Object Pascal. Variants tambm podem referir-se a um array no homogneo, que podem variar em tamanho e cujos elementos

23

de dados podem referir a qualquer um dos tipos de dados anteriores incluindo um outro array variant.array. A Estrutura de Variant A estrutura de dados que define o tipo Variant definida na unit System e tambm mostrada na listagem seguinte: type TVarData = record VType: Word; Reserved1, Reserved2, Reserved3: Word; case Integer of varSmallint: (VSmallint: Smallint); varInteger: (VInteger: Integer); varSingle: (VSingle: Single); varDouble: (VDouble: Double); varCurrency: (VCurrency: Currency); varDate: (VDate: Double); varOleStr: (VOleStr: PWideChar); varDispatch: (VDispatch: Pointer); varError: (VError: Integer); varBoolean: (VBoolean: WordBool); varUnknown: (VUnknown: Pointer); varByte: (VByte: Byte); varString: (VString: Pointer); varArray: (VArray: PVarArray); varByRef: (VPointer: Pointer); end; A estrutura TVarData consome 16 bytes de memria. Os primeiros dois bytes da estrutura de TVarData contm um valor word que representa o tipo de dado ao qual o variante se refere. A listagem seguinte mostra as vrios valores que podem aparecer no campo VType do registro TVarData. Os prximo 6 bytes no so usados. Os 8 bytes restantes contm o dado atual ou um ponteiro para o dado representado pelo variant. { Cdigos dos tipos do Variant } const varEmpty = $0000; varNull = $0001; varSmallint = $0002; varInteger = $0003; varSingle = $0004; varDouble = $0005; varCurrency = $0006; varDate = $0007; varOleStr = $0008; varDispatch = $0009; varError = $000A; varBoolean = $000B; varVariant = $000C; varUnknown = $000D; varByte = $0011; varString = $0100;

24

varTypeMask = $0FFF; varArray = $2000; varByRef = $4000; Nota: Como voc pode notar dos cdigos dos tipos na listagem anterior, um Variant no pode conter uma referncia a um tipo Pointer ou class. Voc notar da listagem de TVarData que o registro TVarData na verdade um registro variant. No confunda isto com o tipo Variant. Embora o registro variant e o tipo variant tenham nomes similares, eles representam duas idias completamente diferentes. Registros variants permitem que multiplos campos de dados existam em uma mesma posio de memria como uma union em C/C++. Isto discutido em maiores detalhes na seo "Registros" deste texto. A sentena case no registro variant TVarData indica que o tipo de dado ao qual o variant refere. Por exemplo, se o campo VType contm o valor varInteger, apenas 4 bytes dos 8 bytes de dados na poro variant do registro so usados para guardar um valor inteiro. Da mesma forma, se VType tem o valor varByte, apenas 1 byte dos 8 so usados para guardar o valor do byte. Voc notar que se VType contm o valor varString, os 8 bytes de dados na verdade no guardaro a string, mas sim, eles guardaro um apontador para esta string. Este um ponto importante porque voc pode acessar campos de um variant diretamente, como mostrado a seguir: var V: Variant; begin TVarData(V).VType := varInteger; TVarData(V).VInteger := 2; end; Voc deve entender que em alguns casos esta uma prtica perigosa porque possvel perder a referncia para uma string ou outra entidade dinmica, o que resultar em sua aplicao perda da memria ou outros recursos. Variants So Dinmicos Delphi automaticamente manipula a alocao e desalocao de memria requerida por um tipo variant. Por exemplo, examine o cdigo seguinte, o qual atribui uma string a uma varivel variant: procedure ShowVariant(S: string); var V: Variant begin V := S; ShowMessage(V); end; Como discutimos anteriormente neste texto, muitas coisas permanecem aqui e podem no ser aparentes. Delphi primeiro inicializa o variant para um valor no atribudo. Durante a atribuio, ele seta seu campo VType par varString e copia o apontador da string para dentro de seu campo VString. Ele ento incrementa o contador de referncia da string S. Quando o variant sai do escopo, isto , o procedimento acaba e retorna para o cdigo que o chamou, o

25

variant limpo, e o contador de referncia da string S decrementado. Delphi faz isso implicitamente inserindo um bloco try..finally no procedimento como mostrado a seguir: procedure ShowVariant(S: string); var V: Variant begin V := No-Atribuido; // inicialize variant para vazio try V := S; ShowMessage(V); finally // Agora limpe os recursos associados com o variant end; end; Esta mesma liberao implcita de recursos ocorre quando voc atribui um tipo de dado diferente ao variant. Por exemplo, examine o cdigo seguinte: procedure ChangeVariant(S: string); var V: Variant begin V := S; V := 34; end; Este cdigo reduz-se ao seguinte pseudo-cdigo: procedure ChangeVariant(S: string); var V: Variant begin // Limpe o Variant V, assegurando que ele inicializado com "vazio" try V.VType := varString; V.VString := S; Inc(S.RefCount); // Limpe o Variant V, atravz disso libere a referncia a string; V.VType := varInteger; V.VInteger := 34; finally // Limpe os recursos associados com o variant end; end; Se voc entendeu o que aconteceu na ilustrao anterior, voc ver porque no recomendado que voc manipule campos do registro TVarData diretamente, como mostrado a seguir: procedure ChangeVariant(S: string); var V: Variant begin V := S; TVarData(V).VType := varInteger;

26

TVarData(V).VInteger := 32; V := 34; end; Embora isto possa parecer seguro, no porque ele resulta no fracasso de decrementar o contador de referncia da string S, provavelmente resultando na perda da referncia. Como uma regra geral, no acesse os campos de TVarData diretamente, ou se voc precisar, tenha certeza absoluta de que voc sabe exatamente o que est fazendo. Typecasting Variants Voc pode explicitamente alterar o tipo de expresses para o tipo Variant. Por exemplo, a expresso seguinte: Variant(X) resulta em um tipo variant cujo cdigo do tipo corresponde ao resultado da expresso X, a qual deve ser do tipo inteiro, real, currency, string, caracter, ou Booleano. Voc pode fazer o typecast de um variant para um tipo de dado simples. Por exemplo, dada a seguinte sentena V := 1.6; onde V uma varivel do tipo Variant, as seguintes expresses tero os resultdos mostrados: S := string(V); // S conter a string '1.6'; // I est arredondando para o valor Inteiro mais prximo, no caso: 2 I := Integer(V); B := Boolean(V); // B contm False se V contm 0, do contrrio B nbsp; nbsp; // True D := Double(V); // D contm o valor 1.6 Esses resultados so ditados pelas regras de converso de tipos aplicveis a Variants. A propsito, no necessrio fazer o typecast do variant para um outro tipo de dado para fazer a atribuio. O cdigo seguinte trabalhar melhor: V := 1.6; S := V; I := V; B := V; D := V; O que acontece aqui que as converses para os tipos de dado alvo so feitas atravs de um typecast implcito. Contudo, como essas converses so feitas em tempo de execuo, h muito mais cdigo lgico atachado a este mtodo. Se voc est certo do tipo que um variant contm, melhor voc fazer o typecast para o outro tipo a fim de aumentar a velocidade da operao. Isto especialmente verdadeiro se o variant est sendo usado em uma expresso, o que discutiremos a seguir. Variants em Expresses Voc pode usar variants em expresses com os seguintes operadores: +, =, *, /, div, mod, shl, shr, and, or, xor, not, :=, <>, <, >, <=, >=. Quando variants so usados em expresses, Delphi sabe como efetuar as operaes baseado no contedo do variant. Por exemplo, se dois variants V1 e V2 contm inteiros, a expresso

27

V1 + V2 resulta na adio dos dois inteiros. Contudo, se V1 e V2 contm strings, o resultado uma concatenao das duas strings. O que acontece se V1 e V2 contm dois tipos de dados diferentes? Delphi usa certas regras de converso a fim efetuar a operao. Por exemplo, se V1 contm a string '4.5' e V2 contm um nmero de ponto-flutuante, V1 ser convertido para um ponto flutuante e ento adicionado a V2. O cdigo seguinte ilustra isto: var V1, V2, V3: Variant; begin V1 := '100'; // Um tipo string V2 := '50'; // Um tipo string V3 := 200; // Um tipo Integer V1 := V1 + V2 + V3; end; Baseado no que acabamos de mencionar sobre regras de converso, parece a primeira vista que o cdigo anterior resultaria no valor 350 como um inteiro. Contudo, se voc der uma boa olhada, ver que este no o caso. Porque a ordem de precedncia da esquerda para a direita, a primeira equao que foi executada V1 + V2. Como esses dois variants referem-se a strings, uma concatenao de string efetuada resultando na string '10050'. O que resulta ento a adio do valor inteiro armazenado na variant V3. Como V3 um inteiro, o resultado '10050' convertido para um inteiro e somado a V3, dando um resultado final de 10250. Delphi converte os variants para o tipo mais elevado na equao a fim de cumprir com maior sucesso o clculo. Contudo, quando uma operao tentada sobre dois variants dos quais o Delphi no pode diferenciar, uma exceo de "converso de tipo variant invlido" causada. O cdigo seguinte ilustra isto: var V1, V2: Variant; begin V1 := 77; V2 := 'hello'; V1 := V1 / V2; // Gera uma exceo end; Como declarado anteriormente, algumas vezes uma boa idia explicitar o typecast do variant para um tipo de dado especfico se voc souber qual o tipo e se ele usado em uma expresso. Considere a seguinte linha de cdigo: V4 := V1 * V2 / V3; Antes que um resultado possa ser gerado por esta equao, cada operao manipulada por uma funo em tempo de execuo que passa por muitos testes para determinar a compatibilidade dos tipos de variants representados. Ento as converses so feitas para os tipos de dados apropriados. Isto resulta em uma grande quantidade de cdigo e overhead. Uma soluo melhor obviamente no usar variants. Entretanto, quando necessrio, voc pode explicitamente converter os variants de modo que os tipos de dados sejam resolvidos em tempo de compilao: V4 := Integer(V1) * Double(V2) / Integer(V3); Lembre que isto assume que voc conhece os tipos de dados que os variants representam. Vazio e Null

28

Dois valores especiais de VType para variants merecem uma breve discusso. O primeiro varEmpty, que significa que ainda no foi atribudo nenhum valor ao variant. Este o valor inicial do variant setado pelo compilador quando ele est envolvido no escopo. O outro varNull, que diferente de varEmpty porque ele representa realmente o valor Null em oposio a ausncia de valor. Esta distino entre nenhum valor e o valor Null especialmente importante quando aplicada a valores de campos de uma tabela de banco de dados. Uma outra diferena que ao tentar efetuar qualquer equao com um variant contendo um valor varEmpty VType resultar em uma exceo de invalid variant operation. O mesmo no verdade para variants contendo um valor varNull , entretanto. Quando um variant envolvido em uma equao contm um valor Null, aquele valor propagar par ao resultado. Ento o resultado de qualquer equao contendo um Null sempre Null. Se voc quiser atribuir ou comparar um variant quanto a um desses dois valores, a unit System define dois variants, Unassigned e Null, que tem os valore VType de varEmpty e varNull, respectivamente. Cuidado: Talvez seja tentador usar variants em vez de tipos de dados convencionais porque eles parecem oferecer uma maior flexibilidade. Entretanto, isto aumentar o tamanho do seu cdigo e far com que suas aplicaes executem mais lentamente. Alm disso, tornar a manuteno do seu cdigo mais difcil. Variants so teis em muitas situaes. De fato, a prpria VCL usa variants em muitos lugares, mais notavelmente em ActiveX e reas de banco de dados, por causa da flexibilidade dos tipos de dados que eles oferecem. Geralmente, entretanto, voc deve usar os tipos de dados convencionais em vez de variants. Apenas em situaes onde a flexibilidade dos variants pesar mais do que a performance dos mtodos convencionais voc deve lanar mo do uso de variants. Tipos de dados ambguos geram bugs de ambiguidade. Arrays de Variant Anteriormente mencionameos que um variant pode referir a um array no-homogneo. Ento a sintaxe seguinte vlida: var V: Variant; I, J: Integer; begin I := V[J]; end; Tenha em considerao que, embora o cdigo anterior compile, voc obter uma exceo em tempo de execuo porque V no contm ainda um array de variant. Object Pascal fornece vrios arrays de variant que suportam funes que permitem a voc criar um array de variant. Essas so VarArrayCreate() e VarArrayOf(). VarArrayCreate() VarArrayCreate() definida na unit System como function VarArrayCreate(const Bounds: array of Integer; VarType: Integer): Variant; Para usar VarArrayCreate(), voc passa os limites do array que voc quer criar e um cdigo do tipo variant para o tipo dos elementos do array (o primeiro parmetro um array aberto, o

29

qual discutido na ltima seo "Passando Parmetros" neste captulo). Por exemplo, o seguinte retorna um variant array de inteiros e atribui valores aos itens do array: var V: Variant; begin V := VarArrayCreate([1, 4], varInteger); // Cria um array de 4 elementos V[1] := 1; V[2] := 2; V[3] := 3; V[4] := 4; end; Se arrays de variant de um tipo simples no so confusos o suficiente, voc passa varVariant como o cdigo do tipo a fim de criar um array de variants de variants! Desta forma, cada elemneto no array tem a habilidade de conter um tipo diferente de dado. Voc pode tambm criar um array multidimensional passando os limites adicionais requeridos. Por exemplo, o cdigo seguinte cria um array com limites [1..4, 1..5]; V := VarArrayCreate([1, 4, 1, 5], varInteger); VarArrayOf() A funo VarArrayOf() definida na unit System como function VarArrayOf(const Values: array of Variant): Variant; Esta funo retorna um array unidimensional cujos elementos so dados no parmetro Values. O exemplo seguinte cria um array variant de trs elementos com um inteiro, uma string e um valor de ponto-flutuante: V := VarArrayOf([1, 'Delphi', 2.2]); Funes e Procedimentos de Suporte de Array de Variant Alm de VarArrayCreate() e VarArrayOf(), h vrias funes de suporte de array de variant. Essas funes so definidas na unit System e so tambm mostradas a seguir. procedure VarArrayRedim(var A: Variant; HighBound: Integer); function VarArrayDimCount(const A: Variant): Integer; function VarArrayLowBound(const A: Variant; Dim: Integer): Integer; function VarArrayHighBound(const A: Variant; Dim: Integer): Integer; function VarArrayLock(const A: Variant): Pointer; procedure VarArrayUnlock(const A: Variant); function VarArrayRef(const A: Variant): Variant; function VarIsArray(const A: Variant): Boolean; A funo VarArrayRedim() permite que voc redimensione o limite superior da dimenso mais a direita de um array de variant. A funo VarArrayDimCount() retorna o nmero de dimenses em um array de variant. VarArrayLowBound() e VarArrayHighBound() retornam os limites inferior e superior de um array, respectivamente. VarArrayLock() e VarArrayUnlock() so duas funes especiais, que so descritas em detalhes na prxima seo. VarArrayRef() destinada a trabalhar em volta de um problema que exista ao passar arrays de variant para servidores de automao OLE. O problema ocorre quando voc passa um variant contendo um array de variant para um mtodo de automao, como este: Server.PassVariantArray(VA); O array passado no como um array de variant, mas sim como um variant contendo um array de variantuma distino importante. Se o servidor expera um array de variant ao

30

contrrio de uma referncia para um, o servidor provalvelmente encontrar uma condio de erro quando voc chamar o mtodo com a sintaxe anterior. VarArrayRef() cuida desta situao modelando o variant para o tipo e valor experados pelo servidor. Esta sintaxe usando VarArrayRef() Server.PassVariantArray(VarArrayRef(VA)); VarIsArray() uma checagem booleana simples, que retorna True se o parmetro do variant passado para ele um array de variant ou False caso contrrio. Inicializando um Large ArrayVarArrayLock(), VarArrayUnlock() Arrays de variant so importantes em Automao OLE, uma vez que eles fornecem os nicos meiso de passar dados binrios 'crus' para um servidor de automao OLE (porque ponteiros no so um tipo legal em automao OLE). Entretanto, se usados incorretamente, arrays de variant podem se tornar um recurso ineficiente de troca de dados. Considere a linha seguinte: V := VarArrayCreate([1, 10000], VarByte); Esta linha cria um array de variant de 10.000 bytes. Suponha que voc tenha um outro array (no-variant) declarado do mesmo tamanho e que voc queira copiar o contedo deste array no-variant apr ao array de variant. Normalmente, voc pode apenas fazer isso com um loop atravz dos elementos e atribu-los aos elementos do array de variant, como mostrado a seguir: begin V := VarArrayCreate([1, 10000], VarByte); for i := 1 to 10000 do V[i] := A[i]; end; O problema com este cdigo que ele est condenado apenas pelo custo requerido para inicializar os elementos do array de variants. Isto por causa das atribuies aos elementos do array tem que passar pela lgica em tempo de execuo para determinar a compatibilidade de tipos, localizao de cada elemento, e assim por diante. Para evitar esses controles em tempo de execuo, voc pode usar a funo VarArrayLock() e o procedimento VarArrayUnlock(). VarArrayLock() trava o array na memria de forma que ele no possa ser movido ou redimensionado enquanto estiver travado, e retorna um ponteiro para os dados do array. VarArrayUnlock() destrava um array travado com VarArrayLock() e permite de novo que o array de variant seja redimensionado e movido na memria. Depois que o array travado, voc pode empregar um meio mais eficiente para inicializar os dados usando, por exemplo, a procedimento Move() com o ponteiro para os dados do array. O cdigo seguinte efetua a inicializao do array de variats mostrado anteriormente, mas de uma manira muito mais eficiente: begin V := VarArrayCreate([1, 10000], VarByte); P := VarArrayLock(V); try Move(A, P^, 10000); finally VarArrayUnlock(V); end; end;

31

Funes de Suporte Existem muitas outras funes de suporte para variants que voc pode usar. Essas funes so declaradas na unit System e esto listadas a seguir: procedure VarClear(var V: Variant); procedure VarCopy(var Dest: Variant; const Source: Variant); procedure VarCast(var Dest: Variant; const Source: Variant; VarType:Integer); function VarType(const V: Variant): Integer; function VarAsType(const V: Variant; VarType: Integer): Variant; function VarIsEmpty(const V: Variant): Boolean; function VarIsNull(const V: Variant): Boolean; function VarToStr(const V: Variant): string; function VarFromDateTime(DateTime: TDateTime): Variant; function VarToDateTime(const V: Variant): TDateTime; O procedimento VarClear()limpa um variant e seta o campo VType para varEmpty. VarCopy()copia o variant Source para o variant Dest. O procedimento VarCast() converte um variant para um tipo especificado e armazena aquele resultado em outro variant. VarType() retorna um dos cdigos de tipo varXXX para um variant especificado. VarAsType() tem a mesma finalidade que VarCast(). VarIsEmpty() retorna True se o cdigo do tipo de um variant especificado varEmpty. VarIsNull() indica se um variant contm um valor Null ou no. VarToStr() converte um variant para sua representao em string (uma string vazia no caso de um variant Null ou vazio). VarFromDateTime() retorna um variant que contm um valor TDateTime dado. VarToDateTime() retorna o valor TDateTime contido em um variant. OleVariant O tipo OleVariant primeiramente idntico ao tipo Variant descrito por toda esta seo. A nica diferena entre OleVariant e Variant que OleVariant suporta apenas tipos compatveis com Automao. Atualmente o tico VType suportado que no compatvel com Automao varString, o cdigo para AnsiString. Quando se tenta atribuir uma AnsiString a um OleVariant, a AnsiString ser automaticamente convertida para um OLE BSTR e armazenada no variant como um varOleStr. Currency Delphi 2.0 introduziu um novo tipo chamado Currency, que ideal para clculos financeiros. Diferentemente de nmeros de ponto-flutuante que permitem o ponto decimal para o "flutuar" dentro de um nmero, Currency um tipo decimal de ponto fixado que codificado para uma preciso de 15 dgitos antes do decimal e 4 dgitos depois do decimal. Como tal, ele no suscetvel a erros de arredondamento como so os tipos de pontos-flutuante. Quando atualizar seus projetos do Delphi 1.0, uma boa idia usar este tipo no lugar de Single, Real, Double, ou Extended onde dinheiro estiver envolvido. Tipos Definidos Pelo Usurio Inteiros, strings, e nmeros de ponto flutuante freqentemente no so suficientes para representar adequadamente variveis nos problemas do mundo real que os programadores devem tentar resolver. Em casos como esses, voc deve criar seus prprios tipos para melhor representar variveis no problema corrente. Em Pascal, esses tipos definidos pelo usurio

32

usualmente vm na forma de registros ou objetos; voc declara esses tipos utilizando a palavra reservada Type Arrays Object Pascal permite que voc crie arrays de qualquer tipo de varivel (exceto files). Por exemplo, uma varivel declarada como um array de oito inteiros var A: Array[0..7] of Integer; Esta instruo equivalente a seguinte declarao em C: int A[8]; E tambm equivalente a este segmento em Visual Basic: Dim A(8) as Integer Arrays em Object Pascal tem uma propriedade especial que diferencia-os de outras linguagens: Eles no comeam em um determinado nmero. Voc ento pode declarar um array de trs elementos que comea em 28, como o exemplo seguinte: var A: Array[28..30] of Integer; Como arrays em Object Pascal no necessariamente comeam em zero ou um, voc deve ter algum cuidado quando interagir com os elementos do array em um loop for. O compilador fornece funes embutidas chamadas High() e Low(), que retornam os limites inferiores e superiores de uma varivel ou tipo array. Seu cdigo estar menos propenso ao erro e mais fcil de manter se voc usar essas funes para controlar seu loop for como mostrado a seguir: var A: array[28..30] of Integer; i: Integer; begin for i := Low(A) to High(A) do // sem cdigo complicado para o loop A[i] := i; end; Dica: Sempre comece arrays de caracteres no 0. Arrays de caracteres com zero na base podem ser passados para funes que requerem variveis do tipo PChar. Para especificar dimenses mltiplas, use uma lista de limites delimitada por vrgula: var // Arrays de Integer de duas dimenses: A: array[1..2, 1..2] of Integer; Para acessar um array multidimensional, use vrgulas para separar cada dimenso dentro de dois colchetes:

33

I := A[1, 2]; Arrays Dinmicos Uma novidade no Delphi 4 so os arrays dinmicos. Arrays Dinmicos so arrays alocados dinamicamente em dimenses que no so conhecidas em tempo de compilao. Para declarar um array dinmico, apenas declare um array sem incluir as dimenses: var // array dinmico de string: SA: array of string; Antes de poder usar um array dinmico, voc deve usar o procedimento SetLength() para alocar memria para o array: begin // aloca lugar para 33 elementos: SetLength(SA, 33); Depois que a memria foi alocada, voc pode acessar os elementos do array dinmico como um array normal: SA[0] := 'O urso parece feliz'; OutraString := SA[0]; Nota: Arrays Dinmicos sempre iniciam no zero. Arrays dinmicos so auto-suficientes, assim no necessrio liber-los quando voc deixar de us-los; eles sero liberados quando deixarem o escopo. Entretanto, pode haver uma hora que voc queira remover o array dinmico da memria antes que ele saia do escopo (se ele usa muita memria, por exemplo). Para fazer isso, voc apenas precisa atribuir ao array dinmico um nil: SA := nil; // libera SA Arrays dinmicos so manipulados utilizando semanticas de referncia similares AnsiStrings. Um rpido testequal o valor de A1[0] no fim do seguinte fragmento de cdigo: var A1, A2: array of Integer; begin SetLength(A1, 4); A2 := A1; A1[0] := 1; A2[0] := 26; A resposta correta 26. Isto porque a atribuio A2 := A1 no cria um novo array mas ao invz disso fornece a A2 uma referncia ao mesmo array A1. Ento, qualquer modificao em A2 tambm afetar A1. Se voc quiser fazer uma cpia completa de A1 em A2, use o procedimento padro Copy():

34

A2 := Copy(A1); Depois que a linha de cdigo acima for executada, A2 e A1 sero dois arrays separados inicialmente contendo os mesmos dados. Mudanas em um no afetar o outro. Voc pode opcionalmente especificar o elemento inicial e o nmero de elementos a serem copiados como parmetros para Copy() como mostrado a seguir: // copia 2 elementos, comeando no elemento 1: A2 := Copy(A1, 1, 2); Arrays dinmicos podem tambm ser multidimensionais. Para especificar mltiplas dimenses, adicione um array of adicional a declarao para cada dimenso: var // array dinmico de Integer de duas dimenses: IA: array of array of Integer; Para alocar memria para um array dinmico multidimensional, passe os tamanhos das outras dimenses como parmetros adicionais para SetLength(): begin // IA ser um array de Integer 5 x 5 SetLength(IA, 5, 5); Acesse arrays dinmicos multidimensionais da mesma forma que voc acessa arrays normais multidimensionaiscada elemento separado por uma vrgula entre colchetes: IA[0,3] := 28; Registros Uma estrutura definida pelo usurio referida como um record em Object Pascal, e equivalente ao struct do C ou Type do Visual Basic. Como um exemplo, aqui est uma definio de registro em Pascal e as definies equivalentes em C e Visual Basic: { Pascal } Type MyRec = record i: Integer; d: Double; end; /* C */ typedef struct { int i; double d; } MyRec; /* Visual Basic */ Type MyRec i As Integer d As Double End Type

35

Quando se trabalha com registros, voc usa um ponto para acessar seus campos, por exemplo: var N: MyRec; begin N.i := 23; N.d := 3.4; end; Object Pascal tambm suporta registros de variants, que permitem diferentes espcies de dados sobre a mesma poro de memria no registro. No confunda com o tipo de dados Variant, registros de variant permitem que cada campo de dado sobrepostos seja acessado independentemente. Se voc conhece C/C++, reconhecer registros de variants como sendo o mesmo conceito de unions dentro de structs do C. O cdigo seguinte mostra um registro de variant no qual um Double, um Integer e um char ocupam o mesmo espao de memria: type TVariantRecord = record NullStrField: PChar; IntField: Integer; case Integer of 0: (D: Double); 1: (I: Integer); 2: (C: char); end; Nota: As regras do Object Pascal estabelecem que a poro variant de um registro no podem ser do tipo autosuficiente. Aqui est o equivalente em C++ da declarao de tipo precedente: struct TUnionStruct { char * StrField; int IntField; union { double D; int i; char c; }; }; Conjuntos (Sets) Sets so o nico tipo do Pascal que no tem um equivalente em Basic, C ou C++ (embora o Borland C++Builder implemente uma classe template chamada Set, que emula o comportamento de um set do Pascal). Sets fornecem um meio muito eficiente de representar uma coleo de valores ordinais, caracteres ou enumerados. Voc pode declarar um novo tipo set utilizando a palavra-reservada set of seguida por um tipo ordinal ou um subconjunto de possveis valores, por exemplo:

36

type // membros possveis: #0 - #255 TCharSet = set of char; TEnum = (Segunda, Tera, Quarta, Quinta, Sexta); // pode conter qualquer combinao dos membros de TEnum TEnumSet = set of TEnum; // membros possveis: 1 - 10 TSubrangeSet = set of 1..10; // membros possveis: 'A' - 'z' TAlphaSet = set of 'A'..'z'; Note que um conjunto pode apenas conter at 256 elementos. Alm disso, apenas tipos ordinais podem seguir as palavras-reservadas set of. Dessa forma, as declaraes seguintes so ilegais: type TIntSet = set of Integer; // Invalido: muitos elementos TStrSet = set of string; // Invalido: no um tipo ordinal Sets armazenam seus elementos internalmente como bits individuais. Isto os torna muito eficientes em termos de velocidade e memria utilizada. Sets com menos do que 32 elementos no tipo base podem ser armazenados e operados em registradores da CPU, para uma maior eficincia. Sets com 32 ou mais elementos so armazenados na memria. Para obter mxima performance dos sets, mantenha o nmero de elementos no tipo base do set a baixo de 32. Utilizando Sets Use colchetes quando referir aos elementos do set. O cdigo seguinte demonstra como declarar variveis do tipo set e atribuir a elas valores: type TCharSet = set of char; // membros possveis: #0 - #255 TEnum = (Segunda, Tera, Quarta, Quinta, Sexta, Sabado, Domingo); // pode conter qualquer combinao dos membros de TEnum TEnumSet = set of TEnum; var CharSet: TCharSet; EnumSet: TEnumSet; SubrangeSet: set of 1..10; // membros possveis: 1 - 10 AlphaSet: set of 'A'..'z'; // membros possveis: 'A' - 'z' begin CharSet := ['A'..'J', 'a', 'm']; EnumSet := [Sabado, Domingo];

37

SubrangeSet := [1, 2, 4..6]; AlphaSet := []; // Vazio; nenhum elemento end; Operadores de Conjuntos Object Pascal fornece muitos operadores para usar na manipulao de sets. Voc pode usar esses operadores para determinar interseco, unio, diferena e membros do conjunto. Membros Use o operador in para determinar se um dado elemento est contido em um set particular. Por exemplo, o cdigo seguinge ser usado para determinar se o conjunto CharSet mencionado anteriormente contm a letra 'S': if 'S' in CharSet then // faz algo; O cdigo seguinte determina se EnumSet no contm o membro Segunda: if not (Segunda in EnumSet) then // faz algo; Uno e Diferena Use os operadores + e - ou os procedimentos Include() e Exclude(), para adicionar e remover elementos para e de uma varivel set: Include(CharSet, 'a'); // adiciona 'a' ao conjunto CharSet := CharSet + ['b']; // adiciona 'b' ao conjunto Exclude(CharSet, 'x'); // remove 'z' do conjunto CharSet := CharSet - ['y', 'z']; // remove 'y' e 'z' do conjunto Dica: Quando possvel, use Include() e Exclude() para adicionar e remover um simples elemento de um conjunto ao invs dos operadores + e -. Include() e Exclude() constituem apenas 1 instruo de mquina cada um, enquanto que os operadores + e - requerem 13 + 6n instrues (onde n o tamanho em bits do conjunto). Interseo Use o operador * para calcular a interseo de dois sets. O resultado da expresso Set1 * Set2 um conjunto contendo todos os membros que Set1 e Set2 tem em comum. Por exemplo, o cdigo a seguir poderia ser usado como um meio eficiente para determinar se um dado conjunto contm elementos mltiplos: if ['a', 'b', 'c'] * CharSet = ['a', 'b', 'c'] then // faz algo Objetos

38

Pense em objetos como registros que tambm contm funes e procedimentos. O modelo de objetos do Delphi discutido em maiores detalhes na seo "Usando Objetos do Delphi", assim esta seo cobrir apenas a sintaxe bsica dos objetos em Object Pascal. Um objeto definido como segue: Type TObjetoFilho = class(TObjetoPai); AlgumaVar: Integer; procedure AlgumProc; end; Embora objetos no Delphi no sejam idnticos aos objetos em C++, esta declarao , grosso modo, equivalente a seguinte declarao em C++: class TObjetoFilho : public TObjetoPai { int AlgumaVar; void AlgumProc(); }; Mtodos so definidos da mesma maneira que funes e procedimentos normais (que so discutidos na seo "Procedimentos e Funes"), com a adio do nome do objeto e o operador ponto: procedure TObjetoFilho.AlgumProc; begin { o cdigo do procedimento vem aqui } end; O smbolo . do Object Pascal similar em funcionalidade ao operador . do Visual Basic e o operador :: do C++. Voc deve notar que, embora todas as trs linguagens permitam o uso de classes, apenas Object Pascal e C++ permitem a criao de novas classes que comportem-se de uma maneira completamente orientada a objetos, a qual descreveremos na seo "Programao Orientada a Objetos." Nota: Objetos em Object Pascal no so colocados na memria da mesma forma que os objetos em C++, assim no possvel usar objetos de C++ diretamente do Delphi ou vice-versa. Entretanto, existem tcnicas que associam objetos de C++ e Delphi. Uma exceo a isto a capacidade do Borland C++Builder de criar classes que mapeiam diretamente para classes de Object Pascal usando a diretiva proprietria __declspec(delphiclass). Tais objetos so igualmente incompatveis com objetos normais de C++. Apontadores Um apontador uma varivel que contm uma posio de memria. Voc j viu um exemplo de um apontador no tipo PChar anteriormente neste tutorial. Tipo genrico de apontador do Pascal chamado Pointer. Um Pointer chamado algumas vezes de um apontador sem tipo porque ele contm apenas um endereo de memria e o compilador no mantm qualquer informao sobre o dado para o qual ele aponta.

39

Nota: Apontadores so um tpico um tanto avanado, e voc definitivamente no necessita dominlos para escrever uma aplicao em Delphi. A medida que voc se tornar mais experiente, apontadores se tornaro uma ferramenta valiosa para sua "caixa de ferramentas" de programao. Apontadores de determinado tipo (apontadores tipados) so declarados usando o operador ^, na seo Type do seu programa. Apontadores tipados ajudam o compilador a ficar de olho exatamente para qual tipo de dado um apontador particular aponta, permitindo que o compilador cuide o que ele est fazendo (e pode fazer) com uma varivel do apontador. Aqui esto algumas declaraes tpicas para apontadores: Type PInt = ^Integer; // PInt agora um apontador para umInteger Foo = record // Um tipo registro GobledyGook: string; Snarf: Real; end; PFoo = ^Foo; // PFoo um apontador para um tipo Foo var P: Pointer; // Um ponteiro sem tipo P2: PFoo; // Uma instncia de PFoo Nota: Programadores de C notaro a similaridade entre o operador ^ do Object Pascal e o operador * do C.operator. O tipo Pointer do Pascal corresponde ao tipo void * do C. Lembre que uma varivel do apontador apenas armazena um endereo de memria. Alocar espao para seja o que for que o apontador aponte trabalho do programador. Voc pode alocar espao para um apontador usando uma das rotinas de alocao de memria discutidas anteriormente e mostradas na Tabela 2.6. Nota: Quando um apontador no aponta para nenhum lugar (seu valor zero), seu valor Nil, e frequente cham-lo de um apontador Nil ou Null. Se voc quiser acessar o dado para o qual um apontador particular aponta, siga o nome da varivel do apontador com o operador ^. O cdigo seguinte ilustra o trabalho com apontadores: Program PtrTest; Type MyRec = record I: Integer; S: string; R: Real; end; PMyRec = ^MyRec; var Rec : PMyRec; begin

40

New(Rec); // aloca memria para Rec Rec^.I := 10; // Pe algo em Rec. Rec^.S := 'E agora para algo completamente diferente.'; Rec^.R := 6.384; { Rec est agora cheio } Dispose(Rec); // No esquea de liberar a memria! end. Quando Usar New() Use a funo New() para alocar memria para um apontador para uma estrutura de um tamanho conhecido. Como o compilador sabe quo grande uma estrutura particular, uma chamada para New() causar a alocao do nmero correto de bytestornando-o mais seguro e mais conveniente do que usar GetMem() ou AllocMem(). Nunca aloque variveis Pointer ou PChar usando a funo New() porque o compilador no pode adivinhar quantos bytes voc necessita para esta alocao. Lembre de usar Dispose() para liberar qualquer memria alocada usando a funo New(). Voc tipicamente usa GetMem() ou AllocMem() para alocar memria para estruturas para as quais o compilador no pode saber o tamanho. O compilador no pode falar antes do tempo quanto de memria voc quer alocar para tipos PChar ou Pointer. Tenha cuidado de no tentar manipular mais dados do que voc tenha alocado com essas funes, entretanto, porque esta uma das causas clssicas de um erro de Access Violation. Voc deve usar FreeMem() para limpar qualquer memria que voc alocou com GetMem() ou AllocMem(). AllocMem(), a propsito, um pouco mais seguro do que GetMem() porque AllocMem() sempre inicializa a memria que ela alocou com zero.

Um aspecto de Object Pascal que pode dar aos programadores de C alguma dor de cabea a estrita checagem de tipo efetuada sobre tipos pointer. Por exemplo, as variveis a e b no exemplo seguinte no compatveis em tipo: var a: ^Integer; b: ^Integer; Contrastando, as variveis a e b na declarao equivalente em C so compatveis em tipo: int *a; int *b Object Pascal cria um tipo nico para cada declarao ponteiro-para-tipo, assim voc deve criar um tipo se voc quiser atribuir valores de a para b como mostrado a seguir: type PtrInteger = ^Integer; // criando um tipo var a, b: PtrInteger; // agora a e b so compatveis

41

Aliases de Tipos Object Pascal tem a capacidade de criar novos nomes, ou apelidos (aliases), para tipos que j esto definidos. Por exemplo, se voc quiser criar um novo nome para um Integer chamado MeuInteiroPerfeito, voc pode fazer isso usando o cdigo seguinte: type MeuInteiroPerfeito = Integer; O alias do novo tipo definido compatvel em tudo com o tipo para o qual ele um alias, o que significa, neste caso, que voc pode usar MeuInteiroPerfeito em qualquer lugar que voc use Integer. possvel, entretanto, definir aliases tipados fortemente que so considerados um novo e nico tipo pelo compilador. Para fazer isso, use a palavra reserda type da seguinte maneira: type MeuOutroInteiroPerfeito = type Integer; Usando esta sintaxe, o tipo MeuOutroInteiroPerfeito ser convertido para um Integer quando necessrio para propsitos de atribuio, mas MeuOutroInteiroPerfeito no ser compatvel com Integer quando usado nos parmetros var ou out. Ento, o cdigo seguinte est sintaticamente correto: var MOIP: MeuOutroInteiroPerfeito; I: Integer; begin I := 1; MOIP := I; Mas o cdigo a seguir no compilar: procedure Mercenario(var Value: Integer); begin // algum cdigo end; var M: MeuOutroInteiroPerfeito; begin M := 29; Mercenario(M); // Erro: M no uma varivel compatvel com Integer Typecasting e Converso de Tipos Typecasting uma tcnica pela qual voc pode forar o compilador a ver uma varivel de um tipo como se fosse outro tipo. Devido a natureza fortemente tipada do Pascal, voc achar que o compilador muito exigente na compatibilidade de tipos nos parmetros formais e atuais de uma chamada de funo. Portanto, voc ocasionalmente ter que moldar um varivel de um tipo para uma varivel de outro tipo para fazer o compilador feliz. Suponha que voc necessite atribuir o valor de um caracter para um varivel byte:

42

var c: char; b: byte; begin c := 's'; b := c; // compilador se queixar desta linha end. Na sintaxe seguinte, um typecast requerido para converter c em um byte. Um typecast fala ao compilador que voc realmente sabe o que voc est fazendo e quer converter um tipo para outro: var c: char; b: byte; begin c := 's'; b := byte(c); // o compilador ficar feliz com esta linha end. Nota: Voc pode converter uma varivel de um tipo para outro apenas se o tamanho do dado das duas variveis o mesmo. Por exemplo, voc no pode fazer o typecast de um Double para um Integer. Para converter um tipo ponto-flutuante para um inteiro, use as funes Trunc() ou Round(). Para converter um inteiro em um valor de ponto-flutuante, use o operador de atribuio: FloatVar := IntVar. Object Pascal tambm suporta um variedade especial de typecasting entre objetos usando o operador as, o qual descrito depois na seo "Informao do Tipo em Tempo de Execuo".

Resources Delphi 3 introduziu a capacidade de colocar um resources de strings diretamente no cdigo fonte do Object Pascal usando a clusula resourcestring clause. Resources so strings literais (usualmente aquelas que so mostradas para o usurio) que so localizadas fisicamente em resource atachados aplicao ou bibliotecas ao invs de serem embutidas no cdigo fonte. Seu cdigo fonte referencia os resouces de strings no lugar das strings literais. Separando strings do cdigo fonte, voc pode mais facilmente traduzir sua aplicao adicionando resources em um lngua diferente. Resources de strings so declarados na forma de identifier = string literal na clusula resourcestring, como mostrado a seguir: resourcestring ResString1 = 'Resource string 1'; ResString2 = 'Resource string 2'; ResString3 = 'Resource string 3'; Sintaticamente, resources de strings podem ser usados em seu cdigo fonte em uma maneira idntica a constantes de strings: resourcestring ResString1 = 'hello';

43

ResString2 = 'world'; var String1: string; begin String1 := ResString1 + ' ' + ResString2; . . . end; Debaixo do pano em tempo de compilao, o compilador do Delphi coloca os resources de strings em uma fonte de strings e linka aquele resource a sua aplicao. Em tempo de execuo, referncias a um resource de strings resultam em uma chamada implcita para a funo API LoadString() para carregar a string do resource para dentro da memria. Testando Condies Esta seo compara as construes if e case em Pascal aos seus similares em C e Visual Basic. Assumindo que voc essas construes de programao antes, para que no seja gasto muito tempo explicando-os. O Comando IF Um comando if capacita voc a determinar se uma certa condio foi encontrada antes de executar um bloco de cdigo particular. Como um exemplo, aqui est um comando if em Pascal, seguido pelas definies equivalentes em C e Visual Basic: { Pascal } if x = 4 then y := x; /* C */ if (x == 4) y = x; 'Visual Basic If x = 4 Then y = x Nota: Se voc tem um comando if que faz mltiplas comparaes, esteja certo de que voc fechou cada uma das comparaes entre parnteses para clarear o cdigo. Faa isto: if (x = 7) and (y = 8) then No faa isso; isso causa uma insatisfao ao compilador: if x = 7 and y = 8 then Use as palavras-chave begin e end em Pascal da mesma forma que voc usa { e } em C ou C+ +. Por exemplo, use a seguinte construo se voc quiser executar mltiplas linhas de texto quando uma dada condio verdadeira: if x = 6 then begin FaaAlgumaCoisa FaaAlgoMais;

44

FaaUmaOutraCoisa; end; Voc pode combinar mltiplas condies usando a construo if..else if x =100 then AlgumaFuno else if x = 200 then AlgumaOutraFuno else begin AlgumaCoisaMais; Finalizando; end; Usando Instrues case A instruo case no Pascal trabalha de forma muito semelhante instruo switch no C ou C++. A instruo case fornece um meio para escolher uma condio entre muitas possibilidades sem uma imensa contruo de if..else if..else if. Aqui est um exemplo da instruo case do Pascalt: case AlgumaVariavelInteira of 101 : FaaAlgo; 202 : begin FaaAlgo; FaaAlgoMais; end; 303 : FaaUmaOutraCoisa; else FaaODefault; end; Nota: O tipo de seletor de uma instruo case deve ser um tipo ordinal. ilegal usar um tipo no ordinal, tal como uma string, com um seletor de case Aqui est uma instruo switch do C equivalente ao exemplo anterior: switch (AlgumaVariavelInteira) { case 101: FaaAlgo; break; case 202: FaaAlgo; FaaAlgoMais; break case 303: FaaUmaOutraCoisa; break; default: FaaODefault; } Loops Um loop uma construo que permite que voc efetue repetidamente algum tipo de ao. Construes de loop em Pascal so similares ao que voc deve estar familiar com a sua experincia em outras linguagens, assim esta seo no gastar tempo ensinado sobre loops.

45

Esta seo descreve as vrias construes de loops que voc pode usar em Pascal. O Loop for O loop for ideal quando voc necessita repetir uma ao um nmero predeterminado de vezes. Aqui est um exemplo, embora no muito til, de um loop for que incrementa um ndice de loop a uma varivel 10 veze: var I, X: Integer; begin X := 0; for I := 1 to 10 do inc(X, I); end. O equivalente em C do exemplo anterior o seguinte: void main(void) { int x, i; x = 0; for(i=1; i<=10; i++) x += i; } E aqui est o equivalente em Visual Basic do mesmo conceito: X=0 For I = 1 to 10 X=X+I Next I Cuidado: Uma advertncia para aqueles familiarizados com Delphi 1.0: atribuies varivel controladora do loop no so mais permitidas devido a forma como o loop otimizado e gerenciado pelo compilador 32 bit. O Loop while Use o loop while quando voc quiser que alguma parte de seu cdigo repita enquanto alguma condio verdadeira. As condie de um loop while so testadas antes que o loop executado, e um exemplo clssico para o uso de um loop while a efetuao repetida de alguma ao sobre um arquivo enquanto o fim do arquivo no for encontrado. Aqui est um exemplo que demonstra um loop que l uma linha por vez de um arquivo e a escreve na tela: Program FileIt; {$APPTYPE CONSOLE} var f: TextFile; // um arquivo texto s: string; begin

46

AssignFile(f, 'foo.txt'); Reset(f); while not EOF(f) do begin readln(f, S); writeln(S); end; CloseFile(f); end. O loop while do Pascal funciona basicamente da mesma forma que o loop while do C ou o loop Do While do Visual Basic. repeat..until O lao repeat..until destina-se ao mesmo tipo de problema que um loop while mas de um ngulo diferente. Ele repete um dado bloco de cdigo at que uma certa condio torne-se True. Diferentemente de um loo while, o cdigo do loo sempre executado ao menos uma vez porque a condio testada no final do loop. Repeat..until aproximadamente equivalente ao loop do..while do C. Por exemplo, o fragmento de cdigo a seguir repete uma instruo que incrementa um contador at que o valor do contador torne-se maior do que 100: var x: Integer; begin X := 1; repeat inc(x); until x > 100; end. O Procedimento Break() A chamada de Break() de dentro de um lao while, for, ou repeat loop faz com que seu programa salte imediatamente para o fim do loop que estiver sendo executado. Este mtodo til quando voc necessita deixar o loop imediatamente por causa de alguma circunstncia que pode aparecer dentro do loop. O procedimento Break() do Pascal anlogo as instrues Break do C e Exit do Visual Basic. O loop a seguir usa Break() para terminar o loop depois de cinco iteraes: var i: Integer; begin for i := 1 to 1000000 do begin MessageBeep(0); // faz o computador apitar if i = 5 then Break; end; end; O Procedimento Continue()

47

Chame Continue() dentro de um loop quando voc quiser saltar sobre um poro de cdigo e continuar na prxima iterao do loop. Note no exemplo seguinte que o cdigo depois de Continue() no executado na primeira iterao do loop: var i: Integer; begin for i := 1 to 3 do begin writeln(i, '. Antes do continue'); if i = 1 then Continue; writeln(i, '. Depois do continue'); end; end; Procedimentos e Funes Como um programador, voc j deve estar familiarizado com os conceitos de funes e procedimetos. Um procedimento uma parte discreta do programa que efetua alguma tarefa particular quando ele chamado e ento retorna a parte do seu cdigo que o chamou. Uma funo trabalha da mesma forma exceto pelo fato de que ela retorna um valor depois de sua sada para a parte do programa que a chamou. Se voc est familiarizado com C ou C++, considere que um procedimento em Pascal equivalente a uma funo em C ou C++ que retorna void, enquanto que uma funo corresponde a uma funo do C ou C++ que retorna um valor. Listagem 2.1 demonstra um pequeno programa do Pascal com um procedimento e uma funo. Program FuncProc; {$APPTYPE CONSOLE} procedure BiggerThanTen(i: Integer); { escreve algo na tela se I maior do que 10 } begin if I > 10 then writeln('Surpresa.'); end; function IsPositive(I: Integer): Boolean; { Retorna True se I 0 ou positivo, False se I negativo } begin if I < 0 then Result := False else Result := True; end; var

48

Num: Integer; begin Num := 23; BiggerThanTen(Num); if IsPositive(Num) then writeln(Num, ' Positivo.') else writeln(Num, ' Negativo.'); end. Nota: A varivel local Result na funo IsPositive() merece ateno especial. Toda funo em Object Pascal tem uma varivel local implcita chamada Result que contm o valor de retorno da funo. Note que diferentemente de C e C++, a funo no termina assim que um valor atribudo a Result. Voc tambm pode retornar um valor de uma funo atribuindo ao nome de uma funo o valor dentro do cdigo da funo. Esta a sintaxe padro do Pascal e costume das verses anteriores do Borland Pascal. Tenha cuidado ao notar tambm que a varivel implcita Result no permitida quando a opo Extended Syntax do compilador est desabilitada em Project | Options | Compiler ou quando usada a diretiva {$X-}. PassandoParmetros Pascal possibilita que voc passe parmetros por valor ou por referncia para funes e procedimentos. Os parmetros qu voc passa podem ser de qualquer tipo bsico ou definido pelo usurio, ou open array (open arrays sero discutidos nesse tutorial). Parmetros tambm podem ser constantes se seus valores no mudarem no procedimento ou funo. Parmetros Valor Parmetros valor so o modo default de passagem de parmetros. Quando um parmetro passado por valor, significa que uma cpia local daquela varivel criada, e a funo ou procedimento opera sobre a cpia. Considere o exemplo a seguir: procedure Foo(s: string); Quando voc chama um procedimento desta forma, uma cpia da string s ser criada, e Foo() operar sobre a cpia local de s. Isto significa que vco pode escolher o valor de s sem ter qualquer efeito sobre a varivel passada para dentro de Foo(). Parmetros Referncia Pascal permite que voc passe variveis para funes e procedimentos por referncia; parmetros passados por referncia so tambm chamados parmetros variveis. Passar por referncia significa que a funo ou procedimento ao receber a varivel pode modificar o valor daquela varivel. Para passar uma varivel por referncia, use a palavra var na lista de parmetros da funo ou procedimento: procedure ChangeMe(var x: longint); begin

49

x := 2; { x agora alterado na chamada do procedimento } end; Em vez de fazer uma cpia de x, a palavra reservada var faz com que o endereo do parmetro seja copiado de forma que seu valor possa ser diretamente alterado. Usar parmetros var equivalente a passar varivels por referncia em C++ usando o operador &. Assim como o operador & do C++, a palavra var faz com que o endereo da varivel seja passado para a funo ou procedimento, e no o valor da varivel. Parametros Constantes Se voc no quiser que o valor de um parmetro passado para dentro de uma funo seja alterado, voc pode declar-lo com a palavra chave const. const no apenas impede que voc modifique o valor dos parmetros, mas ele tambm gera cdigo mais otimizado para strings e registros passados para dentro de procedimentos ou funes. Aqui est um exemplo de uma declarao de procedimento que receber um parmetro string constante: procedure Goon(const s: string); Parmetros Open Array Parmetros open array permite que voc passe um nmero varivel de argumentos para funes e procedimentos. Voc pode passar ou open arrays de algum tipo homogneo ou arrays constantes de tipos diferentes. O cdigo seguinte declarar uma funo que aceita um open array de inteiros: function AddEmUp(A: array of Integer): Integer; Voc pode passar variveis, constantes ou expresses constantes para funes ou procedimentos de open array. O cdigo a seguir demonstra isto chamando AddEmUp() e passando uma variedade de elementos diferentes. var i, Rez: Integer; const j = 23; begin i := 8; Rez := AddEmUp([i, 50, j, 89]); Para funcionar com um open array dentro de um a funo ou procedimento, voc pode usar as funes High(), Low() e SizeOf() para obter informao sobre o array. Para ilustrar isto, o cdigo seguinte mostra uma implementao da funo AddEmUp() que retorna a soma de todos os nmeros passados em A: function AddEmUp(A: array of Integer): Integer; var i: Integer; begin Result := 0; for i := Low(A) to High(A) do

50

inc(Result, A[i]); end; Object Pascal tambm suporta um array of const, que permite que voc passe tipos de dados heterogneos em um array para uma funo ou procedimento. A sintaxe para definir uma funo ou procedimento que aceite um array of const a seguinte: procedure OQueEuTenho(A: array of const); Voc poderia chamar a funo acima com a seguinte sintaxe: OQueEuTenho(['Tabasco', 90, 5.6, @OQueEuTenho, 3.14159, True, 's']); O compilador implicitamente converte todos os parmetor spara o tipo TVarRec quando eles so passados para a funo ou procedimento por um array of const. TVarRec definido na unit System da seguinte forma: type PVarRec = ^TVarRec; TVarRec = record case Byte of vtInteger: (VInteger: Integer; VType: Byte); vtBoolean: (VBoolean: Boolean); vtChar: (VChar: Char); vtExtended: (VExtended: PExtended); vtString: (VString: PShortString); vtPointer: (VPointer: Pointer); vtPChar: (VPChar: PChar); vtObject: (VObject: TObject); vtClass: (VClass: TClass); vtWideChar: (VWideChar: WideChar); vtPWideChar: (VPWideChar: PWideChar); vtAnsiString: (VAnsiString: Pointer); vtCurrency: (VCurrency: PCurrency); vtVariant: (VVariant: PVariant); vtInterface: (VInterface: Pointer); vtWideString: (VWideString: Pointer); vtInt64: (VInt64: PInt64); end; O campo VType indica qual tipo de dado TVarRec contm. Este compo pode ter qualquer um dos seguintes valores: const { TVarRec.VType values } vtInteger = 0; vtBoolean = 1; vtChar = 2; vtExtended = 3; vtString = 4; vtPointer = 5; vtPChar = 6;

51

vtObject = 7; vtClass = 8; vtWideChar = 9; vtPWideChar = 10; vtAnsiString = 11; vtCurrency = 12; vtVariant = 13; vtInterface = 14; vtWideString = 15; vtInt64 = 16; Como voc pode adivinhar, como array of const permite que voc passe parmetros sem levar em considerao o seu tipo, eles podem ser difceis de trabalhar. Como um exemplo de como trabalhar com array of const, a implementao a seguir para OQueEuTenho() itera atravs do array e msotra uma mensagem para o usurio indicando qual tipo de dado foi passado em qual ndice: procedure OQueEuTenho(A: array of const); var i: Integer; TypeStr: string; begin for i := Low(A) to High(A) do begin case A[i].VType of vtInteger : TypeStr := 'Inteiro'; vtBoolean : TypeStr := 'Booleano'; vtChar : TypeStr := 'Char'; vtExtended : TypeStr := 'Extended'; vtString : TypeStr := 'String'; vtPointer : TypeStr := 'Pointer'; vtPChar : TypeStr := 'PChar'; vtObject : TypeStr := 'Object'; vtClass : TypeStr := 'Class'; vtWideChar : TypeStr := 'WideChar'; vtPWideChar : TypeStr := 'PWideChar'; vtAnsiString : TypeStr := 'AnsiString'; vtCurrency : TypeStr := 'Currency'; vtVariant : TypeStr := 'Variant'; vtInterface : TypeStr := 'Interface'; vtWideString : TypeStr := 'WideString'; vtInt64 : TypeStr := 'Int64'; end; ShowMessage(Format('O item do array %d um %s', [i, TypeStr])); end; end; Escopo Escopo refere-se a alguma parte de seu programa na qual uma dada funo ou varivel conhecidad pelo compilador. Uma constante global est no escopo em todos os pontos de seu programa, por exemplo, enquanto uma varivel local para algum procedimento tem escopo apenas dentro daquele procedimento. Considere a Listagem 2.2.

52

Listagem 2.2. Uma ilustrao de escopo. program Foo; {$APPTYPE CONSOLE} const AlgumConstant = 100; var AlgumGlobal: Integer; R: Real; procedure AlgumProc(var R: Real); var LocalReal: Real; begin LocalReal := 10.0; R := R - LocalReal; end; begin AlgumGlobal := AlgumConstant; R := 4.593; AlgumProc(R); end. AlgumConstant, AlgumGlobal e R tem escopo globalseus valores so conhecidos pelo compilador em todos os pontos dentro do programa. O procedimento AlgumProc() tem duas varivel na qual o escopo local para aquele procedimento: R e LocalReal. Se voc tentar acessar LocalReal fora de AlgumProc(), o compilador mostrar um erro de identificador desconhecido. Se voc acessar R dentro de AlgumProc(), voc estar referindo a verso local, mas se voc acessar R fora daquele procedimento, voc estar referindo verso global. Units Units so os mdulos individuais do cdigo fonte que constituem um programa Pascal. Uma unit um local para voc agrupar funes e procedimentos que podem ser chamdos de seu programa principal. Para ser uma unit, um mdulo fonte deve consistir ao mesnos de trs partes: Uma instruo unit. Toda unit deve ter como sua primeira linha uma instruo dizendo que uma unit e identificando o nome da unit. O nome da unit sempre deve ser o mesmo nome do arquivo. Por exemplo, se voc tem um arquivo chamado FooBar, a instruo serunit FooBar; A parte de interface. Depois da instruo unit, uma prxima linha funcional da unit do cdigo deve ser a instruo interface. Tudo que seguir esta instruo, at a instruo implementation, informao que pode ser compartilhada com seu programa e com outras units. A parte de interface de uma unit onde voc declara tipos, constantes, variveis,

53

procedimentos e funes que voc quer tornar disponvel para seu programa principal e para outras units. Apenas declaraesnunca corpos de procedimentospodem aparecer na interface. A instruo interface deve ser uma palavra em uma linha:interface A parte de implementation. Esta segue a parte de interface da unit. Embora a parte de implementation da unit tenha principalmente procedimentos e funes, tambm onde voc declara qualquer tipo, constante ou varivel que vco no queira que esteja disponvel fora desta unit. A parte de implementation onde voc define quaisquer funes e procedimentos que voc declarou na parte de interface. A instruo implementation dever ser uma interface deve ser uma palavra em uma linha:implementation Opcionalmente, uma unit pode tambm incluir duas outras partes: Uma parte de initialization. Esta poro da unit, que localizada prxima do fim do arquivo, contm qualquer cdigo de inicializao para a unit. Este cdigo ser executado antes que o programa principal comece a executar, e executar apenas uma vez. Uma parte de finalization. Esta poro da unit, que localizada entre a initialization e end. da unit, contm qualquer cdigo de limpeza que executa quando o programa termina. A seo de finalization foi introduzida linguagem no Delphi 2.0. No Delphi 1.0, a finalizao da unit era efetuada pela adio de um novo procedimento de sada usando a funo AddExitProc(). Se voc est portando uma aplicao do Delphi 1.0, voc deve mover seus procedimentos de sada para dentro da parte de finalizao de suas units. Nota: Quando muitas units tem cdigo de initialization/finalization, a execuo de cada seo procede na ordem em que as units so encontradas pelo compilador (a primeira unit na clusula uses do programa, ento a primeira unit na clusula uses daquela unit, e assim por diante). Tambm, uma m idia escrever cdigo de inicializao e finalizao que dependa de tal ordem porque uma pequena mudana na clusula uses pode causar alguma dificuldade para encontrar bugs! A Clusula uses A clusula uses onde voc lista as units que voc quer incluir em um programa ou unit particular. Por exemplo, se voc tem um programa chamado FooProg que usa funes e tipos em duas units, UnitA e UnitB, a declarao uses apropriada a seguinte: Program FooProg; uses UnitA, UnitB; Units podem ter duas clusulas uses: uma na seo interface e outra na seo implementation section. Aqui est um cdigo para uma unit exemplo: Unit FooBar; interface

54

uses BarFoo; { declaraes pblicas aqui } implementation uses BarFly; { declaraes privadas aqui } initialization { inicializao da unit aqui } finalization { limpeza da unit aqui } end. Referncia Circular de Units Ocasionalmente, voc ter uma situao onde a UnitA usa a UnitB e a UnitB usa a UnitA. Isto chamado uma referncia circular de units. A ocorrncia de uma referncia circular de units frequentemente uma indicao de uma falha de projeto na sua aplicao, e voc deve evitar estruturar seu programa com uma referncia circular. A melhor soluo frequentemente mover um pedao dos dados que ambas as units UnitA e UnitB precisam usar para fora em uma terceira unit. Entretanto, como com muitas coisas, algumas vezes voc no tem como evitar uma referncia circular. Nestes casos, mova uma das clusulas uses para a parte de implementation de sua unit e deixar a outra na parte de interface. Isto geralmente resolve o problema. Packages Packages do Delphi permitem que voc coloque pores de sua aplicao em mdulos separados, os quais podem ser distribudos atravs de mltiplas aplicaes. Se voc j programou em Delphi 1 ou 2, voc apreciar que voc pode tirar vantagens de packages sem qualquer mudana em seus cdigos fontes existentes. Pense em package como uma coleo de units armazenadas em um mdulo como uma DLL separado (um Delphi Package Library, ou arquivo DPL). Sua aplicao pode ento linkar com estas units "empacotadas" em tempo de execuo ao invz de linkar em tempo de compilao. Como o cdigo para essas units reside em arquivos DPL e no em EXE ou DLL, o tamanho de seu EXE ou DLL pode ficar muito menor. Quatro tipos de packages esto disponveis para voc criar e usar: 1. Runtime package. Este tipo de package contm units requeridas em tempo de execuo por sua aplicao. Quando depender de um package de tempo de execuo particular, sua aplicao no rodar na ausncia daquele package. O VCL30.DPL do Delphi um exemplo deste tipo de package. 2. Design package. Este tipo de package contm elementos necessrios para o projeto de sua aplicao tais como componentes, propriedades e editores de componentes. Ele pode ser instalado na biblioteca de componentes do Delphi usando o item do menu Component | Install Package. Os packagens DCL*.DPL do Delphi so exemplos deste tipo de package.

55

3. Runtime e Design package. Este package serve para os propsitos listados nos itens 1 e 2. Criar este tipo de package torna o desenvolvimento e a distribuio de sua aplicao um pouco mais simples, mas este tipo de package menos eficiente porque ele deve carregar a bagagem de projeto mesmo em suas aplicaes distribudas. 4. Nem runtime nem design package. This rare breed of package is intended to be used only by other packages and is not intended to be referenced directly by an application or used in the design environment. Utilizando Packages do Delphi Utilizar packages em suas aplicaes fcil. Simplismente marce o check box Build with Runtime Packages no dilogo Project | Options | Packages. Na prxima vez que voc construir sua aplicao depois de ter selecionado esta opo, sua aplicao ser linkada dinmicamente aos pacotes em tempo de execuo ao invs de ter units linkadas estaticamente dentro de seu EXE ou DLL. O resultado ser uma aplicao muito mais talhada (embora tenha em mente que voc ter que disponibilizar os packages necessrios com sua aplicao). Sintaxe de Packages Packages so muito comumente criados usando o Package Editor, que voc chama escolhendo o item do menu File | New | Package. Este editor gera um arquivo Delphi Package Source (DPK), que ser compilado dentro de um package. A sintaxe para este arquivo DPK muito simples, e est no seguinte formato: package PackageName requires Package1, Package2, ...; contains Unit1, Unit2, ...; end. Os packages listados na clusula requires so requeridos na ordem para este pacote carregar. Tipicamente, packages contendo units usadas por units listadas na clusula contains so listados aqui. Units listadas na clusula contains sero compilados dentro deste pacote. Note que units listadas aqui no devem estar listadas tambm na clusula contains de algum dos packages listados na clusula requires. Note tambm que qualquer unit usada pelas units na clusula contains sero implicitamente arrastadas para dentro deste package (a menos que elas estejam contidas em um package requerido). Programao Orietada a Objetos Livros tem sido escritos sob o ttulo de programao orientada a objetos (POO), POO se parece mais com uma religio do que com uma metodologia de programao, gerando argumentos sobre seus mritos (ou falta disso). No nos envolveremos nos mritos relativos da POO; apenas queremos dar a voc um conhecimento sobre os princpios fundamentais sobre os quais a linguagem Object Pascal do Delphi baseada. POO um paradigma de programao que usa objetos discretoscontendo dados e cdigo como blocos que constroem a aplicao. Embora o paradigma POO no necessariamente proporcione a voc um cdigo fcil de escrever, o resultado do uso da POO tradicionalmente tem sido um cdigo fcil de se manter. Ter dados e cdigo do objeto juntos simplifica o processo de procurar e encontrar bugs, consertando-os com um efeito mnimo sobre os outros

56

objetos, e melhorando uma parte do seu programa ao mesmo tempo. Tradicionalmente, uma linguagem de POO contm a implementao de no mnimo trs conceitos da POO:

Encapsulamento Trata de combinar campos de dados relacionados e ocultar os detalhes de implementao. As vantagens do encapsulamento incluem modularidade e isolamento do cdigo de outros cdigos. Herana A capacidade de criar novos objetos que mantm as propriedades e comportamentos dos objetos ancestrais. Este conceito permite que voc crie hierarquias de objetos tais como VCL criando primeiro objetos genricos e ento criando objetos que tem funcionalidades mais especficas.A vantagem da herana diviso de cdigo comum. Polimorfismo Literalmente, polimorfismo significa "muitas formas". Chamar mtodos de uma varivel do objeto chamar o cdigo apropriado para qualquer uma das instncias que na verdade uma varivel.

Uma Nota sobre Herana Mltipla Object Pascal no suporta herana mltipla de objetos como o C++. Herana Mltipla o conceito de um dado objeto sendo derivado de dois objetos separados, criar um objeto que contm todo os cdigos e dados dos dois objetos pais. Hernaa mltipla permite que voc crie um objeto bala de morango criando um novo objeto que herde a classe ma e a classe chamada morango. Embora esta funcionalidade parea til, ela pode introduzir mais problemas e ineficincias em seu cdigo do que ajudar se voc no souber utiliz-la. Object Pascal fornece duas aproximaes para resolver este problema. A primeira soluo fazer uma classe conter a outra classe. Voc ver esta soluo por toda a VCL do Delphi. Para construir sob uma analogia da classe bala de morango, voc poderia fazer do objeto morango um membro do objeto bala. A segunda soluo usar interfaces (voc aprender mais sobre interfaces na seo "Interfaces"). Usando interfaces, voc pode essencialmente ter um objeto que suporta as interfaces de morango e bala.

Voc deve compreender os trs termos seguintes antes de continuar a explorar os conceitos de objetos:

57

Campo Tambm chamado de campos de definio ou variveis de instncia, campos so as variveis de dados contidas dentro dos objetos. Um campo em um objeto como um campo em um registro do Pascal. Em C++, campos algumas vezes so referidos como membros. Mtodo O nome dos procedimentos e funes que fazem parte de um objeto. Mtodos so chamados funes membros em C++. Propriedade Uma propriedade uma entidade que acessa os dados e cdigos contidos dentro de um objeto. Propriedades separam o usurio final dos detalhes de implementao de um objeto. Nota: Geralmente considerado um mau estilo de POO acessar um campo de um objeto diretamente. Isto porque os detalhes de implementao do objeto podem mudar. Em vez disso, use propriedades de acesso, as quais permitem a interface padro com o objeto sem se preocupar nos detalhes de como os objetos so implementados. Propriedades so explicadas na seo "Propriedades". Baseado em Objetos Versus Orientado Objetos Em algumas ferramentas, voc manipula entidades (objetos), mas no pode criar seus prprios objetos. Controles OCX no Visual Basic 4 so um bom exemplo disso. Embora voc possa usar um controle OCX em suas aplicaes, voc no pode criar um e no pode herdar um controle OCX de um outro no Visual Basic. Ambientes tais como esses frequentemente so chamados de ambientes baseados em objetos. Delphi um ambiente orientado a objetos. Isto significa que voc pode criar novos objetos no Delphi ou do nada ou baseado em componentes existentes. Isto inclui todos os objetos do Delphi, sejam eles visuais, no-visuais ou forms de tempo de projeto. Usando Objetos do Delphi Como mencionado anteriormente, objetos so entidades que podem conter dados e cdigos. Objetos do Delphi tambm fornece suporte para herana, encapsulamento e polimorfismo. Declarao e Instanciao Antes de usar um objeto, voc deve declarar a classe do objeto usando a palavra-reservada class. Como descrito anteriormente, classes de objetos so declaradas na seo type a unit de um programa: type TFooObject = class; Alm da classe do objeto voc possivelmente ter uma varivel daquela classe, ou instncia, declarada na seo var: var FooObject: TFooObject; Voc cria uma instncia de um objeto em Object Pascal chamando um de seus construtores. Um construtor responsvel por criar uma instncia de seu objeto e alocar qualquer memria

58

ou inicializar qualquer campo necessrio de forma que o objeto esteja em estado de uso depois da sada do construtor. Objetos em Object Pascal sempre tem no mnimo um construtor chamado Create()embora seja possvel para um objeto ter mais de um construtor. Dependendo do tipo do objeto, Create() pode tomar diferentes nmeros de parmetros. Este tutorial foca o caso simples onde Create() no pega nenhum parmetro. Diferente do C++, construtores de objetos em Object Pascal no so chamados automaticamente e incumbido ao programador chamar o construtor do objeto. A sintaxe para chamar um construtor a seguinte: FooObject := TFooObject.Create; Repare que a sintaxe para chamada de um construtor um nico pedao. Voc faz referncia ao mtodo Create() do objeto pelo tipo e no pela instncia como voc faria com outros mtodos. Isto pode parecer estranho no incio, mas vai fazer sentido. FooObject, uma varivel, indefinida em tempo de chamada, mas o cdigo para TFooObject, um tipo, est esttico na memria. Uma chamada esttica para seu mtodo Create() ento totalmente vlido. O ato de chamar um construtor para criar uma instncia de um objeto frequentemente chamado de instanciao. Nota: Quando uma instncia de um objeto criada usando o cosntrutor, o compilador assegurar que todo campo em seu objeto ser inicializado. Voc pode assumir com certeza que todos nmeros sero inicializados para 0, todos os ponteiros para Nil, e todas as strings estaro vazias. Destruio Quando voc finaliza o uso de um objeto, voc deve desalocar a instncia chamando seu mtodo Free(). O mtodo Free() primeiro checa para garantir que a instncia do objeto no Nil, e ento ele chama o mtodo destrutor do objeto, Destroy(). O destrutor, claro, faz o oposto do construtor, ele desaloca qualquer memria alocada e efetua qualquer outro procedimento de limpeza requerido a fim de remover corretamente o objeto da memria. A sintaxe simples: FooObject.Free; Diferente da chamada para Create(), a instncia do objeto usada na chamada para o mtodo Free(). Lembre-se de nunca chamar Destroy() diretamente, em vez disso chame o mtodo Free(). Cuidado: Em C++, o destrutor de um objeto declarado estaticamente chamado automaticamente quando seu objeto sai do escopo, mas voc deve chamar o destrutor para quaisquer objetos alocados dinamicamente. A regra a mesma em Object Pascal, exceto pelo fato de que todos os objetos so implicitamente dinmicos em Object Pascal, assim voc deve seguir a regra de que qualquer coisa que voc criar, voc deve liberar. A exceo a esta regra que quando seus objetos so propriedade de outros objetos, eles sero liberados para voc. Voc pode estar se perguntando como todos esses mtodos ficam dentro de um pequeno objeto. Voc certamente no os declarou por conta prpria, certo? Certo. Os mtodos

59

discutidos at agora na verdade vem do objeto base do Object Pascal: TObject. Em Object Pascal, todos os objetos so sempre descendentes de TObject independentemente de serem declarados como tal ou no. Ento a declarao Type TFoo = Class; equivalente a declarao Type TFoo = Class(TObject); Mtodos Mtodos so procedimentos e funes pertencentes a um dado objeto. Mtodos so aquelas coisas que do a um objeto o comportamento diferente de um simples dado. Dois mtodos importantes dos objetos que voc cria so os mtodos construtor e destrutor, que j discutimos. Voc tambm pode criar mtodos customizados em seus objetos para efetuar outras tarefas. Criar um mtodo um processo de dois passos. Voc deve primeiro declarar o mtodo na classe do objeto, e ento voc deve definir o mtodo no cdigo. O cdigo seguinte demonstra o processo de declarao e definio de um mtodo: type TBoogieNights = class Dance: Boolean; procedure DoTheHustle; end; procedure TBoogieNights.DoTheHustle; begin Dance := True; end; Note que quando definimos o corpo do mtodo, voc tem que usar o nome qualificado completo, como voc fez quando definiu o mtodo DoTheHustle. importante tambm notar que o campo Dance do objeto pode ser acessado diretamente de dentro do mtodo. Tipos de Mtodos Mtodos do objeto podem ser declarados como static, virtual, dynamic, ou message. Considere o seguinte objeto exemplo: TFoo = class procedure EuSouUmEstatico; procedure EuSouUmVirtual; virtual; procedure EuSouUmDinmicp; dynamic; procedure EuSouUmMensagem(var M: TMessage); message wm_AlgumaMensagem; end;

60

Mtodos Estticos IAmAStatic um mtodo esttico. O mtodo esttico o tipo de mtodo default e ele funciona similarmente a um procedimento ou funo regular. O compilador conhece o endereo desses mtodos, e assim, quando voc chama um mtodo esttico, ele capaz de linkar aquela informao dentro do executvel estticamente. Mtodos estticos executam mais rapidamente; contudo eles no podem ser redefinidos em classes descendentes para fornecer polimorfismo. Mtodos Virtuais IAmAVirtual um mtodo virtual. Mtodos Virtuais so chamados da mesma forma que mtodos estticos, mas como mtodos virtuais podem ser redefinidos nas classes descendentess, o compilador no conhece o endereo de uma funo virtual particular quando voc a chama em seu cdigo. O compilador, ento, constri uma Tabela de Mtodos Virtuais (Virtual Method Table - VMT) que fornece um meio de descobrir os endereos das funes em tempo de execuo. Todos os mtodos virtuais chamados so remetidos em tempo de execuo atravs da VMT. Uma VMT do objeto contm todos os metodos virtuais de seus ancestrais assim como os que foram declarados nele, assim mtodos virtuais usam mais memria do que mtodos dinmicos, embora eles executem mais rapidamente. Mtodos Dinmicos IAmADynamic um mtod dinmico. Mtodos Dinmicos so basicamente mtodos virtuais com sistema diferente de execuo. O compilador atribui um nico nmero a cada mtodo dinmico e usa esses nmeros, junto com os endereos dos mtodos, para construir uma Tabela de Mtodos Dinmicos (Dynamic Method Table - DMT). Diferentemente da VMT, a DMT de um objeto contm apenas os mtodos dinmicos que ele declarou, e aqueles mtodos que dependem das DMTs de seus ancestrais para o resto de seus mtodos dinmicos. Por causa disto, mtodos dinmicos so utilizam menos memria do que mtodos virtuais, mas eles so mais lentos porque eles podem ter que propagar atravs de muitas DMTs de ancestrais antes de encontrar o endereo de um mtodo dinmico particular. Mtodos de Mensagem EuSouUmaMensagem um mtodo manipulador de mensagens. O valor depois da palavra reservada message dita para qual mensagem o mtodo responder. Mtodos de mensagem so usados para criar uma resposta automtica para as mensagens do Windows, e voc geralmente no as chamar diretamente. Polimorfismo (Mtodos Impostos) Os mtodos com a palavra reservada override (imposio) so a implementao de Object Pascal de polimorfismo. Eles possibilitam que voc altere o comportamento de um mtodo de descendente para descendente. Os mtodos em Object Pascal podem ser reescritos nas classes descendentes somente se eles forem declarados pela primeira vez como virtual ou dynamic. Para reescrever um mtodo, apenas use a diretiva override no lugar de virtual ou dynamic em sua classe descendente. Voc pode reescrever os mtodos EuSouUmVirtual e EuSouUmDinamico como no exemplo seguinte: TFooChild = class(TFoo) procedure EuSouUmVirtual; override;

61

procedure EuSouUmDinamico; override; procedure EuSouUmaMensagem(var M: TMessage); message wm_AlgumaMensagem; end; A diretiva override troca a entrada do mtodo original na VMT com o novo mtodo. Se voc tiver redeclarado EuSouUmVirtual e EuSouUmDinamico com as palavras reservadas virtual ou dynamic no lugar de override, voc ter que criar novos mtodos em vez de reescrever os mtodos ancestrais. Tambm, se voc tentar reescrever um mtodo esttico em uma classe descendente, o mtodo esttico no novo objeto trocar totalmente o mtodo na classe ancestral. Sobrecarga de Mtodos Assim como procedimentos e funes regulares, mtodos podem ser sobrecarregados de forma que uma classe possa conter mltiplos mtodos com o mesmo nome com diferente listas de parmetros. Mtodos sobregarrecados devem ser marcados com a diretiva overload, embora o uso da diretiva na primeira instncia do nome de um mtodo em uma classe hierrquica seja opcional. O cdigo exemplo a seguir mostra uma classe contendo trs mtodos sobrecarregados: type TAlgumaClasse = class procedure UmMetodo(I: Integer); overload; procedure UmMetodo(S: string); overload; procedure UmMetodo(D: Double); overload; end; Reintroduzindo Nomes de Mtodos Ocasionalmente, voc pode querer adicionar um mtodo a uma de suas classes para para substituir um mtodo de mesmo nome em um ancestral de sua classe. Neste caso, voc no quer cancelar o mtodo do ancestral mas sim obscurecer e suplantar completamente o mtodo da classe base. Se voc simplesmente adicionar o mtodo e compilar, voc ver que o compilador produzir um alerta explicando que o novo mtodo oculta um mtodo de mesmo nome em uma classe base. Para anular este erro, use a diretiva reintroduce sobre o mtodo na classe ancestral. O cdigo a seguir demonstra o uso correto da diretiva reintroduce: type TAlgumaBase = class procedure Cooper; end; TAlgumaClasse = class procedure Cooper; reintroduce; end; Self Uma varivel implcita chamada Self est disponvel dentro dos mtodos de todos os objetos. Self um apontador para a instncia da classe que foi usada para chamar o mtodo. Self passada pelo compilador como um parmetro oculto para todos os mtodos.

62

Propriedades Pode ser mais fcil de pensar em propriedades como campos de acesso especiais que permitem que voc modifique dados e execute o cdigo contido dentro de suas classes. Para componentes, propriedades so aquelas coisas que aparecem na janela do Object Inspector quando so editados. O exemplo seguinte ilustra um Object simples com uma propriedade: TMeuObjeto = class private AlgumValor: Integer; procedure AlteraAlgumValor(UmValor: Integer); public property Valor: Integer read AlgumValor write AlteraAlgumValor; end; procedure TMeuObjeto.AlteraAlgumValor(UmValor: Integer); begin if AlgumValor <> UmValor then AlgumValor := UmValor; end; TMeuObjeto um objeto que contm o seguinte: um campoum inteiro chamado AlgumValor, um mtodoum procedimento chamado AlteraAlgumValor, e uma propriedade chamada Valor. O nico propsito do procedimento AlteraAlgumValor alteara o valor do campo AlgumValor. A propriedade Valor na verdade no contm nenhum dado. Valor um acesso para o campo AlgumValor; quando voc pergunta a Valor qual nmero ele contm, ele l o valor de AlgumValor. Quando voc tentar alterar o valor da propriedade Valor, Valor chama AlteraAlgumValor para modificar o valor de AlgumValor. Isto til por duas razes: Primeiro, permite que voc conceda ao usurio da classe uma varivel simples sem fazer com que ele se preocupe com os detalhes de implementao da classe. Segundo, voc pode permitir que o usurio reescreva os mtodos de acesso em classes descendentes por comportamentos polimrficos. Especificadores de Visibilidade Object Pascal oferece a voc controle sobre o comportamento de seus objetos permitindo que voc declare campos e mtodos com diretivas tais como protected, private, public, published e automated. A sintaxe para o uso dessas palavras a seguinte: TAlgumObjeto = class private UmaVariavelPrivada: Integer; OutraVariavelPrivada: Boolean; protected procedure UmProcedimentoProtegido; function ProtejaMe: Byte; public constructor UmConstrutorPublico; destructor UmAssassinoPublico; published property UmaPropriedade read UmaVariavelPrivada write

63

UmaVariavelPrivada; end; Voc pode colocar quantos campos ou mtodos voc quiser sob cada uma das diretivas. Para manter o estilo melhor que voc indente o especificador da mesma forma que voc indenta o nome da classe. A seguir o significado dessas diretivas: private Estas partes do seu objeto so acessveis apenas pelo cdigo na mesma unit da implementao de seu objeto. Use esta diretiva para ocultar detalhes de implementao de seus objetos de usurios e para evitar que usurios modifiquem diretamente membros sensveis de seu objeto. protected Membros protegidos de seu objeto podem ser acessados por objetos descendentes. Esta capacidade permite que voc oculte os detalhes de implementao de seu objeto dos usurios enquanto ainda fornece mxima flexibilidade para descendentes de seus objeto. public Esses campos e mtodos so acessveis de qualquer lugar em seu programa. Construtores e destrutores de objetos sempre devem ser public. published Runtime Type Information (RTTI) para ser gerada pela poro pblica de seus objetos permite que outras partes de sua aplicao obtenham informao sobre as partes published de seus objetos. O Object Inspector usa RTTI para construir sua lista de propriedades. automated O especificador automated obsoleto mas permanece para manter a compatibilidade com Delphi 2. Aqui, ento, est o cdigo para a classe TMeuObjeto que foi introduzida anteriormente, com diretivas adicionadas para melhorar a integridade do objeto: TMeuObjeto = class private AlgumValor: Integer; procedure AlteraAlgumValor(UmValor: Integer); published property Valor: Integer read AlgumValor write AlteraAlgumValor; end; procedure TMeuObjeto.AlteraAlgumValor(UmValor: Integer); begin if AlgumValor <> UmValor then AlgumValor := UmValor; end; Agora, usurios de seu objeto no sero capazes de modificar o valor de AlgumValor diretamente, e elese tero que utilizar a interface fornecida pela propriedade Valor para modificar os dados do objeto. Classes "Amigas"

64

A linguagem C++ tem um conceito de classes amigasisto , classes que podem acessar os dados privados e funes em outras classes. Isto efetuado em C++ usando a palavra friend. Embora, estritamente falando, Object Pascal no temnha uma palavra reservada similar, ele permite funcionalidade parecida. Todos os objetos declarados dentro de uma mesma unit so considerados "amigos" e possuem permiso de acesso a informao privada localizada em outros objetos naquela unit. Dentro dos Objetos Todas as instncias de classes em Object Pascal so na verdade armazenadas com apontadores de 32 bits para dados localizados na memria heap. Quando voc acessa campos, mtodos ou propriedades dentro de uma classe, o compilador automaticamente efetua um pequeno abracadabra que gera o cdigo para identificar aquele apontador para voc. Assim, para olhos destreinados, uma classe mostra-se como uma varivel esttica. O que isto significa, entretanto, que diferentemente de C++, Object Pascal oferece nenhuma forma razovel de alocar uma classe de um segmento de dado da aplicao fora do heap. TObject: A Me dos Objetos Como tudo descende de TObject, todas as classes tem alguns mtodos que so inerente de TObject, e voc pode fazer algumas suposies especiais sobre as capacidades de um objeto. Toda classe tem a habilidade de, por exemplo, informar a voc o seu nome, tipo ou at mesmo se ela herdade de uma classe particular. A beleza disso que voc, como um programador de aplicaes, no tem que se preocupar em que tipo de mgica o compilador faz para que isto acontea. Voc apenas tira vantagens da funcionalidade que ele propicia! TObject um objeto especial porque sua definio vem da unit System, e o compilador do Object Pascal est "ciente" de TObject. O cdigo a seguir ilustra a definio da classe TObject: type TObject = class constructor Create; procedure Free; class function InitInstance(Instance: Pointer): TObject; procedure CleanupInstance; function ClassType: TClass; class function ClassName: ShortString; class function ClassNameIs(const Name: string): Boolean; class function ClassParent: TClass; class function ClassInfo: Pointer; class function InstanceSize: Longint; class function InheritsFrom(AClass: TClass): Boolean; class function MethodAddress(const Name: ShortString): Pointer; class function MethodName(Address: Pointer): ShortString; function FieldAddress(const Name: ShortString): Pointer; function GetInterface(const IID: TGUID; out Obj): Boolean; class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry; class function GetInterfaceTable: PInterfaceTable; function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; virtual;

65

procedure AfterConstruction; virtual; procedure BeforeDestruction; virtual; procedure Dispatch(var Message); virtual; procedure DefaultHandler(var Message); virtual; class function NewInstance: TObject; virtual; procedure FreeInstance; virtual; destructor Destroy; virtual; end; Voc encontrar cada um desses mtodos documentados na ajuda do Delphi. Em particular, note os mtodos que so precedidos pela palavr class. Acrescentar a palavra class a um mtodo permite que ele seja chamado como um procedimento ou funo normal sem na verdade ter uma instnca da classe da qual o mtodo um membro. Esta uma interessante funcionalidade que foi emprestada de funes estticas do C++. Tenha cuidado, apesar de tudo, de no fazer um mtodo da classe depender da informao de alguma instncia, ou voc obter um erro de compilao. Interfaces Talvez a mais significante adio linguagem Object Pascal no passado recente foi o suporte nativo para interfaces, o qual foi introduzido no Delphi 3. De forma simples, uma interface define um conjunto de funes e procedimentos que podem ser usados para interagir com um objeto. A definio de uma dada interface conhecida tanto pelo implementador quanto pelo cliente da interfaceatuando como um contrato dizendo como uma interface ser definida e usada. Uma classe pode implementar mltiplas interfaces, fornecendo mltiplas "faces" pelas quais um cliente pode controlar um objeto. Como seu nome implica, uma interface define apenas uma interface pela qual objeto e clientes se comunicam. Isto similar em conceito classe VIRTUAL PURA do C++. o trabalho de uma classe que suporta uma interface implementar cada uma das funes e procedimentos da interface. Neste tutorial voc aprender sobre os elementos de linguagem de interfaces. Definindo Interfaces Assim como todas as classes do Delphi so implicitamente descendentes de TObject, todas as interfaces so implicitamente derivadas de uma interface chamada IUnknown. IUnknown definida na unit System como segue: type IUnknown = interface [{00000000-0000-0000-C000-000000000046}] function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end; Como voc pode ver, a sintaxe para definir uma interface muito similar quela de uma classe. A diferena principal que uma interface pode opcionalmente ser associada com um

66

Globally Unique Identifier (GUID), o qual nico para a interface. A definio de IUnknown vem da especificao de Component Object Model (COM) fornecida pela Microsoft. Definir uma interface customizada fcil se voc entender como criar se criam classes no Delphi. O cdigo a seguir define uma nova interface chamada IFoo, que implementa um mtodo chamado F1(): type IFoo = interface ['{2137BF60-AA33-11D0-A9BF-9A4537A42701}'] function F1: Integer; end; Dica: O IDE do Delphi produzir novos GUIDs para suas interfaces quando voc usar a combinao de teclas Ctrl+Shift+G. O cdigo seguinte define uma nova interface, IBar, que descende de IFoo: type IBar = interface(IFoo) [{2137BF61-AA33-11D0-A9BF-9A4537A42701}] function F2: Integer; end;

Implementando Interfaces O seguinte pedao de cdigo demonstra como implementar IFoo e IBar em uma classe chamada TFooBar: type TFooBar = class(TInterfacedObject, IFoo, IBar) function F1: Integer; function F2: Integer; end; function TFooBar.F1: Integer; begin Result := 0; end; function TFooBar.F2: Integer; begin Result := 0; end; Note que mltiplas interfaces podem ser listadas depois da classe ancestra na primeira linha da declarao da classe a fim de implementar mltiplas interfaces. A ligao de uma funo de interface a uma funo particular na classe acontece quando o compilador combina a

67

assinatura de um mtodo na interface com a assinatura compatvel na classe. Um erro de compilao ocorrer se uma classe declarar que ela implementa uma interface mas faltar com a implementao de um ou mais mtodos da interface. Se uma classe implementa mltiplas interfaces que tem mtodos de um mesmo nome, voc deve apelidar os mtodos com mesmo nome como mostrado no pequeno exemplo a seguir: type IFoo = interface ['{2137BF60-AA33-11D0-A9BF-9A4537A42701}'] function F1: Integer; end; IBar = interface [{2137BF61-AA33-11D0-A9BF-9A4537A42701}] function F1: Integer; end; TFooBar = class(TInterfacedObject, IFoo, IBar) // mtodos apelidados function IFoo.F1 = FooF1; function IBar.F1 = BarF1; // mtodos da interface function FooF1: Integer; function BarF1: Integer; end; function TFooBar.FooF1: Integer; begin Result := 0; end; function TFooBar.BarF1: Integer; begin Result := 0; end; A Diretiva implements Novo para o Delphi 4 a diretiva implements, que permite que voc delegue a implementao dos mtodos da interface para uma outra classe ou interface. Esta tcnica algumas vezes chamada de implementao por delegao. Implements usada como a ltima diretiva sobre uma propriedade da classe ou da interface como a seguir: type TSomeClass = class(TInterfacedObject, IFoo) // enchimento function GetFoo: TFoo; property Foo: TFoo read GetFoo imlements IFoo; // enchimento end;

68

O uso de implements no cdigo exemplo anterior instrui o compilador a procurar na propriedade Foo por mtodos que implementem a interface IFoo. O tipo da propriedade deve ser uma classe que contm mtodos de IFoo ou uma interface do tipo IFoo ou um descendente de IFoo. Voc podem tambm fornecer uma lista delimitada por vrgulas de interfaces seguindo a diretiva implements, na qual cada tipo da propriedade deve conter os mtodos para implementar as mltiplas interfaces. A diretiva implements traz duas vantagens importantes em seu desenvolvimento. Primeiro, ela permite que voc efetue agrupamentos em uma maneira no tumultuada. Agrupamento um conceito de COM relacionado a combinao de mltiplas classes para um propsito nico. Segundo, ela permite que voc adie o consumo de recursos necessrios para implementar uma interface at que eles sejam absolutamente necessrios. Por exemplo, digamos que houvesse uma interface cuja implementao requer a alocao de um bitmap de 1MB, mas aquela interface raramente requerida pelos clientes. Voc provalvelmente no gostaria de implementar aquela interface o tempo todo s por garantia porque isto poderia ser um desperdcio de recursos. Usando implements, voc poderia criar a classe para implementar a interface sob demanda no mtodo que acessa a propriedade. Utilizando Interfaces Umas poucas regras importantes da linguagem aplicam-se quando voc est usando variveis de tipos de interface em suas aplicaes. A principal regra a lembrar que interfaces so tipos autosuficientes. Isto significa que elas so sempre inicializadas com nil, elas so referenciadas, uma referncia automaticamente adicionada quando voc obtm uma interface, e elas so automaticamente liberadas quando saem do escopo ou quando recebem o valor nil. O cdigo exemplo a seguir ilustra o gerenciamento de memria de uma varivel de interface: var I: ISomeInterface; begin // I inicializado com nil I := FunctionReturningAnInterface; // contador de referncia de I // incrementado I.SomeFunc; // contador de referencia de I decrementado. //Se 0, automaticamente liberado end; Uma outra regra nica de variveis de interface que uma interface compatvel de atribuio com classes que implementam a interface. Por exemplo, o cdigo a seguir legal usando a classe TFooBar definida anteriormente: procedure Test(FB: TFooBar) var F: IFoo; begin F := FB; // legal porque FB suporta IFoo . .

69

. Finalmente, o operador de typecas pode ser aproveitado para QueryInterface de uma dada varivel de interface para uma outra interface. Isto ilustrado a seguir: var FB: TFooBar; F: IFoo; B: IBar; begin FB := TFooBar.Create F := FB; // legal porque FB suporta IFoo B := F as IBar; // QueryInterface F para IBar . . . Se a interface requerida no suportada, uma exceo ser gerada.

Tratamento Estruturado de Excees Tratamento estruturado de excees (Structured exception handling -SEH) um mtodo de tratamento de erros que permite que sua aplicao se recupere elegantemente de qualquer forma de condies de erros fatais. No Delphi 1.0, excees foram implementadas na linguagem Object Pascal, mas a partir do Delphi 2.0, excees so uma parte da API Win32. O que torna as excees em Object Pascal fceis de se usar que elas so como classes que contm informao sobre o local e a natureza de um erro particular. Isto faz com que excees sejam to fceis de implementar e usar em suas aplicaes quanto qualquer outra classe. Delphi contm excees predefinidas para condies de erro comuns, tais como falta de memria, diviso por zero, overflow e underflow numrico e erros de E/S de arquivos. Delphi tambm permite que voc defina suas prprias classes de excees da forma que voc achar conveniente em suas aplicaes. A Listagem 2.3 demonstra como usar o tratamento de excees durante E/S de arquivos. Listagem 2.3. E/S de arquivos usando tratamento de excees. Program FileIO; uses Classes, Dialogs; {$APPTYPE CONSOLE} var F: TextFile; S: string; begin AssignFile(F, 'FOO.TXT'); try Reset(F); try ReadLn(F, S); finally CloseFile(F); end;

70

except on EInOutError do ShowMessage('Erro de Acesso ao Arquivo!'); end; end. Na Listagem 2.3, o bloco try..finally interno usado para garantir que o arquivo fechado no importando se ocorrer uma exceo ou no. O que este bloco significa em Portugus "Ei, programa, tente executar as instrues entre try e finally. Se voc finaliz-los ou esbarrar com uma exceo, execute as instrues entre o finally e o end. Se uma exceo ocorrer, v adiante para o prximo bloco de tratamento de excees". Isto significa que o arquivo ser fechado e o erro poder ser tratado adequadamente no importando que erros ocorrerem. Nota: As instrues depois do bloco try..finally executam sem se importar se uma exceo ocorreu ou no. Esteja certo de que o cdigo no seu bloco finally no assume que uma exceo ocorreu. E o fluxo de execuo de seu programa continuar at o prximo manipulador de exceo. O outro bloco try..except usado para tratar as excees a medida que elas ocorrerem no programa. Depois que o arquivo fechado no bloco finally, o bloco except prope uma mensagem informando o usurio que um erro de E/S foi encontrado. Uma das principais vantagens que tratamento de excees fornece a mais que o mtodo tradicional de manipulao de excees a capacidade de separar distintamente o cdigo de deteco do erro do cdigo de correo do erro. Isto uma coisa boa principalmente porque ele torna seu cdigo mais fcil de ler e manter permitindo que voc concentre sobre um aspecto distindo do cdigo de cada vez. O fato de que voc no pode capturar qualquer exceo usando o bloco try..finally importante. Quando voc usa um bloco try..finally em seu cdigo, isso significa que voc no se preocupa com quais excees podem ocorrer. Voc apenas efetua algumas tarefas quando eles ocorrem saindo elegantemente de uma situao apertada. O bloco finally um lugar ideal para liberar qualquer recurso que voc alocou (tais como arquivos ou recursos do Windows), porque eles sempre so prejudicados no caso de um erro. Em muitos casos, contudo, voc necessita de algum tipo de tratamento de erro que capaz de responder diferentemente dependendo do tipo de erro que ocorre. Voc pode capturar excees especficas usando o bloco try..except, o qual ilustrado novamente na Listagem 2.4. Listagem 2.4. Um bloco de tratamento de excees try..except. Program HandleIt; {$APPTYPE CONSOLE} var R1, R2: Double; begin while True do begin try Write('Entre com um nmero real: '); ReadLn(R1); Write('Entre com outro nmero real: '); ReadLn(R2); Writeln('Eu agora dividirei o primeiro nmero pelo segundo...'); On EInOutError do

71

Writeln('Este no um nmero vlido!'); end; end; end. Embora voc possa capturar excees especficas com o bloco try..except, voc tambm pode pegar outras excees adicionando a clusula pega-tudo else para esta construo. A sintaxe da construo try..except..else a seguinte: try Instrues except On EAlgumaException do AlgumaCoisa; else { faz algum tratamento default } end; Cuidado: Quando usar a construo try..except..else, voc deve estar ciente de que a parte else capturar todas as exceesmesmo as excees que voc poderia no estar esperando, tais como falta de memria ou outra exceo de tempo de execuo. Tenha cuidado quando usar a clusula else, e use a clusula de forma econmica. Voc deve sempre causar novamente (reconstruir) a exceo quando voc a capturar com um manipulador de exceo no qualificado. Isto explicado na seo "Reconstruindo uma Exceo". Voc pode alcanar o mesmo efeito da construo try..except..else no especificando a classe de exceo no bloco try..except, como mostrado neste exemplo: try Instrues except ManipulaExceo // quase o mesmo que a instruo else end; Classes de Exceo Excees so simplesmente instncias especiais de objetos. Esses objetos so instanciados quando uma exceo ocorre e so destrudos quando uma exceo tratada. O objeto de exceo base chamado Exception, e este objeto definido como segue: type Exception = class(TObject) private FMessage: string; FHelpContext: Integer; public constructor Create(const Msg: string); constructor CreateFmt(const Msg: string; const Args: array of const); constructor CreateRes(Ident: Integer); constructor CreateResFmt(Ident: Integer; const Args: array of const); constructor CreateHelp(const Msg: string; AHelpContext: Integer); constructor CreateFmtHelp(const Msg: string; const Args: array of const; AHelpContext: Integer); constructor CreateResHelp(Ident: Integer; AHelpContext: Integer); constructor CreateResFmtHelp(Ident: Integer; const Args: array of const; AHelpContext: Integer); property HelpContext: Integer read FHelpContext write FHelpContext; property Message: string read FMessage write FMessage; end; O elemento importante do objeto Exception a propriedade Message, que uma string. Message fornece mais informao ou explicao sobre a exceo. A informao fornecida por Message depende do tipo de exceo que ocorreu.

72

Quando voc trata um tipo especfico de exceo em um bloco except, aquele manipulador tambm capturar qualquer exceo que forem descendentes da exceo especificada. Por exemplo, EMathError o objeto ancestral para uma variedade de excees relacionadas matemtica, tal como EZeroDivide e EOverflow. Voc pode capturar qualquer uma dessas excees colocando um manipulador para EMathError como mostrado aqui: try Instrues except on EMathError do // capturar EMathError ou qualquer descendente TrataExceo end; Cuidado: Se voc definir seu prprio objeto de exceo, esteja certo de que voc derivou-o de um objeto de exceo conhecido tal como Exception ou um de seus descendentes. A razo para isto que assim os manipuladores de exceo genricos sero capazes de capturar sua exceo. Qualquer exceo que voc no manipular explicitamente em seu programa eventualmente ocorrer, e ser tratada pelo manipulador default localizado dentro da biblioteca de tempo de execuo (runtime library) do Delphi . O manipulador default ir propor uma mensagem informando o usurio que uma exceo ocorreu. Quando se manipula uma exceo, voc algumas vezes necessita acessar a instncia do objeto de exceo a fim de encontrar mais informao sobre a exceo, tal como o que fornecido pela sua propriedade Message. Existem duas maneiras de fazer isso: usar um identificador opcional com o construtor on ESomeException, ou usar a funo ExceptObject(). Voc pode inserir um identificador opcional na poro on ESomeException de um bloco except e ter a descrio do identificador para uma instncia da exceo causada atualmente. A sintaxe para isto introduzir o tipo da exceo com um identificador e uma vrgula, como segue: try Algo except on E:ESomeException do ShowMessage(E.Message); end; Neste caso, o identificador (E neste caso) torna-se a instncia da exceo causada atualmente. Este idenficador sempre do mesmo tipo da exceo que ele introduz. Voc pode tambm usar a funo ExceptObject(), que retorna uma instncia da exceo causada atualmente. A desvantagem para ExceptObject(), contudo, que ele retorna um TObject que voc deve ento converter para o objeto exceo de sua preferncia. O exemplo seguinte mostra o uso desta funo: try AlgumaCoisa except

73

on ESomeException do ShowMessage(ESomeException(ExceptObject).Message); end; A funo ExceptObject() retornar Nil se no houverem excees ativas. A sintaxe para provocar uma exceo similar a sintaxe para criar uma instncia de objeto. Para provocar uma exceo definida pelo usurio chamada EBadStuff, por exemplo, voc deve usar esta sintaxe: Raise EBadStuff.Create('Algum mau negcio aconteceu.'); Sequncia de Execuo Depois que uma exceo ocorre, a sequncia de execuo de seu programa propaga at o prximo manipulador de exceo at que a instncia de exceo for finalmente manipulada e destruda. Este processo determinado por chamada a pilha, ento age sobre todo a extenso do programa (e no apenas dentro de um procedimento ou de uma unit). A Listagem 2.5 ilustra a sequncia de execuo de um programa quando uma exceo ocorre. Esta listagem a unit principal de uma aplicao do Delphi que consiste de um form com um boto sobre o form. Quando o boto clicado, o mtodo Button1Click() chama Proc1(), que chama Proc2(), que em fila chama Proc3(). Uma exceo ocorre em Proc3(), e voc pode ver a sequncia de execuo propagar atravs de cada bloco try..finally at que a exceo finalmente manipulada dentro de Button1Click(). Dica: Quando voc roda seu este programa da IDE do Delphi, voc ser capaz de ver a sequncia de execuo melhor se desabilitar o manipulador de excees integrado do debugger desmarcando Tools | Debugger Options | Preferences | Break on Exception. Listagem 2.5. Unit principal para o projeto de propagao de exceo. unit Principal; interface uses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM}

74

type EBadStuff = class(Exception); procedure Proc3; begin try raise EBadStuff.Create('Vamos para o topo da pilha!'); finally ShowMessage('Exceo ocorrida. Proc3 v a exceo'); end; end; procedure Proc2; begin try Proc3; finally ShowMessage('Proc2 v a exceo'); end; end; procedure Proc1; begin try Proc2; finally ShowMessage('Proc1 v a exceo'); end; end; procedure TForm1.Button1Click(Sender: TObject); const ExceptMsg = 'Exceo manipulada na chamada do procedimento. A mensagem "%s"'; begin ShowMessage('Este mtodo chama Proc1 que chama Proc2 que chama Proc3'); try Proc1; except on E:EBadStuff do ShowMessage(Format(ExceptMsg, [E.Message])); end; end; end. Reconstruindo uma Exceo Quando voc precisa efetuar tratamento especial para uma instruo dentro de um bloco try..except existente e ainda permitir que a exceo para o manipulador default exterior ao

75

bloco, use a tcnica chamada reconstruo de exceo. A Listagem 2.6 demonstra um exemplo de reconstruo de uma exceo. Listagem 2.6. Reconstruindo uma exceo. try // este o bloco externo { instrues } { instrues } ( instrues } try // este o bloco interno especial { alguma instruo que pode requerir tratamento especial } except on ESomeException do begin { tratamento especial para a instruo do bloco inter raise; // reconstri a exceo para o bloco exter end; end; except // bloco externo sempre efetuar tratamento default on ESomeException do AlgumaCoisa; end;

Runtime Type Information Runtime Type Information (RTTI) uma caracterstica da linguagem que d a uma aplicao Delphi a capacidade de encontrar informao sobre seus objetos em tempo de execuo. RTTI tambm a chave para a ligao entre componentes do Delphi e sua incorporao dentro da IDE do Delphi, mas no apenas um processo acadmico que ocorre nas sombras do IDE. Objetos, em virtude de serem descendentes de TObject, contm um apontador para sua RTTI e tem muitos mtodos embutidos que permitem que voc obtenha alguma informao til dentro da RTTI. A tabela a seguir lista alguns dos mtodos de TObject que usam RTTI encontrar informao sobre uma instncia particular do objeto. Funo ClassName() ClassType() InheritsFrom() ClassParent() InstanceSize() ClassInfo() Tipo Retornado string TClass Boolean TClass word Pointer Retorno O nome da classe do objeto O tipo do objeto Booleano para indicar se a classe descende de uma classe dada O tipo do ancestral do objeto O tamanho em bytes de uma instncia Um apontador para a RTTI do objeto na memria

Object Pascal fornece dois operadores, is e as, que permitem comparaes e typecast de objetos via RTTI. A palavra reservada as uma nova forma de typecast de tipo protegido. Ela permite que voc molde um objeto de baixo nvel para um descendente e causar uma exceo se o typecast for invlido. Suponha que voc tenha um procedimento para o qual voc quer ser capaz de passar qualquer tipo de objeto. Esta definio de funo poderia ser definida como Procedure Foo(UmObjeto: TObject); Se voc quiser fazer algo til com UmObjeto depois neste procedimento, voc provavelmente ter que mold-lo para um objeto descendente. Suponha que voc queira

76

assumir que UmObjeto um descendente de TEdit, e voc quer mudar o texto que ele contm (um TEdit um controle de edio da VCL do Delphi). Voc pode usar o seguinte cdigo: (Foo as TEdit).Text := 'Hello World.'; Voc pode usar o operador de comparao Booleana is para checar se dois objetos so de tipos compatveis. Use o operador is para comparar um objeto desconhecido a um tipo ou instncia conhecida para determinar quais propriedades e comportamentos voc pode assumir sobre o objeto desconhecido. Por exemplo, voc poderia querer checar para ver se UmObjeto compatvel com TEdit antes de tentar fazer um typecast nele: If (Foo is TEdit) then TEdit(Foo).Text := 'Hello World.'; Note que voc no usou o operador as para efetuar o typecast neste exemplo. Isto porque uma certo custo elevado envolvido no uso de RTTI, e porque a primeira linha j determinou que Foo um TEdit, voc pode otimizar efetuando um typecast de apontador na segunda linha.

Resumo Muita coisa foi coberta neste tutorial. Voc aprendeu a sintaxe bsica e a semntica da linguagem Object Pascal, incluindo variveis, operadores, funes, procedimentos, tipos, construtores e estilo. Voc deveria ter um claro entendimento de POO, objetos, campos, propriedades, mtodos, TObject, interfaces, tratamento de excees e RTTI. Este texto foi adaptado de Delphi User's Guide

You might also like