Ling Object Pascal

download Ling Object Pascal

of 76

Transcript of Ling Object Pascal

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.

2

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:

3

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

4

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!';

5

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".

6

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:

7

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.

8

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 >. 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.

9

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

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 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 s