1

CARACTERÍSTICAS E RECURSOS DA LINGUAGEM OBJECT PASCAL. AGOSTO/2002 • • • • • • • • • • • • • • • • • • • COMENTÁRIOS NOVAS CARACTERÍSTICAS DE PROCEDIMENTOS E FUNÇÕES VARIÁVEIS CONSTANTES OPERADORES TIPOS DO OBJECT PASCAL TIPOS DEFINIDOS PELO USUÁRIO TYPECASTING E CONVERSÃO DE TIPOS RESOURCES TESTANDO CONDIÇÕES LOOPS PROCEDIMENTOS E FUNÇÕES ESCOPO UNITS PACKAGES PROGRAMAÇÃO ORIENTADA A OBJETOS USANDO OS OBJETOS DO DELPHI TRATAMENTO ESTRUTURADO DE EXCEÇÕES RUNTIME TYPE INFORMATION

-------------------------------------------------------------------------------Para começar, você receberá uma introdução aos fundamentos da linguagem Object Pascal tal como regras da linguagem e construções. Mais tarde, você aprenderá sobre alguns dos mais avançados aspectos de Object Pascal tais como classes e tratamento de exceções. Por não ser um tutorial para iniciantes, assumimos que você já tem alguma experiência com outra linguagem de programação de alto nível 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 programação tais como variáveis, tipos, operadores, loops, casos, exceções e objetos funcionam no Pascal. Comentários Como ponto de partida, você verá como fazer comentários no seu código Pascal. Object Pascal suporta três tipos de comentários: comentários com chaves, comentários com parênteses/asterisco; e comentários com barras duplas no estilo do C++. Abaixo, exemplos dos três tipos de comentários: { Comment using curly braces } (* Comment using paren and asterisk *) // C++-style comment Os dois tipos de comentários do Pascal são praticamente idênticos no comportamento. O compilador considera o comentário como sendo tudo entre os delimitadores de abertura e fechamento de comentários. Para comentários no estilo do C++, tudo que segue as barras duplas até o fim da linha é considerado um comentário.

2

Novas Características de Procedimentos e Funções Parênteses Uma das pequenas características do Object Pascal é que parênteses são opcionais quando chamamos um procedimento ou função que não pega parâmetros. Assim, os seguintes exemplos de sintaxe são ambos válidos: Form1.Show; Form1.Show(); Esta não é uma das coisas com as quais você deve esquentar a cabeça, mas é particularmente bom para aqueles que dividem seu tempo entre Delphi e linguagens como C++ ou Java, em que parênteses são requeridos. Se você não gasta 100% do seu tempo com Delphi, esta característica significa que você não tem que lembrar de usar diferentes sintaxes de chamada de funções para linguagens diferentes. Sobrecarga Delphi 4 introduz o conceito de sobrecarga de função, isto é, a capacidade de ter múltiplos procedimentos ou funções de mesmo nome com lista de parâmetros diferentes. Todos os métodos sobrecarregados não 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 métodos de uma classe são levemente diferentes e são explicados na seção Sobrecarga de Métodos. Parâmetros de Valor Default Também novo para Delphi 4 são os parâmetros de valor default, isto é, a habilidade de fornecer um valor default para uma função ou procedimento e não ter que passar aquele parâmetro quando chamar a rotina. Para declarar um procedimento ou função que contém parâmetros de valor default, siga o tipo do parâmetro 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 parâmetros: TemValDef('ola', 26); Segundo, você pode especificar apenas o parâmetro S e usar o valor default para I: TemValDef('ola'); // valor default usado para I Você deve seguir várias regras quando usar parâmetros de valor default:

3

Parâmetro que tem valor default devem aparecer no fim da lista de parâmetros. Parâmetros sem valores default não podem seguir parâmetros com valores default em uma lista de parâmetros de uma função ou procedimento. Parâmetros de valor default devem ser de um tipo ordinal, ponteiro ou set. Parâmetros de valor default devem ser passados por valor ou como constante. Eles não devem ser parâmetros de referência, out ou sem tipo. Um dos maiores benefícios dos parâmetros de valor default é adicionar funcionalidade a funções e procedimentos existentes sem sacrificar compatibilidades anteriores. Por exemplo, suponha que você venda uma unidade que contenha uma função revolucionária chamada AddInts() que some dois números: function AddInts(I1, I2: Integer): Integer; begin Result := I1 + I2; end; Para fugir da concorrência, você sente que deve atualizar esta função de forma que ela tenha a capacidade de somar três números. Contudo, você está detestanto fazer isso porque adicionar um parâmetro fará com que códigos existentes que chamam esta função não compilem. Graças aos parâmetros 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; Variáveis Você pode ter o costume de declarar variáveis instintivamente: "Eu preciso de um outro inteiro, então é só declarar um bem aqui no meio deste bloco de código". Se esta tem sido a sua prática, você vai ter que se retreinar um pouco para usar variáveis em Object Pascal. Object Pascal requer que você declare todas as variáveis no início de sua própria seção antes de começar um procedimento, função ou programa. Talvez você escreva códigos como este: void foo(void) { int x = 1; x++; int y = 2; float f; //... etc ... } Em Object Pascal, tal código deve ser ajeitado e estruturado um pouco mais para se paracer com este: Procedure Foo; var x, y: Integer; f: Double; begin

Exemplos demonstrando a sintaxe para fazer isso são mostrados a seguir: var i: Integer = 10. const int i = 10. todo tipo inteiro possui 0. Perigo'. assim como Visual Basic.141579. Danger!'. que comportam-se similarmente a palavra reservada const do C.0 permite que você inicialize variáveis globais dentro de um bloco var.14.0. Note como Object Pascal permite a você agrupar mais do que uma variável do mesmo tipo juntamente na mesma linha com a seguinte sintaxe: Var1. S: string = 'Olá mundo'. y := 2. Dica: O compilador Delphi entende que todos os dados globais são automaticamente inicializados com zero. Perigo. const char * ErrorString = 'Perigo. Lembre que quando você está declarando uma variável em Object Pascal. Danger. inc(x). Aqui está um exemplo de três declarações de constantes em C: const float ANumeroDecimal = 3.. e há uma vírgula entre as variáveis e tipos. . Note que a inicialização da variável é sempre separada da declaração da variável. Quando sua aplicação é iniciada. end. Nota: Pré-inicialização de variáveis é apenas permitida para variáveis globais e não para variáveis locais a um procedimento ou função. D: Double = 3. Var2 : AlgumTipo. o compilador fiscaliza os valores que ele manipula. ErrorString = 'Danger. e espaços nunca são alocados: const ADecimalNumber = 3... Constantes Constantes em Pascal são definidas na cláusula const. dados globais em seu código fonte.14.. não requer que você declare o tipo constante junto com o valor na declaração. não é necessário inicializar com zero. strings estarão vazias. ou. etc . //. ponteiros serão nil. O compilador do Delphi automaticamente aloca espaço apropriado para a constante baseado nos seus valores. e assim em diante. Uma característica da linguagem introduzida no Delphi 2. no caso de constantes escalares tais como Inteiros. i = 10. tipos ponto-flutuante possuem 0. A maior diferença entre constantes do C e do Object Pascal é que Object Pascal. o nome da variável precede o tipo. Então.4 x := 1.

5 Nota: Espaço é alocado para constantes como segue: Valores Inteiros são ajustados dentro do menor tipo alocável (10 dentro de um ShortInt. Valores de ponto-flutuante são mapeados para o tipo de dado extendido. esse dispositivo está habilitado para compatibilidade com código do Delphi 1. a menos que o valor contenha 4 ou menos casas decimais explicidamente. e SizeOf(). ou então você pode usar a diretiva de compilação $J. Object Pascal otimiza seu espaço de dados armazenando aquelas constantes que merecem armazenagem nessas páginas de código da aplicação. Low(). Pelo fato das constantes serem somente-leitura. você pode também especificar um tipo de constante na declaração. B2: Byte = Low(A).0 16-bit.71828).0. const w: Word = SizeOf(Byte). x: ShortInt = Round(2. Se você tentar alterar o valor de qualquer dessas constantes.0. e assim em diante). Trunc(). veja "A API Win32".14. Contudo. No Delphi 1. Por exemplo. C: char = Chr(46). a partir do Delphi 2. Danger!'. mas é melhor você não confiar nessa capacidade. High(). Object Pascal permite o uso de funções em tempo de compilação em declarações const e var. L: Longint = Trunc(3.14159). o identificador declarado não era tratado como uma constante mas como uma variável pré-inicializada chamada de typed constant . Isto fornece a você controle sobre como o compilador trata suas constantes: const ADecimalNumber: Double = 3. j: SmallInt = Ord(‘a’). var i: Integer = 8. o compilador Delphi emite um erro explicando que é contra as regras alterar o valor de uma constante. Chr(). Se para você não está claro as noções de páginas de código e dados. ErrorString: string = 'Danger. todos os códigos seguintes são válidos: type A = array[1. Danger. B1: Byte = High(A). Cuidado: O comportamento das constantes de tipo especificado do Delphi 32-bit é diferente daquele no Delphi 1. Round().2] of Integer. Delphi fornece uma compatibilidade às versões anteriores ao acionar a página Compiler do diálogo Project | Options. Conjuntos de inteiros e char são claramente armazenados neles mesmos. Por default. I: Integer = 10. 32000 dentro de um SmallInt. neste caso ele é mapeado para um tipo Comp. Valores alfanuméricos são colocados em char ou no tipo string definido atualmente (por $H).. constantes de tipo especificado tem a capacidade de serem verdadeiramente constante. . Opcionalmente. Essas rotinas incluem: Ord().

Não há o conceito de uma macro em Object Pascal e. e o operador = compara os valores de dois operandos. você não pode usá-la para definir constantes. multiplicar e dividir dados numéricos. Para determinar se duas expressões não são iguais. Há também operadores para endereçar um elemento particular de um array. há operadores para somar. Use const em Object Pascal onde você usaria #define para declarar uma constate em C ou C++. use o operador := como você usaria o operador = do C ou do Visual Basic. Embora você possa usar a diretiva de compilação $define do Object Pascal para compilação condicional similarmente a diretiva #define do C. Por exemplo. Operadores Operadores são os símbolos no seu código que habilitam você a manipular todos os tipos de dados. assim. Esta seção explica alguns dos operadores do Pascal e descreve algumas das diferenças entre as suas contrapartes no C e no Visual Basic. O operador = do Object Pascal é análogo ao operador == do C. O não-igual-a do Delphi é <>. Exemplo: Number1 := 5. assim uma expressão C que seria escrita como: if (x == y) Será escrita dessa forma no Object Pascal: if x = y Note: Lembre que em Object Pascal. você deverá se sentir confortável com os operadores de comparação do Delphi porque eles são praticamente idênticos. Operadores de Comparação Se você já programou em Visual Basic. e seu propósito é idêntico ao operador != do C. o operador de atribuição do Delphi será uma das coisas mais duras para se acostumar. Operadores de Atribuição Se você é iniciante em Pascal. Object Pascal usa o operador = para executar comparações lógicas entre duas expressões ou valores. o operador := é usado para atribuir um valor a uma variável. Esses operadores são claramente um padrão em todas as linguagens de programação. nenhum equivalente para #define do C para declaração de constantes. use este código: . Para atribuir um valor a uma variável. subtrair.6 Nota: Object Pascal não tem um pré-processador como C e C++ tem. então eles serão vistos brevemente nesta seção.

enquanto que C usa os símbolos && e || .1. tais como os dois exemplos seguintes: if (Condição 1) and (Condição 2) then FazerAlgo. que é usado para checar a comparação para uma condição falsa. -------------------------------------------------------------------------------Operador Pascal C Visual Basic -------------------------------------------------------------------------------Atribuição Comparação Não igual a Menor que Maior que Menor ou igual a Maior ou igual a E lógico OU Lógico Não Lógico := = <> < > <= >= and or not = == != < > <= >= && || ! = = <> < > <= >= and or not -------------------------------------------------------------------------------Operadores Aritméticos Você já deve estar familiarizado com muitos dos operadores aritméticos do Object Pascal porque eles geralmente são similares àqueles usados em C.2 ilustra todos os operadores aritméticos dos Pascal e suas contrapartes em C e Visual Basic. comparação e operadores lógicos.1 fornece uma fácil referência de como os operadores do Pascal mapeiam os operadores correspondentes do C e do Visual Basic. Tabela 2... O uso mais comum dos operadores and e or é como parte de uma expressão if ou loops. A Tabela 2. C++ e Visual Basic. . A Tabela 2. O operador não lógico do Pascal é not. if not (condição) then (fazer algo). respectivamente. while (Condição 1) or (Condição 2) do FazerAlgo. Atribuição.7 if x <> y then FazerAlgo Operadores Lógicos Pascal usa as palavras and e or como operadores e e ou lógicos. É também usado freqüentemente como parte de uma expressão if: // Se condição é falsa então. É análogo ao operador ! do C. para esses operadores.

8 Tabela 2. .3. Os demais operadores bit-a-bit do Pascal são fáceis de se lembrar: and. not. O operador div do Pascal é mais rápido e mais específico. respectivamente. como os seguintes códigos ilustrativos: var i: Integer. Em vez disso.4 div 2. Muitas outras linguagens de programação não distinguem entre as divisões inteiras e de ponto-flutuante. A Tabela 2. Operadores Aritméticos. ou e ou exclusivo (xor) bit-a-bit com dois números. end. elas sempre efetuam divisão de ponto-flutuante e então convertem o resultado para um inteiro quando necessário. Operadores Bit-a-Bit Operadores Bit-a-bit são operadores que permitem que você modifique bits individuais de uma determinada variável. // Esta linha também causará um erro f := 3. O compilador Object Pascal dá a você um erro se você tentar dividir dois números de ponto-flutuante com o operador de inteiros div ou dois inteiros com o operador de ponto-flutuante / .2. -------------------------------------------------------------------------------Operador Pascal C Visual Basic -------------------------------------------------------------------------------Adição + + + Subtração Multiplicação * * * Divisão de ponto-flutuante / / / Divisão inteira div / / Módulo mod % Mod -------------------------------------------------------------------------------Você pode notar que a principal diferença entre Pascal e outras linguagens é que Pascal tem diferentes operadores de divisão para a matemática de inteiros e de pontos-flutuante. r: Real. Os operadores Shift+left e Shift+right são shl e shr. O operador div trunca automaticamente qualquer resto quando divide duas expressões inteiras. begin // Esta linha causará um erro de compilação i := 4 / 3. Os operadores bit-a-bit comuns permitem que você desloque para a esquerda. para a direita ou efetuar as operações e. e eles são no C os operadores << e >>. Nota: Lembre-se de usar o operador de divisão correto para os tipos de expressões com os quais você está trabalhando. não. e xor.3 lista os operadores bit-a-bit. or. Isto pode ser um pouco caro em termos de performance.

usando as instruções assembly inc e dec: Inc(variable). mas os procedimentos Inc() e Dec() do Pascal compilam de forma mais otimizada para uma instrução de máquina. de um. Compare as duas linhas seguintes.do C.3. assim use qualquer um que você sentir mais confortável para incrementar e decrementar variáveis.9 Tabela 2. as duas linhas seguintes de código incrementam e decrementam variável. os procedimentos Inc() e Dec() freqüentemente produzem o mesmo código de máquina que a sintaxe variavel := variavel + 1 .4 compara os operadores de incremento e decremento de diferentes linguagens. que incrementam ou decrementam variável de 3 usando as instruções assembly add e sub: Inc(variable. -------------------------------------------------------------------------------Operador Pascal C Visual Basic -------------------------------------------------------------------------------Incremento Inc() ++ Nenhum Decremento Dec() -Nenhum -------------------------------------------------------------------------------- . 3). Você pode chamar Inc() ou Dec() com um ou dois parâmetros. Pascal na verdade não fornece operadores de incremento e decremento similares aos operadores ++ e -. Note: Com a otimização de compilação habilitada. Por exemplo. 3). Dec(variable). Dec(variable. Operadores de incremento e decremento. respectivamente. A Tabela 2. Tabela 2.4. Operadores bit-a-bit -------------------------------------------------------------------------------Operador Pascal C Visual Basic -------------------------------------------------------------------------------E and & And Não 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 código otimizado para somar um ou subtrair um de uma determinada variável inteira.

Basicamente.5. Você pode querer imprimir esta tabela porque ela fornece uma excelente referência para combinar tipos quando chamar funções em Dynamic Link Libraries (DLLs) que não sejam do Delphi ou arquivos objetos (OBJs) do Delphi ou vice-versa. BOOL Nenhum . Nenhum unsigned short inteiro sinalizado 16-bit SmallInt short Short inteiro não-sinalizado 16-bit Word unsigned short Nenhum inteiro sinalizado 32-bit Integer.5 compara e constata os tipos básicos de Object Pascal com aqueles de C/C++ e Visual Basic. Isto porque o compilador Object Pascal não permite que você chame uma função com um tipo de ponteiro quando um outro tipo é especificado nos parâmetros formais da função (ainda que funções que tomem tipos Pointer não tipados aceitem qualquer tipo de ponteiro). ou typesafe. Você não verá nenhum compilador famoso alertando sobre conversões de ponteiro suspeitas que os programadores de C estão acostumados. A Tabela 2. Uma comparação dos tipos de 32-bits Pascal-para-C/C++-para-Visual Basic. Variant (Default) OleVariant. 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.para garantir que você não esteja tentando por algo em um lugar errado.10 Tipos do Object Pascal Uma das grandes características do Object Pascal é que ele é fortemente tipado. Isto significa que variáveis atuais passadas para procedimentos ou funções devem ser do mesmo tipo que o parâmetro formal identificado na definição do procedimento ou função. Type of Variable Pascal C/C++ Visual Basic inteiro sinalizado de 8-bit ShortInt char Nenhum inteiro não-sinalizado de 8-bit Byte BYTE. long Integer Longint inteiro não-sinalizado 32-bit Cardinal. Table 2. Variant †. Caracter de 1-byte Char char Nenhum Caracter de 2-byte WideChar WCHAR String de tamanho em bytes fixo ShortString Nenhum Nenhum String dinâmica AnsiString AnsiString† $ String terminada com Null PChar char * Nenhum Wide String terminada com Null PWideChar LPCWSTR Nenhum String dinâmica 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. a natureza fortemente tipada do Pascal permite que ele efetue uma verificação de sanidade do seu código . TVarData OleVariant†. Comparação de Tipos Os tipos base do Delphi são similares àqueles do C e do Visual Basic. int.

Cuidado: Nos Delphis 1. em bytes. No Delphi 4. · WideChar Este caracter é de 2 bytes de tamanho e representa um caracter Unicode. Tenha consciência de que pelo fato de um caracter não garantir ser de um byte de tamanho. mas agora ele é identificado por Real48. Real é um alias para o tipo Double. Pascal tem muitos tipos strings diferentes para satisfazer as necessidades do programador: . de um tipo ou instância. Um Grande Número de Strings Strings são tipos de variáveis usadas para representar grupos de caracteres. mas a Borland previne que a definição pode mudar nas próximas versões do Delphi para um WideChar. O antigo número de pontoflutuante de 6 bytes ainda existe.0 e 3 o tipo Cardinal foi tratado como um inteiro não sinalizado de 31-bit para preservar a precisão aritmética (porque Delphi 2 e 3 careciam de um verdadeiro inteiro não sinalizado de 32-bit). No Delphi 4. Nota: O procedimento padrão SizeOf() retorna o tamanho. 2 e 3 o identificador de tipo Real especificava um número de ponto-flutuante de 6 bytes.0. Cardinal é um verdadeiro inteiro não sinalizado de 32-bit. isto é pouco preciso: no Delphi 2. Na verdade. você deve usar a função SizeOf() onde for apropriado. Nota: Se você possui um código de 16-bit do Delphi 1. Você pode também forçar o identificador Real para referir ao número de ponto-flutuante de 6 bytes usando a diretiva {$REALCOMPATIBILITY ON}. Toda linguagem tem seu próprio jeito de como os tipos strings são armazenados e usados. Em vez disso. você não deve codificar o tamanho em suas aplicações.11 LongBool -------------------------------------------------------------------------------- † Classe do Borland C++Builder que emula o tipo correspondente em Object Pascal. esteja avisado de que o tamanho dos tipos Integer e Cardinal aumentou de 16 para 32 bits. que é um tipo único para o Pascal e geralmente incompatível com outras linguagens. Caracteres Delphi fornece três tipos de caracteres: · AnsiChar Este é o caracter padrão de 1 byte ANSI que programadores já estão acostumados. · Char Este é atualmente idêntico ao AnsiChar.

• PWideChar é um ponteiro para uma string de WideChar terminada em null. você pode induzir variáveis declaradas com strings ser do tipo ShortString usando a diretiva de compilação $H. AnsiString é isto e mais. • PAnsiChar é um ponteiro para uma string de AnsiChar terminada em null. // S1 é uma ShortString {$H+} S2: string. e quando o valor da diretiva é positivo (o default). • ShortString permanece na linguagem basicamente para manter a compatibidade com Delphi 1. • WideString é similar em funcionalidade a AnsiString exceto que é formada por caracteres WideChar. Embora AnsiStrings mantenha uma interface quase idêntica com seus predecessores. Object Pascal também gerencia automaticamente a alocação de strings temporárias quando necessário. Quando o valor da diretiva de compilação $H é negativo. quando você declara uma variável string no seu código como mostrado no exemplo seguinte. Sua capacidade é limitada a 255 caracteres. É também compatível com strings terminadas em null. // S2 é uma AnsiString A exceção à regra do $H é que uma string declarada com um tamanho explícito (limitado para o máximo de 255 caracteres) é sempre uma ShortString: var // Uma ShortString de no máximo 63 caracteres S: string[63].0.12 • AnsiString. Ele existe antes de mais nada como um resultado de pedido dos programadores do Delphi 1. • PChar é um ponteiro para uma string de Char terminada em null — como o char * do C ou os tipos lpstr. O Tipo AnsiString O AnsiString.0 por um tipo string fácil de usar sem a chata limitação de 255 caracteres. Por default. ou tipo “long string” foi introduzido na linguagem no Delphi 2. o tipo string default para Object Pascal. AnsiStrings são sempre terminadas com null. é compreendido de caracteres AnsiChar e permite tamanhos virtualmente ilimitados. variáveis string são AnsiStrings. o que as torna compatíveis com as strings terminadas com null usadas pela API do Win32. // S é uma AnsiString Alternativamente. . Adicionalmente. o compilador assume que você está criando um AnsiString: var S: string. variáveis string são ShortStrings. Assim você não precisa se preocupar em alocar buffers para resultados intermediários como você faz em C/C++. O tipo AnsiString é na verdade implementado como um ponteiro para uma estrutura string na memória heap. O código seguinte demonstra este comportamento: var {$H-} S1: string. eles são alocados dinamicamente e com garbage-collected (coleta de lixo).0.

Para variáveis globais. AnsiStrings são referências. então. Desta forma. WideString. OleVariant. o que significa que muitas strings podem apontar para a mesma memória física. É claro. // S2 agora referencia S1. '. este processo é claramente direto: como uma parte da finalização do código gerado por sua aplicação. Por exemplo. Delphi fornece muitos outros tipos que são dinâmicos.13 Cuidado: O formato interno completo de um tipo long string não é documentado pela Borland. Cópias de string. e dispinterface. e a contagem de referência de S1 é decrementado. vazia. Contagem de referência de S1 é 2.. begin // armazena string em S1. Você aprenderá mais sobre cada um desses tipos depois. ou com outro valor indicando que a variável "não está em uso". Variant. Desenvolvedores que escreveram código que dependia do formato interno (tal como o 0ésimo elemento na string sendo o tamanho) tiveram que modificar seu código para o Delphi 2. a contagem de referência de S1 é 1 S1 := 'E agora para algo. A informação aqui pretende principalmente ajudá-lo a entender como trabalham AnsiStrings. são tipos que consomem potencialmente algum recurso particular enquanto o usam e liberam esse recurso automaticamente quando ele sai do escopo.. Como todos os dados globais são inicializados com zero quando sua aplicação é carregada. Quando duas ou mais AnsiStrings dividem uma referência para a mesma string física. S2 := S2 + 'Completamente diferente!'. e você deve evitar ficar dependendo da estrutura de uma AnsiString no seu código. assim ele é copiado para seu próprio // espaço de memória. S2: string. // S2 é alterado. o gerenciador de memória do Delphi usa uma técnica de cópia-sobre-escrita. Tipos Dinâmicos Além de AnsiString. O exemplo seguinte ilustra esses conceitos: var S1. Tipos dinâmicos. que permite que ele espere até que uma string seja modificada para liberar uma referência e alocar uma nova string física. Desenvolvedores que migraram do Delphi 1 para o Delphi 2 e que evitaram a implementação de detalhes de string foram capazes de migrar seu código sem nenhum problema. Esses tipos incluem arrays dinâmicos. a finalização do código não se esforçará em liberar recursos a menos que eles sejam realmente usados na sua aplicação. . a variedade de recursos usados depende do tipo envolvido. interface. o compilador insere código para garantir que cada variável global dinâmica está limpa. e a Borland reserva-se o direito de mudar o formato interno de long strings nas distribuições futuras do Delphi. são muito mais rápidas porque ela meramente copia um ponteiro ao invés de copiar o conteúdo real da string. algumas vezes chamados de tipos com "coleta de lixo". uma AnsiString consome memória para uma string de caracteres enquanto os usa e a memória ocupada pela string de caracteres é liberada quando ela sai do escopo. S2 := S1. cada variável global dinâmica sempre inicializará contendo um zero.

o compilador gera um bloco de manipulação de exceção try. Com isto em mente. { usando Concat() } . begin S := ''. { Cookie Monstro } end. ele na verdade se parecerá com isto: procedure Foo. Embora este procedimento pareça simples. end. S := S + S2. try // corpo do procedimento // use S aqui finally // limpe S aqui end.14 Sempre que você declarar uma variável dinâmica local. O exemplo seguinte demonstra o uso de + e Concat(): { usando + } var S. considere o seguinte procedimento: procedure Foo. var S: string. o processo é levemente mais complexo: primeiro o compilador insere código para garantir que a variável é inicializada com zero quando a função ou procedimento inicia. se você levar em conta o código gerado pelo compilador por trás das cenas. S2: string begin S:= 'Cookie ': S2 := 'Monstro'. begin // corpo do procedimento // use S aqui end.finally para limpar a variável dinâmica (manipulação de exceções são explicados em mais detalhes na seção “Manipulação Estruturada de Exceções”).. var S: string. O melhor método de concatenação de string é o operador + porque a função Concat() existe primeiramente para manter a compatibilidade com versões anteriores. Operações sobre String Você pode concatenar duas strings usando o operador + ou a função Concat(). Em seguida.

Comprimento e Alocação Quando declaramos. S2 := 'Monstro'. S := Concat(S. // string inicialmente não tem tamanho begin // Aloca o mínimo de espaço suficiente // para a string literal S := 'Doh!'. por exemplo) que não tem uma definição em Object Pascal. o compilador fornece um caso especial para cada uma dessas funções e gera uma chamada para uma das funções de ajuda "mágicas do compilador" definidas na unit System. Para fazer com que espaço seja alocado para a string. begin S:= 'Cookie '. Essas funções de ajuda são geralmente implementadas na linguagem assembly para contornar as regras da linguagem Object Pascal. há uma variedade de funções e procedimentos na unit SysUtils destinadas a tornar o trabalho com strings mais fácil. S2: string.15 var S. Dica: Concat() é uma das muitas funções e procedimentos "mágicas do compilador" (como ReadLn() e WriteLn(). { Cookie Monstro } end. você pode atribuir à string um literal ou uma outra string. eles não podem ser definidos em termos da linguagem Object Pascal. Nota: Sempre use aspas simples (‘Uma String’) quando trabalhar com strings literais em Object Pascal. { ou } // adiciona a ref count da OutraString (assume que // OutraString já aponta para uma string válida) S := OutraString { ou } // aloca espaço suficiente para no mínimo // 4 caracteres. . como mostrado aqui: var S: string. Procure por “String-handling routines (Pascal-style)” no sistema de help online do Delphi. Como tais funções e procedimentos são destinados a aceitar um número indeterminado de parâmetros ou parâmetros opcionais. S2). Além dos procedimentos e funções "mágicas do compilador" de suporte à strings. Por causa disto. ou você pode usar o procedimento SetLength(). uma AnsiString não tem comprimento e portanto nenhum espaço alocado para caracteres na string.

Por exemplo. end. Isto possibilita chamar funções API Win32 ou outras funções que requerem strings do tipo PChar.16 SetLength(S. 256). Chamar RealizeLength() completa a substituição de uma long string por um PChar: . AnsiStrings são sempre finalizadas com null. begin SetLength(S. funciona adequadamente: var S: string. assim elas são compatíveis com strings finalizadas com null. que aceita um PChar e o tamanho do buffer como parâmetros: var S: string. o fragmento de código seguinte causa um erro: var S: string. A função RealizeLength(). 256). Este código. // chame a função. Você pode indexar os caracteres de uma AnsiString como um array. 1). Win32 Compatibility Como mencionado inicialmente. end. Tudo que é requerido é que você faça um typecast de string para PChar (typecasting é explicado em mais detalhes na seção "Typecasting e Conversão de Tipos"). mas tenha cuidado de não indexar além do tamanho da string. você deve manualmente setar o comprimento da variável string para seu tamanho terminado com null. que também vem da unit STRUTILS. end. 4). end. StrLen(PChar(S))). contudo. efetua essa tarefa: procedure RealizeLength(var S: string). begin SetLength(S. Depois de usar uma AnsiString onde a função ou procedimento espera um PChar. // Agora S tem espaço suficiente para guardar o caracter S[1] := 'a'. // Não funcionará porque S não foi alocado! end. begin S[1] := 'a'. begin // importante! obtenha tamanho da string primeiro SetLength(S. S agora guarda a // string do diretório GetWindowsDirectory(PChar(S). O código seguinte demonstra como chamar a função Win32 GetWindowsDirectory().

lembre que o valor da diretiva $H determina se variáveis declaradas com string são tratadas pelo compilador como AnsiString ou ShortString.'. Assim como com AnsiStrings. você reconhecerá o tipo ShortString como o tipo string do Delphi 1.'. trabalhar com ShortString é muito fácil porque o compilador aloca strings temporárias quando necessário. O comprimento máximo que você pode especificar é 255 caracteres. assim você não tem que se preocupar em alocar buffers para resultados intermediários. Isto significa que você nunca pode ter mais do que 255 caracteres em uma ShortString (255 characters + 1 tamanho em byte = 256). . como no exemplo seguinte: var S: string[45]. end. O código anterior faz com que uma ShortString seja criada apesar da configuração da diretiva $H. Na memória. // Altere o comprimento de S para o // comprimento com null RealizeLength(S). Uma variável ShortString é declarada e inicializada com a seguinte sintaxe: var S: ShortString. // S agora guarda a string do diretório GetWindowsDirectory(PChar(S). Opcionalmente. Para reiterar. // chame a função GetWindowsDirectory. end. a string lembra um array de caracteres onde o caracter 0 na string contém o tamanho da string. 256). O Tipo ShortString Se você é um veterano de Delphi. begin // importante! obtenha tamanho da string primeiro SetLength(S.0. você pode alocar menos do que 256 bytes para uma ShortString usando apenas o identificador do tipo string e um tamanho especificador. begin S := 'Bob o gato. e a string em si contém é contida nos caracteres seguintes. O tamanho de armazenagem de uma ShortString default é de no máximo de 256 bytes.17 var S: string. { uma ShortString de 45 caracteres } begin S := 'Esta string deve ser de 45 ou menos caracteres. 256). ShortStrings são algumas vezes referidos como strings do Pascal. end.

Dica: Embora incluir a checagem de limites em seu programa ajude-o a encontrar erros de strings. Cuidado: As funções e procedimentos na API requerem strings terminadas com null. i: Integer. Se você então tentar escrever o décimo elemento da string como a seguir. Não tente passar um tipo ShortString para uma função API porque seu programa não compilará. Se a string é maior do que 254 chars. então ela será truncada para 254. suponha que você declara uma variável como a seguir: var Str: string[8]. e tenta atribuir 'uma_pequena_longa_string' à variável. Por causa disso. } begin if Length(S) = High(S) then Dec(S[0]). você pode obter falsos resultados ou invadir memória se você tentar usar um indíce que é maior do que o tamanho declarado da ShortString. Str[i] := 's'. mas remover a checagem depois que você estiver confiante na estabilidade de seu programa. ShortStrings não são inerentemente compatíveis com strings terminadas com null. a string será truncada para apenas oito caracteres. Se você declara uma variável como uma string[8]. { Coloca null no fim da string } Result := @S[1]. ShortStringAsPChar(): function ShortStringAsPChar(var S: ShortString): PChar. Diferente de AnsiStrings. begin i := 10.18 Nunca armazene mais caracteres em uma ShortString do que a memória alocada para ela. um pouco de trabalho é necessário para que seja possível passar uma ShortString para uma função API. e você perderá dados. a checagem de limites atrasará levemente a performance de sua aplicação. { Retorna a string em em "PChar" } end. por exemplo. Sua vida será mais fácil se você usar long strings quando trabalhar com a API. Por exemplo. É prática comun usar a checagem de limites durante o desenvolvimento e fases de depuração de seu programa. Veja a função seguinte. { Trunca S se ela for muito longa } S[Ord(Length(S)) + 1] := #0. você provavelmente invadirá memória usada por outras variáveis: var Str: string[8]. Quando usar um array indexado para endereçar um caracter particular em uma ShortString. . // invadirá memória Você pode ativar uma opção do compilador para verificar esses tipos de erros em tempo de execução selecionando Range Checking na caixa de diálogo Options | Project . { Esta função não finaliza com null uma string de modo que ela possa ser passada para funções } { que requerem tipos PChar.

// Wide convertido para Ansi S := 'Outra String'. W2 := 'campo'. =. Assim como nos tipos AnsiString e ShortString. assim atribuir uma WideString à outra requer que toda a string seja copiada de um local na memória para outro. W := S. Como mencionado anteriormente. S: string. • WideStrings são alocados usando a função API SysAllocStrLen(). C := W[Length(W)]. Então. P: Integer. W2). // Ansi convertido para Wide end. W2: WideString. e ambos são alocados dinamicamente. // C contém o último caracter em W end. Length(). o que o torna compatível com strings BSTR de OLE. begin W1 := 'Envolver'. begin W := 'Ébano e Marfim vivem em perfeita harmonia'. Insert(). o código seguinte está sintaticamente correto: var W1. if W1 <> W2 then P := Pos(W1. end. Isto torna WideStrings menos eficiente do que AnsiStrings em termos de velocidade e memória usada. e <> para serem usados com WideStrings. o que o torna compatível com strings Unicode.19 O Tipo WideString O tipo WideString é tipo similar a AnsiString. . Para que o trabalho com WideStrings seja natural. S := W. Copy(). o compilador sabe automaticamente como fazer a conversão entre variáveis do tipo AnsiString e WideString como mostrado a seguir: var W: WideString. • WideStrings não são contados por referência. begin W := 'Uma String'. e SetLength() e os operadores +. Contudo. WideString difere de AnsiString em três aspectos: • WideStrings são compreendidos de caracteres WideChar e não de caracteres AnsiChar. possuem coleta de lixo e são compatíveis em tamanho um com o outro. C: WideChar. Pos(). Object Pascal sobrecarrega as rotinas Concat(). você pode usar colchetes de arrays para referenciar caracteres individuais de uma WideString: var W: WideString.

O teórico tamanho máximo de uma string PChar é um pouco menos de 4 gigabytes. você reduzirá drasticamente a chance de ocorrerem bugs por invasão de memória em sua aplicação se. e PWideChar. Tabela 2.20 Strings Terminadas com Null Inicialmente. ou até mesmo a função da API VirtualAlloc(). você aloca memória para um buffer de PChar usando a função StrAlloc(). usando uma das funções de alocação de memória do Object Pascal. Dica: Como o tipo AnsiString do Object Pascal pode ser usado como um PChar na maioria das situações. S1. FreeMem() GlobalFree() FreeMem() Dispose() StrDispose() StrDispose() VirtualFree() O exemplo seguinte demonstra técnicas de alocação de memória quando se trabalha com PChars e strings: var P1. begin // P1 aponta para uma alocação de 63 Chars . Neste capítulo. você deve usar este tipo no lugar de PChars sempre que possível.. onde possível. Funções correspondentes também existem para muitas dessas funções. que faz uso extensivo de strings terminadas com null. P2: PChar. GetMem(). O tipo PChar no Delphi existe principalmente para manter a compatibilidade com Delphi 1. Como seus nomes indicam. este capítulo mencionou que Delphi tem três diferentes tipos de strings terminadas com null: PChar. Usando PChars Como mencionado anteriormente. Diferentemente da memória para AnsiStrings e WideStrings. Funções de alocação e desalocação de memóra Memória Alocada com . variáveis PChar requerem que você aloque e libere manualmente os buffers de memória que contém suas strings. você evitar PChars e a alocação de memóra associada a eles. nos referimos a cada um desses tipos de strings genericamente como PChar. a memória para PChars não é alocada automaticamente e gerenciada pelo Object Pascal.. AllocMem() GlobalAlloc() GetMem() New() StrAlloc() StrNew() VirtualAlloc() Deve Ser Liberada com.. Então.6 lista várias funções de alocação e suas funções de desalocação correspondentes. mas muitas outras funções podem ser usadas para alocar memória para PChars.6. S2: string. cada um desses representa uma string terminada com um null de cada um dos três tipos de caracteres do Delphi. StrNew(). leia sobre. Como o gerenciamento de memórai para strings ocorre automaticamente. você com freqüência necessitará alocar memória para a string para a qual ele aponta. Um PChar é definido como um ponteiro para uma string seguida por um valor nulo (zero) (se você não sabe exatamente o que é um ponteiro. Normalmente. A Tabela 2. PAnsiChar.0 e com a API Win32.. ponteiros são discutidos em maiores detalhes no fim desta seção). incluindo AllocMem(). as quais devem ser usadas para desalocar a memória.

antes de tudo. 'Delphi 4 '). A função StrNew()é usada para copiar o valor contido na string S1 para dentro do PChar P2. Lembre que o tamanho de um Char pode mudar de um byte para dois nas futuras versões de Delphi. // TENHA CUIDADO! Corrupção de memória.21 P1 := StrAlloc(64 * SizeOf(Char)). P2: Pchar. . assim você não pode assumir o valor de Char para sempre como sendo de um byte. Note. P2). É comun erros de sobrescrita de memóra qando usase StrNew() porque ele aloca apenas a memória suficiente para guardar a string. P2 := StrNew('World'). SizeOf() garante que a alocação funcionará adequadamente não importando quantos bytes um caracter ocupe. // P1 aponta para uma cópia de S1 P2 := StrNew(PChar(S1)). Tenha cuidado quando usar esta função. StrCat(P1. end. // S2 agora contém 'Delphi 4 Developer' S2 := P1. // concatena P1 e P2 StrCat(P1. . o uso de SizeOf(Char) com StrAlloc() quando aloca-se memória para P1. // Copia a string literal string para P1 StrPCopy(P1. // Põe algum texto na string S1 S1 := 'Developer'. Note aqui que você não pode usar + para concatenar da mesma forma que você pode com long strings e ShortStrings. StrDispose(P2). StrCat() é usado para concatenar duas strings de PChar. . begin // Aloca apenas a memória suficiente para P1 e P2 P1 := StrNew('Hello '). // limpa os buffers P1 e P2 StrDispose(P1). P2). Considere o seguinte exemplo: var P1. . end.

com isso o compilador sempre sabe o que é um variant. begin V := 'Delphi 4 is Great!'. Object Pascal fornece uma biblioteca decente de funções úteis e procedimentos para operar sobre PChars. Tipos Variant Delphi 2. Object Pascal é a única linguagem compilada que integra completamente variantes como um tipo de dado dinâmico em tempo de execução. Nesta seção inicialmente enfocaremos sobre o tipo Variant.. valores de ponto-flutuante. // Variant agora contém um ponto flutuante V := True. Variants foram produzidos primeiramente a fim de suportar Automação OLE. Note que variants não podem referir-se a Objetos do Object Pascal.34.22 Dica: Assim como com outros tipos de strings. e como um tipo estático em tempo de compilação. Procure por "String-handling routines (null-terminated)" no help online do Delphi.Basic'). Variants também podem referir-se a um array não homogêneo. De fato. tempo e data. Delphi 3 introduziu um novo tipo chamado OleVariant. A implementação de Delphi de variants tem também a vantagem de ser útil em outras áreas de programação Delphi.0 introduziu um poderoso tipo de dado chamado Variant. tais como Inteiros. currency e também objetos de Automação OLE. Variants Mudam de Tipos Dinamicamente Um dos principais propósitos de varitants é ter uma variável cujo tipo de dado base não possa ser determinado em tempo de compilação. que usa o tipo Variant pesadamente. // Variant agora contém um booleano V := CreateOleObject('Word. o tipo de dado Variant do Delphi é uma encapsulação do variant usado com OLE. // Variant agora contém um Integer V := 123. Isto significa que um variant pode mudar o tipo para o qual ele referir em tempo de execução. Variants podem suportar todos os tipos de dados simples. que podem variar em tamanho e cujos elementos . o qual é idêntico a Variant exceto que ele pode apenas conter tipos compatíveis com Automação. como você aprenderá. // Variant contém uma string V := 1. o código seguinte compilará e rodará adequadamente: var V: Variant. Booleanos. // Variant agora contém um objeto OLE end. strings. Por exemplo. e então discutiremos OleVariant e contrastaremos ele com Variant.

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

Você deve entender que em alguns casos esta é uma prática perigosa porque é possível perder a referência para uma string ou outra entidade dinâmica. o . varByRef = $4000. Por exemplo. ele seta seu campo VType par varString e copia o apontador da string para dentro de seu campo VString. begin TVarData(V). o procedimento acaba e retorna para o código que o chamou.VType := varInteger. Ele então incrementa o contador de referência da string S. o qual atribui uma string a uma variável variant: procedure ShowVariant(S: string). Como discutimos anteriormente neste texto. apenas 4 bytes dos 8 bytes de dados na porção variant do registro são usados para guardar um valor inteiro. o que resultará em sua aplicação perda da memória ou outros recursos. se VType tem o valor varByte. se o campo VType contém o valor varInteger. Da mesma forma. Você notará que se VType contém o valor varString. Delphi primeiro inicializa o variant para um valor não atribuído. end. isto é. Você notará da listagem de TVarData que o registro TVarData é na verdade um registro variant. A sentença case no registro variant TVarData indica que o tipo de dado ao qual o variant refere. muitas coisas permanecem aqui e podem não ser aparentes. examine o código seguinte. Embora o registro variant e o tipo variant tenham nomes similares. Isto é discutido em maiores detalhes na seção "Registros" deste texto. end. um Variant não pode conter uma referência a um tipo Pointer ou class. como mostrado a seguir: var V: Variant. mas sim.24 varTypeMask = $0FFF. Por exemplo. eles guardarão um apontador para esta string. Variants São Dinâmicos Delphi automaticamente manipula a alocação e desalocação de memória requerida por um tipo variant. varArray = $2000. Este é um ponto importante porque você pode acessar campos de um variant diretamente. apenas 1 byte dos 8 são usados para guardar o valor do byte. Nota: Como você pode notar dos códigos dos tipos na listagem anterior. Registros variants permitem que multiplos campos de dados existam em uma mesma posição de memória como uma union em C/C++. eles representam duas idéias completamente diferentes. var V: Variant begin V := S.VInteger := 2. ShowMessage(V). Não confunda isto com o tipo Variant. os 8 bytes de dados na verdade não guardarão a string. Durante a atribuição. Quando o variant sai do escopo. TVarData(V).

var V: Variant begin V := S. você verá porque não é recomendado que você manipule campos do registro TVarData diretamente. TVarData(V). atravéz disso libere a referência a string. var V: Variant begin V := S. V. Esta mesma liberação implícita de recursos ocorre quando você atribui um tipo de dado diferente ao variant. // inicialize variant para “vazio” try V := S. assegurando que ele é inicializado com "vazio" try V. finally // Agora limpe os recursos associados com o variant end. end.VInteger := 34. como mostrado a seguir: procedure ChangeVariant(S: string). V.VType := varInteger. // Limpe o Variant V. end.finally no procedimento como mostrado a seguir: procedure ShowVariant(S: string). end.25 variant é limpo. . e o contador de referência da string S é decrementado.RefCount).VString := S. examine o código seguinte: procedure ChangeVariant(S: string). V. var V: Variant begin V := Não-Atribuido.VType := varString. Este código reduz-se ao seguinte pseudo-código: procedure ChangeVariant(S: string). V := 34. ShowMessage(V). Inc(S.VType := varInteger. finally // Limpe os recursos associados com o variant end.. Se você entendeu o que aconteceu na ilustração anterior. Por exemplo. var V: Variant begin // Limpe o Variant V. Delphi faz isso implicitamente inserindo um bloco try.

se dois variants V1 e V2 contém inteiros. não é necessário fazer o typecast do variant para um outro tipo de dado para fazer a atribuição. :=. Se você está certo do tipo que um variant contém. não acesse os campos de TVarData diretamente. no caso: 2 I := Integer(V). currency. ou Booleano. xor.VInteger := 32. shl. Delphi sabe como efetuar as operações baseado no conteúdo do variant. Por exemplo. S := V. é melhor você fazer o typecast para o outro tipo a fim de aumentar a velocidade da operação. A propósito. Embora isto possa parecer seguro. como essas conversões são feitas em tempo de execução. há muito mais código lógico atachado a este método. do contrário B é nbsp. or. provavelmente resultando na perda da referência. Variants em Expressões Você pode usar variants em expressões com os seguintes operadores: +. V := 34. /. a qual deve ser do tipo inteiro. Você pode fazer o typecast de um variant para um tipo de dado simples. >=. B := Boolean(V). a expressão . dada a seguinte sentença V := 1. B := V. // D contém o valor 1. Como uma regra geral. =. string. Typecasting Variants Você pode explicitamente alterar o tipo de expressões para o tipo Variant. ou se você precisar. as seguintes expressões terão os resultdos mostrados: S := string(V). real. tenha certeza absoluta de que você sabe exatamente o que está fazendo. não é porque ele resulta no fracasso de decrementar o contador de referência da string S.6. *. a expressão seguinte: Variant(X) resulta em um tipo variant cujo código do tipo corresponde ao resultado da expressão X. Contudo.6'. <. onde V é uma variável do tipo Variant. Por exemplo.26 TVarData(V). D := V. shr. nbsp. <=. // I está arredondando para o valor Inteiro mais próximo. O que acontece aqui é que as conversões para os tipos de dado alvo são feitas através de um typecast implícito. mod. // B contém False se V contém 0. // S conterá a string '1. div. // True D := Double(V). Quando variants são usados em expressões. not.6. caracter. >. o que discutiremos a seguir. I := V. <>. end.6 Esses resultados são ditados pelas regras de conversão de tipos aplicáveis a Variants. Isto é especialmente verdadeiro se o variant está sendo usado em uma expressão. Por exemplo. and. O código seguinte trabalhará melhor: V := 1.

Uma solução melhor é obviamente não usar variants. begin V1 := '100'. Baseado no que acabamos de mencionar sobre regras de conversão. // Gera uma exceção end. Contudo. o resultado é uma concatenação das duas strings. // Um tipo string V3 := 200. Antes que um resultado possa ser gerado por esta equação. dando um resultado final de 10250. Vazio e Null .27 V1 + V2 resulta na adição dos dois inteiros. Como declarado anteriormente. Contudo. quando uma operação é tentada sobre dois variants dos quais o Delphi não pode diferenciar. cada operação é manipulada por uma função em tempo de execução que passa por muitos testes para determinar a compatibilidade dos tipos de variants representados. begin V1 := 77. uma exceção de "conversão de tipo variant inválido" é causada. // Um tipo string V2 := '50'. quando necessário. Porque a ordem de precedência é da esquerda para a direita. V1 será convertido para um ponto flutuante e então adicionado a V2. o resultado '10050' é convertido para um inteiro e somado a V3. se você der uma boa olhada. Entretanto. Lembre que isto assume que você conhece os tipos de dados que os variants representam. Considere a seguinte linha de código: V4 := V1 * V2 / V3. Então as conversões são feitas para os tipos de dados apropriados. se V1 contém a string '4. uma concatenação de string é efetuada resultando na string '10050'. V1 := V1 / V2. verá que este não é o caso. // Um tipo Integer V1 := V1 + V2 + V3. V2 := 'hello'. O que acontece se V1 e V2 contém dois tipos de dados diferentes? Delphi usa certas regras de conversão a fim efetuar a operação. Contudo. Como esses dois variants referem-se a strings. Delphi converte os variants para o tipo mais elevado na equação a fim de cumprir com maior sucesso o cálculo.5' e V2 contém um número de ponto-flutuante. Como V3 é um inteiro. V3: Variant. parece a primeira vista que o código anterior resultaria no valor 350 como um inteiro. a primeira equação que foi executada é V1 + V2. O código seguinte ilustra isto: var V1. é algumas vezes uma boa idéia explicitar o typecast do variant para um tipo de dado específico se você souber qual é o tipo e se ele é usado em uma expressão. se V1 e V2 contém strings. end. Por exemplo. Isto resulta em uma grande quantidade de código e overhead. V2: Variant. O que resulta então é a adição do valor inteiro armazenado na variant V3. O código seguinte ilustra isto: var V1. V2. você pode explicitamente converter os variants de modo que os tipos de dados sejam resolvidos em tempo de compilação: V4 := Integer(V1) * Double(V2) / Integer(V3).

O mesmo não é verdade para variants contendo um valor varNull . I. Geralmente. aquele valor propagará par ao resultado. você passa os limites do array que você quer criar e um código do tipo variant para o tipo dos elementos do array (o primeiro parâmetro é um array aberto. begin I := V[J]. end. Apenas em situações onde a flexibilidade dos variants pesar mais do que a performance dos métodos convencionais você deve lançar mão do uso de variants. Variants são úteis em muitas situações. Cuidado: Talvez seja tentador usar variants em vez de tipos de dados convencionais porque eles parecem oferecer uma maior flexibilidade. que é diferente de varEmpty porque ele representa realmente o valor Null em oposição a ausência de valor. Este é o valor inicial do variant setado pelo compilador quando ele está envolvido no escopo. Unassigned e Null. VarType: Integer): Variant. você deve usar os tipos de dados convencionais em vez de variants. Quando um variant envolvido em uma equação contém um valor Null. que tem os valore VType de varEmpty e varNull. respectivamente. entretanto. por causa da flexibilidade dos tipos de dados que eles oferecem. Object Pascal fornece vários arrays de variant que suportam funções que permitem a você criar um array de variant. o . Se você quiser atribuir ou comparar um variant quanto a um desses dois valores. Uma outra diferença é que ao tentar efetuar qualquer equação com um variant contendo um valor varEmpty VType resultará em uma exceção de “invalid variant operation”. Então o resultado de qualquer equação contendo um Null é sempre Null. isto aumentará o tamanho do seu código e fará com que suas aplicações executem mais lentamente. De fato. tornará a manutenção do seu código mais difícil. VarArrayCreate() VarArrayCreate() é definida na unit System como function VarArrayCreate(const Bounds: array of Integer.28 Dois valores especiais de VType para variants merecem uma breve discussão. entretanto. O outro é varNull. Tipos de dados ambíguos geram bugs de ambiguidade. Então a sintaxe seguinte é válida: var V: Variant. a unit System define dois variants. Arrays de Variant Anteriormente mencionameos que um variant pode referir a um array não-homogêneo. Para usar VarArrayCreate(). O primeiro é varEmpty. Além disso. a própria VCL usa variants em muitos lugares. Tenha em consideração que. Essas são VarArrayCreate() e VarArrayOf(). que significa que ainda não foi atribuído nenhum valor ao variant. Esta distinção entre nenhum valor e o valor Null é especialmente importante quando aplicada a valores de campos de uma tabela de banco de dados. Entretanto. J: Integer. mais notavelmente em ActiveX e áreas de banco de dados. embora o código anterior compile. você obterá uma exceção em tempo de execução porque V não contém ainda um array de variant.

4. V[3] := 3. 2. procedure VarArrayUnlock(const A: Variant). cada elemneto no array tem a habilidade de conter um tipo diferente de dado. varInteger). function VarArrayDimCount(const A: Variant): Integer. VarArrayRef() é destinada a trabalhar em volta de um problema que exista ao passar arrays de variant para servidores de automação OLE. begin V := VarArrayCreate([1. V[2] := 2. Se arrays de variant de um tipo simples não são confusos o suficiente. 4. 'Delphi'. como este: Server. você passa varVariant como o código do tipo a fim de criar um array de variants de variants! Desta forma.. O array é passado não como um array de variant. Funções e Procedimentos de Suporte de Array de Variant Além de VarArrayCreate() e VarArrayOf().2]). O exemplo seguinte cria um array variant de três elementos com um inteiro.. function VarArrayLowBound(const A: Variant. function VarIsArray(const A: Variant): Boolean. o seguinte retorna um variant array de inteiros e atribui valores aos itens do array: var V: Variant. function VarArrayRef(const A: Variant): Variant.PassVariantArray(VA). uma string e um valor de ponto-flutuante: V := VarArrayOf([1. Essas funções são definidas na unit System e são também mostradas a seguir. end.29 qual é discutido na última seção "Passando Parâmetros" neste capítulo). mas sim como um variant contendo um array de variant—uma distinção importante. function VarArrayHighBound(const A: Variant. Dim: Integer): Integer. Dim: Integer): Integer.5]. V[4] := 4. // Cria um array de 4 elementos V[1] := 1. VarArrayLowBound() e VarArrayHighBound() retornam os limites inferior e superior de um array. VarArrayOf() A função VarArrayOf() é definida na unit System como function VarArrayOf(const Values: array of Variant): Variant. O problema ocorre quando você passa um variant contendo um array de variant para um método de automação. HighBound: Integer). 1. Se o servidor expera um array de variant ao . procedure VarArrayRedim(var A: Variant. 1. o código seguinte cria um array com limites [1. Por exemplo. varInteger). Esta função retorna um array unidimensional cujos elementos são dados no parâmetro Values. Você pode também criar um array multidimensional passando os limites adicionais requeridos. há várias funções de suporte de array de variant. VarArrayLock() e VarArrayUnlock() são duas funções especiais. que são descritas em detalhes na próxima seção. 5]. Por exemplo. respectivamente. 4]. function VarArrayLock(const A: Variant): Pointer. A função VarArrayDimCount() retorna o número de dimensões em um array de variant. A função VarArrayRedim() permite que você redimensione o limite superior da dimensão mais a direita de um array de variant. V := VarArrayCreate([1.

se usados incorretamente. arrays de variant podem se tornar um recurso ineficiente de troca de dados. a procedimento Move() com o ponteiro para os dados do array. VarByte). VarByte). end. end.PassVariantArray(VarArrayRef(VA)). mas de uma manira muito mais eficiente: begin V := VarArrayCreate([1. 10000].30 contrário de uma referência para um. Isto por causa das atribuições aos elementos do array tem que passar pela lógica em tempo de execução para determinar a compatibilidade de tipos. Entretanto.000 bytes. finally VarArrayUnlock(V). por exemplo. 10000]. Suponha que você tenha um outro array (não-variant) declarado do mesmo tamanho e que você queira copiar o conteúdo deste array não-variant apr ao array de variant. Para evitar esses controles em tempo de execução. VarIsArray() é uma checagem booleana simples. VarArrayRef() cuida desta situação modelando o variant para o tipo e valor experados pelo servidor. 10000]. P := VarArrayLock(V). como mostrado a seguir: begin V := VarArrayCreate([1. uma vez que eles fornecem os únicos meiso de passar dados binários 'crus' para um servidor de automação OLE (porque ponteiros não são um tipo legal em automação OLE). O código seguinte efetua a inicialização do array de variats mostrado anteriormente. 10000). VarArrayLock() trava o array na memória de forma que ele não possa ser movido ou redimensionado enquanto estiver travado. Considere a linha seguinte: V := VarArrayCreate([1. você pode usar a função VarArrayLock() e o procedimento VarArrayUnlock(). O problema com este código é que ele está condenado apenas pelo custo requerido para inicializar os elementos do array de variants. VarByte). Esta linha cria um array de variant de 10. try Move(A. localização de cada elemento. e retorna um ponteiro para os dados do array. end. e assim por diante. que retorna True se o parâmetro do variant passado para ele é um array de variant ou False caso contrário. for i := 1 to 10000 do V[i] := A[i]. Esta sintaxe usando VarArrayRef() é Server. Inicializando um Large Array—VarArrayLock(). VarArrayUnlock() destrava um array travado com VarArrayLock() e permite de novo que o array de variant seja redimensionado e movido na memória. . P^. Normalmente. VarArrayUnlock() Arrays de variant são importantes em Automação OLE. você pode apenas fazer isso com um loop atravéz dos elementos e atribuí-los aos elementos do array de variant. você pode empregar um meio mais eficiente para inicializar os dados usando. Depois que o array é travado. o servidor provalvelmente encontrará uma condição de erro quando você chamar o método com a sintaxe anterior.

ele não é suscetível a erros de arredondamento como são os tipos de pontos-flutuante. Quando se tenta atribuir uma AnsiString a um OleVariant. Tipos Definidos Pelo Usuário Inteiros. Atualmente o útico VType suportado que não é compatível com Automação é varString. O procedimento VarCast() converte um variant para um tipo especificado e armazena aquele resultado em outro variant. A única diferença entre OleVariant e Variant é que OleVariant suporta apenas tipos compatíveis com Automação. VarFromDateTime() retorna um variant que contém um valor TDateTime dado. const Source: Variant. function VarAsType(const V: Variant. Essas funções são declaradas na unit System e estão listadas a seguir: procedure VarClear(var V: Variant). function VarToStr(const V: Variant): string. Double. VarType: Integer): Variant. VarIsNull() indica se um variant contém um valor Null ou não. e números de ponto flutuante freqüentemente não são suficientes para representar adequadamente variáveis nos problemas do mundo real que os programadores devem tentar resolver. esses tipos definidos pelo usuário . que é ideal para cálculos financeiros. VarToDateTime() retorna o valor TDateTime contido em um variant. O procedimento VarClear()limpa um variant e seta o campo VType para varEmpty. function VarFromDateTime(DateTime: TDateTime): Variant. procedure VarCast(var Dest: Variant. a AnsiString será automaticamente convertida para um OLE BSTR e armazenada no variant como um varOleStr. VarType() retorna um dos códigos de tipo varXXX para um variant especificado. o código para AnsiString. function VarIsEmpty(const V: Variant): Boolean. const Source: Variant). ou Extended onde dinheiro estiver envolvido. Currency Delphi 2.31 Funções de Suporte Existem muitas outras funções de suporte para variants que você pode usar. VarCopy()copia o variant Source para o variant Dest. function VarIsNull(const V: Variant): Boolean. é uma boa idéia usar este tipo no lugar de Single.0.0 introduziu um novo tipo chamado Currency. procedure VarCopy(var Dest: Variant. VarType:Integer). function VarType(const V: Variant): Integer. strings. VarToStr() converte um variant para sua representação em string (uma string vazia no caso de um variant Null ou vazio). Real. VarIsEmpty() retorna True se o código do tipo de um variant especificado é varEmpty. Diferentemente de números de ponto-flutuante que permitem o ponto decimal para o "flutuar" dentro de um número. você deve criar seus próprios tipos para melhor representar variáveis no problema corrente. VarAsType() tem a mesma finalidade que VarCast(). function VarToDateTime(const V: Variant): TDateTime. Como tal. Em Pascal. Quando atualizar seus projetos do Delphi 1. OleVariant O tipo OleVariant é primeiramente idêntico ao tipo Variant descrito por toda esta seção. Currency é um tipo decimal de ponto fixado que é codificado para uma precisão de 15 dígitos antes do decimal e 4 dígitos depois do decimal. Em casos como esses.

2. Esta instrução é equivalente a seguinte declaração em C: int A[8].30] of Integer...32 usualmente vêm na forma de registros ou objetos. O compilador fornece funções embutidas chamadas High() e Low(). você deve ter algum cuidado quando interagir com os elementos do array em um loop for.. use uma lista de limites delimitada por vírgula: var // Arrays de Integer de duas dimensões: A: array[1. você declara esses tipos utilizando a palavra reservada Type Arrays Object Pascal permite que você crie arrays de qualquer tipo de variável (exceto files). 1. Para acessar um array multidimensional. Para especificar dimensões múltiplas. begin for i := Low(A) to High(A) do // sem código complicado para o loop A[i] := i. end. Por exemplo. use vírgulas para separar cada dimensão dentro de dois colchetes: . Você então pode declarar um array de três elementos que começa em 28. Como arrays em Object Pascal não necessariamente começam em zero ou um. que retornam os limites inferiores e superiores de uma variável ou tipo array. Seu código estará menos propenso ao erro e mais fácil de manter se você usar essas funções para controlar seu loop for como mostrado a seguir: var A: array[28.7] of Integer.30] of Integer. como o exemplo seguinte: var A: Array[28. i: Integer. Arrays de caracteres com zero na base podem ser passados para funções que requerem variáveis do tipo PChar.2] of Integer. Dica: Sempre comece arrays de caracteres no 0.. uma variável declarada como um array de oito inteiros var A: Array[0. E também é 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 não começam em um determinado número..

você apenas precisa atribuir ao array dinâmico um nil: SA := nil. eles serão liberados quando deixarem o escopo. 33). Arrays Dinâmicos são arrays alocados dinamicamente em dimensões que não são conhecidas em tempo de compilação. Então. Depois que a memória foi alocada. você pode acessar os elementos do array dinâmico como um array normal: SA[0] := 'O urso parece feliz'. pode haver uma hora que você queira remover o array dinâmico da memória antes que ele saia do escopo (se ele usa muita memória. // libera SA Arrays dinâmicos são manipulados utilizando semanticas de referência similares à AnsiStrings. use o procedimento padrão Copy(): . qualquer modificação em A2 também afetará A1. begin SetLength(A1. Um rápido teste—qual é o valor de A1[0] no fim do seguinte fragmento de código: var A1. A1[0] := 1. OutraString := SA[0]. Arrays Dinâmicos Uma novidade no Delphi 4 são os arrays dinâmicos. apenas declare um array sem incluir as dimensões: var // array dinâmico de string: SA: array of string. Se você quiser fazer uma cópia completa de A1 em A2. Isto porque a atribuição A2 := A1 não cria um novo array mas ao invéz disso fornece a A2 uma referência ao mesmo array A1. A resposta correta é 26. A2: array of Integer. assim não é necessário liberá-los quando você deixar de usá-los. você deve usar o procedimento SetLength() para alocar memória para o array: begin // aloca lugar para 33 elementos: SetLength(SA. Nota: Arrays Dinâmicos sempre iniciam no zero. Para fazer isso. 4).33 I := A[1. Entretanto. A2[0] := 26. A2 := A1. 2]. Arrays dinâmicos são auto-suficientes. Para declarar um array dinâmico. por exemplo). Antes de poder usar um array dinâmico.

d: Double. Para alocar memória para um array dinâmico multidimensional. passe os tamanhos das outras dimensões como parâmetros adicionais para SetLength(): begin // IA será um array de Integer 5 x 5 SetLength(IA.3] := 28. Acesse arrays dinâmicos multidimensionais da mesma forma que você acessa arrays normais multidimensionais—cada elemento separado por uma vírgula entre colchetes: IA[0. começando no elemento 1: A2 := Copy(A1. Como um exemplo. /* Visual Basic */ Type MyRec i As Integer d As Double End Type . 5. Mudanças em um não afetará o outro. 5). Registros Uma estrutura definida pelo usuário é referida como um record em Object Pascal. Para especificar múltiplas dimensões. } MyRec. e é equivalente ao struct do C ou Type do Visual Basic. adicione um array of adicional a declaração para cada dimensão: var // array dinâmico de Integer de duas dimensões: IA: array of array of Integer. 1. end. Você pode opcionalmente especificar o elemento inicial e o número de elementos a serem copiados como parâmetros para Copy() como mostrado a seguir: // copia 2 elementos. aqui está uma definição de registro em Pascal e as definições equivalentes em C e Visual Basic: { Pascal } Type MyRec = record i: Integer. double d.34 A2 := Copy(A1). A2 e A1 serão dois arrays separados inicialmente contendo os mesmos dados. Depois que a linha de código acima for executada. 2). /* C */ typedef struct { int i. Arrays dinâmicos podem também ser multidimensionais.

Object Pascal também suporta registros de variants. int IntField. O código seguinte mostra um registro de variant no qual um Double. que emula o comportamento de um set do Pascal). caracteres ou enumerados. IntField: Integer. um Integer e um char ocupam o mesmo espaço de memória: type TVariantRecord = record NullStrField: PChar. reconhecerá registros de variants como sendo o mesmo conceito de unions dentro de structs do C. begin N. int i.d := 3. union { double D. Nota: As regras do Object Pascal estabelecem que a porção variant de um registro não podem ser do tipo autosuficiente. você usa um ponto para acessar seus campos. registros de variant permitem que cada campo de dado sobrepostos seja acessado independentemente. por exemplo: . }. Sets fornecem um meio muito eficiente de representar uma coleção de valores ordinais. Se você conhece C/C++. char c.35 Quando se trabalha com registros. C ou C++ (embora o Borland C++Builder implemente uma classe template chamada Set. Aqui está o equivalente em C++ da declaração de tipo precedente: struct TUnionStruct { char * StrField. por exemplo: var N: MyRec. end.4. Você pode declarar um novo tipo set utilizando a palavra-reservada set of seguida por um tipo ordinal ou um subconjunto de possíveis valores. Conjuntos (Sets) Sets são o único tipo do Pascal que não tem um equivalente em Basic. }. que permitem diferentes espécies de dados sobre a mesma porção de memória no registro. 2: (C: char). 1: (I: Integer). N. end. Não confunda com o tipo de dados Variant.i := 23. case Integer of 0: (D: Double).

para uma maior eficiência. Quarta. // membros possíveis: 'A' . TEnum = (Segunda. Sabado. apenas tipos ordinais podem seguir as palavras-reservadas set of.'z' begin CharSet := ['A'. Quarta. as declarações seguintes são ilegais: type TIntSet = set of Integer. Note que um conjunto pode apenas conter até 256 elementos.'J'. Quinta. // membros possíveis: 1 . Domingo]. Para obter máxima performance dos sets..'z'..36 type // membros possíveis: #0 .'z'. 'm']. Dessa forma. Isto os torna muito eficientes em termos de velocidade e memória utilizada. EnumSet := [Sabado. // membros possíveis: 1 . Sets com 32 ou mais elementos são armazenados na memória. // Invalido: muitos elementos TStrSet = set of string. EnumSet: TEnumSet.#255 TEnum = (Segunda. // Invalido: não é um tipo ordinal Sets armazenam seus elementos internalmente como bits individuais. Além disso. Terça.. Sets com menos do que 32 elementos no tipo base podem ser armazenados e operados em registradores da CPU. var CharSet: TCharSet. Quinta. mantenha o número de elementos no tipo base do set a baixo de 32. . 'a'.10 AlphaSet: set of 'A'.. Domingo).10.. Utilizando Sets Use colchetes quando referir aos elementos do set. // pode conter qualquer combinação dos membros de TEnum TEnumSet = set of TEnum. Sexta). O código seguinte demonstra como declarar variáveis do tipo set e atribuir a elas valores: type TCharSet = set of char. SubrangeSet: set of 1.'z' TAlphaSet = set of 'A'. // pode conter qualquer combinação dos membros de TEnum TEnumSet = set of TEnum. Terça. // membros possíveis: #0 .#255 TCharSet = set of char. Sexta.10 TSubrangeSet = set of 1. // membros possíveis: 'A' .10.

37

SubrangeSet := [1, 2, 4..6]; AlphaSet := []; // Vazio; nenhum elemento end; Operadores de Conjuntos Object Pascal fornece muitos operadores para usar na manipulação de sets. Você pode usar esses operadores para determinar intersecção, união, diferença e membros do conjunto. Membros Use o operador in para determinar se um dado elemento está contido em um set particular. Por exemplo, o código seguinge será usado para determinar se o conjunto CharSet mencionado anteriormente contém a letra 'S': if 'S' in CharSet then // faz algo; O código seguinte determina se EnumSet não contém o membro Segunda: if not (Segunda in EnumSet) then // faz algo; Unão e Diferença Use os operadores + e - ou os procedimentos Include() e Exclude(), para adicionar e remover elementos para e de uma variável 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 possível, use Include() e Exclude() para adicionar e remover um simples elemento de um conjunto ao invés dos operadores + e -. Include() e Exclude() constituem apenas 1 instrução de máquina cada um, enquanto que os operadores + e - requerem 13 + 6n instruções (onde n é o tamanho em bits do conjunto). Interseção Use o operador * para calcular a interseção de dois sets. O resultado da expressão Set1 * Set2 é um conjunto contendo todos os membros que Set1 e Set2 tem em comum. Por exemplo, o código a seguir poderia ser usado como um meio eficiente para determinar se um dado conjunto contém elementos múltiplos: if ['a', 'b', 'c'] * CharSet = ['a', 'b', 'c'] then // faz algo Objetos

38

Pense em objetos como registros que também contém funções e procedimentos. O modelo de objetos do Delphi é discutido em maiores detalhes na seção "Usando Objetos do Delphi", assim esta seção cobrirá apenas a sintaxe básica dos objetos em Object Pascal. Um objeto é definido como segue: Type TObjetoFilho = class(TObjetoPai); AlgumaVar: Integer; procedure AlgumProc; end; Embora objetos no Delphi não sejam idênticos aos objetos em C++, esta declaração é, grosso modo, equivalente a seguinte declaração em C++: class TObjetoFilho : public TObjetoPai { int AlgumaVar; void AlgumProc(); }; Métodos são definidos da mesma maneira que funções e procedimentos normais (que são discutidos na seção "Procedimentos e Funções"), com a adição do nome do objeto e o operador ponto: procedure TObjetoFilho.AlgumProc; begin { o código do procedimento vem aqui } end; O símbolo . do Object Pascal é similar em funcionalidade ao operador . do Visual Basic e o operador :: do C++. Você deve notar que, embora todas as três linguagens permitam o uso de classes, apenas Object Pascal e C++ permitem a criação de novas classes que comportem-se de uma maneira completamente orientada a objetos, a qual descreveremos na seção "Programação Orientada a Objetos." Nota: Objetos em Object Pascal não são colocados na memória da mesma forma que os objetos em C++, assim não é possível usar objetos de C++ diretamente do Delphi ou vice-versa. Entretanto, existem técnicas que associam objetos de C++ e Delphi. Uma exceção a isto é a capacidade do Borland C++Builder de criar classes que mapeiam diretamente para classes de Object Pascal usando a diretiva proprietária __declspec(delphiclass). Tais objetos são igualmente incompatíveis com objetos normais de C++. Apontadores Um apontador é uma variável que contém uma posição de memória. Você já viu um exemplo de um apontador no tipo PChar anteriormente neste tutorial. Tipo genérico de apontador do Pascal é chamado Pointer. Um Pointer é chamado algumas vezes de um apontador sem tipo porque ele contém apenas um endereço de memória e o compilador não mantém qualquer informação sobre o dado para o qual ele aponta.

39

Nota: Apontadores são um tópico um tanto avançado, e você definitivamente não necessita dominálos para escrever uma aplicação em Delphi. A medida que você se tornar mais experiente, apontadores se tornarão uma ferramenta valiosa para sua "caixa de ferramentas" de programação. Apontadores de determinado tipo (apontadores tipados) são declarados usando o operador ^, na seção 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 variável do apontador. Aqui estão algumas declarações típicas 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 instância de PFoo Nota: Programadores de C notarão 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 variável do apontador apenas armazena um endereço de memória. Alocar espaço para seja o que for que o apontador aponte é trabalho do programador. Você pode alocar espaço para um apontador usando uma das rotinas de alocação de memória discutidas anteriormente e mostradas na Tabela 2.6. Nota: Quando um apontador não 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 variável do apontador com o operador ^. O código seguinte ilustra o trabalho com apontadores: Program PtrTest; Type MyRec = record I: Integer; S: string; R: Real; end; PMyRec = ^MyRec; var Rec : PMyRec; begin

{ Rec está agora cheio } Dispose(Rec). b: PtrInteger.S := 'E agora para algo completamente diferente. porque esta é uma das causas clássicas de um erro de Access Violation. Como o compilador sabe quão grande é uma estrutura particular. Lembre de usar Dispose() para liberar qualquer memória alocada usando a função New(). Por exemplo. Tenha cuidado de não tentar manipular mais dados do que você tenha alocado com essas funções.I := 10. a propósito. as variáveis a e b na declaração equivalente em C são compatíveis em tipo: int *a.R := 6. b: ^Integer. Contrastando. int *b Object Pascal cria um tipo único para cada declaração ponteiro-para-tipo. // Não esqueça de liberar a memória! end. Quando Usar New() Use a função New() para alocar memória para um apontador para uma estrutura de um tamanho conhecido. Você deve usar FreeMem() para limpar qualquer memória que você alocou com GetMem() ou AllocMem(). entretanto. Rec^. Um aspecto de Object Pascal que pode dar aos programadores de C alguma dor de cabeça é a estrita checagem de tipo efetuada sobre tipos pointer.40 New(Rec). // agora a e b são compatíveis .384. // aloca memória para Rec Rec^. O compilador não pode falar antes do tempo quanto de memória você quer alocar para tipos PChar ou Pointer. // criando um tipo var a. as variáveis a e b no exemplo seguinte não compatíveis em tipo: var a: ^Integer. AllocMem(). Rec^. // Põe algo em Rec. Nunca aloque variáveis Pointer ou PChar usando a função New() porque o compilador não pode adivinhar quantos bytes você necessita para esta alocação.'. uma chamada para New() causará a alocação do número correto de bytes—tornando-o mais seguro e mais conveniente do que usar GetMem() ou AllocMem(). Você tipicamente usa GetMem() ou AllocMem() para alocar memória para estruturas para as quais o compilador não pode saber o tamanho. assim você deve criar um tipo se você quiser atribuir valores de a para b como mostrado a seguir: type PtrInteger = ^Integer. é um pouco mais seguro do que GetMem() porque AllocMem() sempre inicializa a memória que ela alocou com zero.

begin // algum código end. mas MeuOutroInteiroPerfeito não será compatível com Integer quando usado nos parâmetros var ou out. o que significa. se você quiser criar um novo nome para um Integer chamado MeuInteiroPerfeito. o tipo MeuOutroInteiroPerfeito será convertido para um Integer quando necessário para propósitos de atribuição. definir aliases tipados fortemente que são considerados um novo e único tipo pelo compilador. você pode fazer isso usando o código seguinte: type MeuInteiroPerfeito = Integer. Mercenario(M). Usando esta sintaxe. Então. I: Integer. ou apelidos (aliases). É possível. begin I := 1. O alias do novo tipo definido é compatível em tudo com o tipo para o qual ele é um alias. Por exemplo. Para fazer isso. que você pode usar MeuInteiroPerfeito em qualquer lugar que você use Integer. você achará que o compilador é muito exigente na compatibilidade de tipos nos parâmetros formais e atuais de uma chamada de função. para tipos que já estão definidos. // Erro: M não é uma variável compatível com Integer Typecasting e Conversão de Tipos Typecasting é uma técnica pela qual você pode forçar o compilador a ver uma variável de um tipo como se fosse outro tipo. Devido a natureza fortemente tipada do Pascal. Suponha que você necessite atribuir o valor de um caracter para um variável byte: .41 Aliases de Tipos Object Pascal tem a capacidade de criar novos nomes. o código seguinte está sintaticamente correto: var MOIP: MeuOutroInteiroPerfeito. MOIP := I. você ocasionalmente terá que moldar um variável de um tipo para uma variável de outro tipo para fazer o compilador feliz. Mas o código a seguir não compilará: procedure Mercenario(var Value: Integer). begin M := 29. var M: MeuOutroInteiroPerfeito. use a palavra reserda type da seguinte maneira: type MeuOutroInteiroPerfeito = type Integer. entretanto. neste caso. Portanto.

Separando strings do código fonte. você não pode fazer o typecast de um Double para um Integer. b := byte(c). use as funções Trunc() ou Round(). use o operador de atribuição: FloatVar := IntVar. Resources são strings literais (usualmente aquelas que são mostradas para o usuário) que são localizadas fisicamente em resource atachados à aplicação ou bibliotecas ao invés de serem embutidas no código fonte. ResString2 = 'Resource string 2'. Seu código fonte referencia os resouces de strings no lugar das strings literais. Para converter um inteiro em um valor de ponto-flutuante. b := c. // o compilador ficará feliz com esta linha end. você pode mais facilmente traduzir sua aplicação adicionando resources em um língua diferente. Para converter um tipo ponto-flutuante para um inteiro. Object Pascal também suporta um variedade especial de typecasting entre objetos usando o operador as. b: byte. . Por exemplo. Resources Delphi 3 introduziu a capacidade de colocar um resources de strings diretamente no código fonte do Object Pascal usando a cláusula resourcestring clause. o qual é descrito depois na seção "Informação do Tipo em Tempo de Execução". como mostrado a seguir: resourcestring ResString1 = 'Resource string 1'.42 var c: char. 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. Resources de strings são declarados na forma de identifier = string literal na cláusula resourcestring. ResString3 = 'Resource string 3'. begin c := 's'. Sintaticamente. begin c := 's'. b: byte. // compilador se queixará desta linha end. resources de strings podem ser usados em seu código fonte em uma maneira idêntica a constantes de strings: resourcestring ResString1 = 'hello'. Na sintaxe seguinte. Nota: Você pode converter uma variável de um tipo para outro apenas se o tamanho do dado das duas variáveis é o mesmo.

'Visual Basic If x = 4 Then y = x Nota: Se você tem um comando if que faz múltiplas comparações. Testando Condições Esta seção compara as construções if e case em Pascal aos seus similares em C e Visual Basic. Em tempo de execução. referências a um resource de strings resultam em uma chamada implícita para a função API LoadString() para carregar a string do resource para dentro da memória. Assumindo que você essas construções de programação antes. Como um exemplo. var String1: string. Faça isto: if (x = 7) and (y = 8) then Não faça isso. begin String1 := ResString1 + ' ' + ResString2. end. esteja certo de que você fechou cada uma das comparações entre parênteses para clarear o código. para que não seja gasto muito tempo explicando-os. Debaixo do pano em tempo de compilação.43 ResString2 = 'world'. . O Comando IF Um comando if capacita você a determinar se uma certa condição foi encontrada antes de executar um bloco de código particular. . isso causa uma insatisfação 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+ +. . /* C */ if (x == 4) y = x. aqui está um comando if em Pascal. use a seguinte construção se você quiser executar múltiplas linhas de texto quando uma dada condição é verdadeira: if x = 6 then begin FaçaAlgumaCoisa FaçaAlgoMais. . seguido pelas definições equivalentes em C e Visual Basic: { Pascal } if x = 4 then y := x. Por exemplo. o compilador do Delphi coloca os resources de strings em uma fonte de strings e linka aquele resource a sua aplicação.

. Construções de loop em Pascal são similares ao que você deve estar familiar com a sua experiência em outras linguagens. FaçaAlgoMais. Aqui está um exemplo da instrução case do Pascalt: case AlgumaVariavelInteira of 101 : FaçaAlgo. Nota: O tipo de seletor de uma instrução case deve ser um tipo ordinal. Usando Instruções case A instrução case no Pascal trabalha de forma muito semelhante à instrução switch no C ou C++. A instrução case fornece um meio para escolher uma condição entre muitas possibilidades sem uma imensa contrução de if. FaçaAlgoMais. end. Finalizando. else FaçaODefault.else if. end. É ilegal usar um tipo não ordinal. Você pode combinar múltiplas condições usando a construção if. break. 202 : begin FaçaAlgo. default: FaçaODefault. break.else if. assim esta seção não gastará tempo ensinado sobre loops. 303 : FaçaUmaOutraCoisa. case 202: FaçaAlgo. . end.else if x =100 then AlgumaFunção else if x = 200 then AlgumaOutraFunção else begin AlgumaCoisaMais. com um seletor de case Aqui está uma instrução switch do C equivalente ao exemplo anterior: switch (AlgumaVariavelInteira) { case 101: FaçaAlgo. break case 303: FaçaUmaOutraCoisa.44 FaçaUmaOutraCoisa. } Loops Um loop é uma construção que permite que você efetue repetidamente algum tipo de ação. tal como uma string... end.

Aqui está um exemplo. {$APPTYPE CONSOLE} var f: TextFile. } 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 advertência para aqueles familiarizados com Delphi 1. As condiçõe de um loop while são testadas antes que o loop é executado. de um loop for que incrementa um índice de loop a uma variável 10 veze: var I. i. for(i=1. end. for I := 1 to 10 do inc(X.45 Esta seção descreve as várias construções de loops que você pode usar em Pascal. X: Integer. // um arquivo texto s: string. embora não muito útil. O Loop while Use o loop while quando você quiser que alguma parte de seu código repita enquanto alguma condição é verdadeira. begin .0: atribuições à variável controladora do loop não são mais permitidas devido a forma como o loop é otimizado e gerenciado pelo compilador 32 bit. e um exemplo clássico para o uso de um loop while é a efetuação repetida de alguma ação sobre um arquivo enquanto o fim do arquivo não 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. O Loop for O loop for é ideal quando você necessita repetir uma ação um número predeterminado de vezes. i++) x += i. O equivalente em C do exemplo anterior é o seguinte: void main(void) { int x. i<=10. x = 0. begin X := 0. I).

'foo.txt'). writeln(S).until é aproximadamente equivalente ao loop do. 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. begin X := 1. // faz o computador apitar if i = 5 then Break. ou repeat loop faz com que seu programa salte imediatamente para o fim do loop que estiver sendo executado. O Procedimento Continue() . Repeat. Por exemplo. Reset(f). CloseFile(f). Ele repete um dado bloco de código até que uma certa condição torne-se True.46 AssignFile(f.until destina-se ao mesmo tipo de problema que um loop while mas de um ângulo diferente. while not EOF(f) do begin readln(f.while do C. begin for i := 1 to 1000000 do begin MessageBeep(0). end.. repeat. until x > 100. o código do loo sempre é executado ao menos uma vez porque a condição é testada no final do loop. end. O loop a seguir usa Break() para terminar o loop depois de cinco iterações: var i: Integer. for. end.. o fragmento de código a seguir repete uma instrução que incrementa um contador até que o valor do contador torne-se maior do que 100: var x: Integer. repeat inc(x). Diferentemente de um loo while.until O laço repeat. O procedimento Break() do Pascal é análogo as instruções Break do C e Exit do Visual Basic. Este método é útil quando você necessita deixar o loop imediatamente por causa de alguma circunstância que pode aparecer dentro do loop. end.. O Procedimento Break() A chamada de Break() de dentro de um laço while. S).

Program FuncProc. end. '. { Retorna True se I é 0 ou positivo. Note no exemplo seguinte que o código depois de Continue() não é executado na primeira iteração do loop: var i: Integer. você já deve estar familiarizado com os conceitos de funções e procedimetos. False se I é negativo } begin if I < 0 then Result := False else Result := True. end. {$APPTYPE CONSOLE} procedure BiggerThanTen(i: Integer). Procedimentos e Funções Como um programador. Depois do continue'). Uma função trabalha da mesma forma exceto pelo fato de que ela retorna um valor depois de sua saída para a parte do programa que a chamou. end. var . { escreve algo na tela se I é maior do que 10 } begin if I > 10 then writeln('Surpresa. Se você está familiarizado com C ou C++. Um procedimento é uma parte discreta do programa que efetua alguma tarefa particular quando ele é chamado e então retorna a parte do seu código que o chamou. end. function IsPositive(I: Integer): Boolean. Antes do continue'). Listagem 2.'). begin for i := 1 to 3 do begin writeln(i.1 demonstra um pequeno programa do Pascal com um procedimento e uma função. '.47 Chame Continue() dentro de um loop quando você quiser saltar sobre um porção de código e continuar na próxima iteração do loop. considere que um procedimento em Pascal é equivalente a uma função em C ou C++ que retorna void. enquanto que uma função corresponde a uma função do C ou C++ que retorna um valor. if i = 1 then Continue. writeln(i.

begin . use a palavra var na lista de parâmetros da função ou procedimento: procedure ChangeMe(var x: longint). Você também pode retornar um valor de uma função atribuindo ao nome de uma função o valor dentro do código da função. BiggerThanTen(Num). Considere o exemplo a seguir: procedure Foo(s: string). Parâmetros Valor Parâmetros valor são o modo default de passagem de parâmetros. begin Num := 23. Parâmetros também podem ser constantes se seus valores não mudarem no procedimento ou função. 'É Negativo. Tenha cuidado ao notar também que a variável implícita Result não é permitida quando a opção Extended Syntax do compilador está desabilitada em Project | Options | Compiler ou quando é usada a diretiva {$X-}.') else writeln(Num. uma cópia da string s será criada. Toda função em Object Pascal tem uma variável local implícita chamada Result que contém o valor de retorno da função. if IsPositive(Num) then writeln(Num. 'É Positivo. ou open array (open arrays serão discutidos nesse tutorial). Nota: A variável local Result na função IsPositive() merece atenção especial. Quando você chama um procedimento desta forma. Isto significa que vcoê pode escolher o valor de s sem ter qualquer efeito sobre a variável passada para dentro de Foo(). parâmetros passados por referência são também chamados parâmetros variáveis. e a função ou procedimento opera sobre a cópia. Para passar uma variável por referência. Quando um parâmetro é passado por valor. Note que diferentemente de C e C++. Esta é a sintaxe padrão do Pascal e costume das versões anteriores do Borland Pascal. a função não termina assim que um valor é atribuído a Result. Parâmetros Referência Pascal permite que você passe variáveis para funções e procedimentos por referência.48 Num: Integer. significa que uma cópia local daquela variável é criada. Os parâmetros qu você passa podem ser de qualquer tipo básico ou definido pelo usuário. e Foo() operará sobre a cópia local de s.'). Passar por referência significa que a função ou procedimento ao receber a variável pode modificar o valor daquela variável. end. PassandoParâmetros Pascal possibilita que você passe parâmetros por valor ou por referência para funções e procedimentos.

o código seguinte mostra uma implementação da função AddEmUp() que retorna a soma de todos os números passados em A: function AddEmUp(A: array of Integer): Integer. Você pode passar ou open arrays de algum tipo homogêneo ou arrays constantes de tipos diferentes. O código seguinte declarar uma função que aceita um open array de inteiros: function AddEmUp(A: array of Integer): Integer. var i: Integer. const não apenas impede que você modifique o valor dos parâmetros. j. Low() e SizeOf() para obter informação sobre o array. mas ele também gera código mais otimizado para strings e registros passados para dentro de procedimentos ou funções. e não o valor da variável. a palavra reservada var faz com que o endereço do parâmetro seja copiado de forma que seu valor possa ser diretamente alterado. var i. begin i := 8. 89]). Para ilustrar isto. Parametros Constantes Se você não quiser que o valor de um parâmetro passado para dentro de uma função seja alterado. const j = 23. Assim como o operador & do C++. Usar parâmetros var é equivalente a passar variávels por referência em C++ usando o operador &. 50. você pode usar as funções High(). Rez: Integer. você pode declará-lo com a palavra chave const. Aqui está um exemplo de uma declaração de procedimento que receber um parâmetro string constante: procedure Goon(const s: string). a palavra var faz com que o endereço da variável seja passado para a função ou procedimento. Parâmetros Open Array Parâmetros open array permite que você passe um número variável de argumentos para funções e procedimentos.49 x := 2. Você pode passar variáveis. Em vez de fazer uma cópia de x. Rez := AddEmUp([i. Para funcionar com um open array dentro de um a função ou procedimento. O código a seguir demonstra isto chamando AddEmUp() e passando uma variedade de elementos diferentes. { x agora é alterado na chamada do procedimento } end. for i := Low(A) to High(A) do . begin Result := 0. constantes ou expressões constantes para funções ou procedimentos de open array.

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

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

A parte de interface de uma unit é onde você declara tipos. Uma ilustração de escopo.2. Depois da instrução unit. AlgumGlobal e R tem escopo global—seus valores são conhecidos pelo compilador em todos os pontos dentro do programa. mas se você acessar R fora daquele procedimento. Para ser uma unit. Units Units são os módulos individuais do código fonte que constituem um programa Pascal. Uma unit é um local para você agrupar funções e procedimentos que podem ser chamdos de seu programa principal. se você tem um arquivo chamado FooBar. R := R . AlgumConstant. você estará referindo a versão local. begin AlgumGlobal := AlgumConstant. program Foo. Se você acessar R dentro de AlgumProc(). var LocalReal: Real. var AlgumGlobal: Integer.52 Listagem 2. {$APPTYPE CONSOLE} const AlgumConstant = 100. constantes. uma próxima linha funcional da unit do código deve ser a instrução interface. Por exemplo. é informação que pode ser compartilhada com seu programa e com outras units. • A parte de interface. a instrução seráunit FooBar. variáveis. end. até a instrução implementation.LocalReal. Se você tentar acessar LocalReal fora de AlgumProc(). Toda unit deve ter como sua primeira linha uma instrução dizendo que é uma unit e identificando o nome da unit. AlgumProc(R). begin LocalReal := 10. procedure AlgumProc(var R: Real). você estará referindo à versão global.0. O nome da unit sempre deve ser o mesmo nome do arquivo. R: Real. . O procedimento AlgumProc() tem duas variável na qual o escopo é local para aquele procedimento: R e LocalReal. end.593. o compilador mostrará um erro de identificador desconhecido. R := 4. Tudo que seguir esta instrução. um módulo fonte deve consistir ao mesnos de três partes: • Uma instrução unit.

Se você está portando uma aplicação do Delphi 1. Esta segue a parte de interface da unit. a finalização da unit era efetuada pela adição de um novo procedimento de saída usando a função AddExitProc(). Apenas declarações—nunca corpos de procedimentos—podem aparecer na interface.0. Aqui está um código para uma unit exemplo: Unit FooBar. Nota: Quando muitas units tem código de initialization/finalization. A parte de implementation é onde você define quaisquer funções e procedimentos que você declarou na parte de interface. é uma má idéia escrever código de inicialização e finalização que dependa de tal ordem porque uma pequena mudança na cláusula uses pode causar alguma dificuldade para encontrar bugs! A Cláusula uses A cláusula uses é onde você lista as units que você quer incluir em um programa ou unit particular. você deve mover seus procedimentos de saída para dentro da parte de finalização de suas units. A instrução implementation dever ser uma interface deve ser uma palavra em uma linha:implementation Opcionalmente.0. Este código será executado antes que o programa principal comece a executar. e executará apenas uma vez. uses UnitA. A instrução interface deve ser uma palavra em uma linha:interface • A parte de implementation.0. então a primeira unit na cláusula uses daquela unit. UnitB. e assim por diante). a declaração uses apropriada é a seguinte: Program FooProg. contém qualquer código de limpeza que executa quando o programa termina. Por exemplo. uma unit pode também incluir duas outras partes: • Uma parte de initialization. da unit. é também onde você declara qualquer tipo. que é localizada entre a initialization e end.53 procedimentos e funções que você quer tornar disponível para seu programa principal e para outras units. Esta porção da unit. A seção de finalization foi introduzida à linguagem no Delphi 2. • Uma parte de finalization. No Delphi 1. se você tem um programa chamado FooProg que usa funções e tipos em duas units. Também. contém qualquer código de inicialização para a unit. a execução de cada seção procede na ordem em que as units são encontradas pelo compilador (a primeira unit na cláusula uses do programa. Esta porção da unit. Embora a parte de implementation da unit tenha principalmente procedimentos e funções. que é localizada próxima do fim do arquivo. constante ou variável que vcoê não queira que esteja disponível fora desta unit. interface . UnitA e UnitB. Units podem ter duas cláusulas uses: uma na seção interface e outra na seção implementation section.

Pense em package como uma coleção de units armazenadas em um módulo como uma DLL separado (um Delphi Package Library. Isto é chamado uma referência circular de units. Isto geralmente resolve o problema. sua aplicação não rodará na ausência daquele package. A ocorrência de uma referência circular de units é frequentemente uma indicação de uma falha de projeto na sua aplicação. 2. mova uma das cláusulas uses para a parte de implementation de sua unit e deixar a outra na parte de interface. você terá uma situação onde a UnitA usa a UnitB e a UnitB usa a UnitA. propriedades e editores de componentes. Referência Circular de Units Ocasionalmente. ou arquivo DPL). { declarações privadas aqui } initialization { inicialização da unit aqui } finalization { limpeza da unit aqui } end. como com muitas coisas. { declarações públicas aqui } implementation uses BarFly.54 uses BarFoo. Este tipo de package contém units requeridas em tempo de execução por sua aplicação. os quais podem ser distribuídos através de múltiplas aplicações. Nestes casos. . Os packagens DCL*.DPL do Delphi é um exemplo deste tipo de package. Design package. Se você já programou em Delphi 1 ou 2. Entretanto. A melhor solução é frequentemente mover um pedaço dos dados que ambas as units UnitA e UnitB precisam usar para fora em uma terceira unit. o tamanho de seu EXE ou DLL pode ficar muito menor. Como o código para essas units reside em arquivos DPL e não em EXE ou DLL. Sua aplicação pode então linkar com estas units "empacotadas" em tempo de execução ao invéz de linkar em tempo de compilação. Quando depender de um package de tempo de execução particular. Packages Packages do Delphi permitem que você coloque porções de sua aplicação em módulos separados. algumas vezes você não tem como evitar uma referência circular. Runtime package. Ele pode ser instalado na biblioteca de componentes do Delphi usando o item do menu Component | Install Package. você apreciará que você pode tirar vantagens de packages sem qualquer mudança em seus códigos fontes existentes. O VCL30. Quatro tipos de packages estão disponíveis para você criar e usar: 1. e você deve evitar estruturar seu programa com uma referência circular. Este tipo de package contém elementos necessários para o projeto de sua aplicação tais como componentes.DPL do Delphi são exemplos deste tipo de package.

Units listadas na cláusula contains serão compilados dentro deste pacote. sua aplicação será linkada dinâmicamente aos pacotes em tempo de execução ao invés de ter units linkadas estaticamente dentro de seu EXE ou DLL. Note também que qualquer unit usada pelas units na cláusula contains serão implicitamente arrastadas para dentro deste package (a menos que elas estejam contidas em um package requerido). 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. Nem runtime nem design package. mas este tipo de package é menos eficiente porque ele deve carregar a bagagem de projeto mesmo em suas aplicações distribuídas. Note que units listadas aqui não devem estar listadas também na cláusula contains de algum dos packages listados na cláusula requires. Os packages listados na cláusula requires são requeridos na ordem para este pacote carregar. Simplismente marce o check box Build with Runtime Packages no diálogo Project | Options | Packages. gerando argumentos sobre seus méritos (ou falta disso). Ter dados e código do objeto juntos simplifica o processo de procurar e encontrar bugs. Runtime e Design package... Package2. consertando-os com um efeito mínimo sobre os outros . O resultado será uma aplicação muito mais talhada (embora tenha em mente que você terá que disponibilizar os packages necessários com sua aplicação). que será compilado dentro de um package. o resultado do uso da POO tradicionalmente tem sido um código fácil de se manter. .. Este package serve para os propósitos listados nos itens 1 e 2. e está no seguinte formato: package PackageName requires Package1. Criar este tipo de package torna o desenvolvimento e a distribuição de sua aplicação um pouco mais simples. POO se parece mais com uma religião do que com uma metodologia de programação. A sintaxe para este arquivo DPK é muito simples. Unit2. Utilizando Packages do Delphi Utilizar packages em suas aplicações é fácil. Na próxima vez que você construir sua aplicação depois de ter selecionado esta opção. Não nos envolveremos nos méritos relativos da POO. POO é um paradigma de programação que usa objetos discretos—contendo dados e código— como blocos que constroem a aplicação. packages contendo units usadas por units listadas na cláusula contains são listados aqui. end. . apenas queremos dar a você um conhecimento sobre os princípios fundamentais sobre os quais a linguagem Object Pascal do Delphi é baseada.55 3. contains Unit1. Tipicamente. Embora o paradigma POO não necessariamente proporcione a você um código fácil de escrever.. Programação Orietada a Objetos Livros tem sido escritos sob o título de programação orientada a objetos (POO). Sintaxe de Packages Packages são muito comumente criados usando o Package Editor. 4... que você chama escolhendo o item do menu File | New | Package. Este editor gera um arquivo Delphi Package Source (DPK).

Uma Nota sobre Herança Múltipla Object Pascal não suporta herança múltipla de objetos como o C++. Usando interfaces. A primeira solução é fazer uma classe conter a outra classe. Você verá esta solução por toda a VCL do Delphi. Polimorfismo Literalmente. Para construir sob uma analogia da classe bala de morango. e melhorando uma parte do seu programa ao mesmo tempo. criar um objeto que contém todo os códigos e dados dos dois objetos pais. você pode essencialmente ter um objeto que suporta as interfaces de morango e bala. Herança A capacidade de criar novos objetos que mantêm as propriedades e comportamentos dos objetos ancestrais. Você deve compreender os três termos seguintes antes de continuar a explorar os conceitos de objetos: . ela pode introduzir mais problemas e ineficiências em seu código do que ajudar se você não souber utilizá-la.A vantagem da herança é divisão de código comum. Tradicionalmente. Herança Múltipla é o conceito de um dado objeto sendo derivado de dois objetos separados. A segunda solução é usar interfaces (você aprenderá mais sobre interfaces na seção "Interfaces"). Chamar métodos de uma variável do objeto chamará o código apropriado para qualquer uma das instâncias que é na verdade uma variável.56 objetos. As vantagens do encapsulamento incluem modularidade e isolamento do código de outros códigos. polimorfismo significa "muitas formas". você poderia fazer do objeto morango um membro do objeto bala. Hernaça múltipla permite que você crie um objeto bala de morango criando um novo objeto que herde a classe maçã e a classe chamada morango. Object Pascal fornece duas aproximações para resolver este problema. Este conceito permite que você crie hierarquias de objetos tais como VCL —criando primeiro objetos genéricos e então criando objetos que tem funcionalidades mais específicas. uma linguagem de POO contém a implementação de no mínimo três conceitos da POO: Encapsulamento Trata de combinar campos de dados relacionados e ocultar os detalhes de implementação. Embora esta funcionalidade pareça útil.

Em C++. classes de objetos são declaradas na seção type a unit de um programa: type TFooObject = class. não-visuais ou forms de tempo de projeto. Propriedade Uma propriedade é uma entidade que acessa os dados e códigos contidos dentro de um objeto. Nota: Geralmente é considerado um mau estilo de POO acessar um campo de um objeto diretamente. use propriedades de acesso. Além da classe do objeto você possivelmente terá uma variável daquela classe. sejam eles visuais. ou instância. Como descrito anteriormente. Ambientes tais como esses frequentemente são chamados de ambientes baseados em objetos. Embora você possa usar um controle OCX em suas aplicações. Um construtor é responsável por criar uma instância de seu objeto e alocar qualquer memória . campos algumas vezes são referidos como membros. Delphi é um ambiente orientado a objetos. declarada na seção var: var FooObject: TFooObject. Propriedades são explicadas na seção "Propriedades". Controles OCX no Visual Basic 4 são um bom exemplo disso. Objetos do Delphi também fornece suporte para herança. você deve declarar a classe do objeto usando a palavra-reservada class. você manipula entidades (objetos). Um campo em um objeto é como um campo em um registro do Pascal. encapsulamento e polimorfismo. você não pode criar um e não pode herdar um controle OCX de um outro no Visual Basic. Você cria uma instância de um objeto em Object Pascal chamando um de seus construtores. Propriedades separam o usuário final dos detalhes de implementação de um objeto. Declaração e Instanciação Antes de usar um objeto. mas não pode criar seus próprios objetos. Em vez disso. Métodos são chamados funções membros em C++.57 Campo Também chamado de campos de definição ou variáveis de instância. Isto porque os detalhes de implementação do objeto podem mudar. as quais permitem a interface padrão com o objeto sem se preocupar nos detalhes de como os objetos são implementados. Método O nome dos procedimentos e funções que fazem parte de um objeto. Isto inclui todos os objetos do Delphi. Usando Objetos do Delphi Como mencionado anteriormente. Isto significa que você pode criar novos objetos no Delphi ou do nada ou baseado em componentes existentes. campos são as variáveis de dados contidas dentro dos objetos. objetos são entidades que podem conter dados e códigos. Baseado em Objetos Versus Orientado à Objetos Em algumas ferramentas.

Diferente do C++. mas o código para TFooObject. em vez disso chame o método Free(). Destruição Quando você finaliza o uso de um objeto. eles serão liberados para você. A sintaxe é simples: FooObject. Dependendo do tipo do objeto. está estático na memória. FooObject. todos os ponteiros para Nil. Lembre-se de nunca chamar Destroy() diretamente. Nota: Quando uma instância de um objeto é criada usando o cosntrutor. ele desaloca qualquer memória alocada e efetua qualquer outro procedimento de limpeza requerido a fim de remover corretamente o objeto da memória. Você faz referência ao método Create() do objeto pelo tipo e não pela instância como você faria com outros métodos. mas você deve chamar o destrutor para quaisquer objetos alocados dinamicamente. Você certamente não os declarou por conta própria. você deve liberar. Cuidado: Em C++. Diferente da chamada para Create(). Você pode assumir com certeza que todos números serão inicializados para 0. o destrutor de um objeto declarado estaticamente é chamado automaticamente quando seu objeto sai do escopo. A exceção a esta regra é que quando seus objetos são propriedade de outros objetos.Free. e então ele chama o método destrutor do objeto. Este tutorial foca o caso simples onde Create() não pega nenhum parâmetro. A sintaxe para chamar um construtor é a seguinte: FooObject := TFooObject.58 ou inicializar qualquer campo necessário de forma que o objeto esteja em estado de uso depois da saída do construtor. um tipo. Os métodos . certo? Certo. é indefinida em tempo de chamada. faz o oposto do construtor. o compilador assegurará que todo campo em seu objeto será inicializado. Uma chamada estática para seu método Create() é então totalmente válido. Objetos em Object Pascal sempre tem no mínimo um construtor chamado Create()—embora seja possível para um objeto ter mais de um construtor. uma variável.Create. O destrutor. construtores de objetos em Object Pascal não são chamados automaticamente e é incumbido ao programador chamar o construtor do objeto. mas vai fazer sentido. assim você deve seguir a regra de que qualquer coisa que você criar. O ato de chamar um construtor para criar uma instância de um objeto é frequentemente chamado de instanciação. Isto pode parecer estranho no início. Você pode estar se perguntando como todos esses métodos ficam dentro de um pequeno objeto. exceto pelo fato de que todos os objetos são implicitamente dinâmicos em Object Pascal. é claro. e todas as strings estarão vazias. a instância do objeto é usada na chamada para o método Free(). Repare que a sintaxe para chamada de um construtor é um único pedaço. O método Free() primeiro checa para garantir que a instância do objeto não é Nil. você deve desalocar a instância chamando seu método Free(). Create() pode tomar diferentes números de parâmetros. A regra é a mesma em Object Pascal. Destroy().

Tipos de Métodos Métodos do objeto podem ser declarados como static. ou message. procedure TBoogieNights. procedure DoTheHustle. . todos os objetos são sempre descendentes de TObject independentemente de serem declarados como tal ou não. Métodos Métodos são procedimentos e funções pertencentes a um dado objeto. É importante também notar que o campo Dance do objeto pode ser acessado diretamente de dentro do método. end. Você também pode criar métodos customizados em seus objetos para efetuar outras tarefas. end. message wm_AlgumaMensagem. end. como você fez quando definiu o método DoTheHustle. é equivalente a declaração Type TFoo = Class(TObject). procedure EuSouUmDinâmicp. Considere o seguinte objeto exemplo: TFoo = class procedure EuSouUmEstatico. begin Dance := True. Então a declaração Type TFoo = Class. procedure EuSouUmVirtual. Note que quando definimos o corpo do método. dynamic. e então você deve definir o método no código. você tem que usar o nome qualificado completo. procedure EuSouUmMensagem(var M: TMessage).59 discutidos até agora na verdade vem do objeto base do Object Pascal: TObject. virtual. Métodos são aquelas coisas que dão a um objeto o comportamento diferente de um simples dado. Você deve primeiro declarar o método na classe do objeto. Em Object Pascal.DoTheHustle. O código seguinte demonstra o processo de declaração e definição de um método: type TBoogieNights = class Dance: Boolean. que já discutimos. virtual. Dois métodos importantes dos objetos que você cria são os métodos construtor e destrutor. Criar um método é um processo de dois passos. dynamic.

Métodos Virtuais são chamados da mesma forma que métodos estáticos.VMT) que fornece um meio de descobrir os endereços das funções em tempo de execução. O compilador. Polimorfismo (Métodos Impostos) Os métodos com a palavra reservada override (imposição) são a implementação de Object Pascal de polimorfismo. Você pode reescrever os métodos EuSouUmVirtual e EuSouUmDinamico como no exemplo seguinte: TFooChild = class(TFoo) procedure EuSouUmVirtual. a DMT de um objeto contém apenas os métodos dinâmicos que ele declarou. Por causa disto. mas como métodos virtuais podem ser redefinidos nas classes descendentess. e aqueles métodos que dependem das DMTs de seus ancestrais para o resto de seus métodos dinâmicos. então. O compilador conhece o endereço desses métodos. O método estático é o tipo de método default e ele funciona similarmente a um procedimento ou função regular. embora eles executem mais rapidamente. e você geralmente não as chamará diretamente.60 Métodos Estáticos IAmAStatic é um método estático. . Todos os métodos virtuais chamados são remetidos em tempo de execução através da VMT. Os métodos em Object Pascal podem ser reescritos nas classes descendentes somente se eles forem declarados pela primeira vez como virtual ou dynamic. ele é capaz de linkar aquela informação dentro do executável estáticamente. O compilador atribui um único número a cada método dinâmico e usa esses números. quando você chama um método estático. Métodos de Mensagem EuSouUmaMensagem é um método manipulador de mensagens. Diferentemente da VMT. O valor depois da palavra reservada message dita para qual mensagem o método responderá. mas eles são mais lentos porque eles podem ter que propagar através de muitas DMTs de ancestrais antes de encontrar o endereço de um método dinâmico particular. Métodos estáticos executam mais rapidamente. constrói uma Tabela de Métodos Virtuais (Virtual Method Table .DMT). o compilador não conhece o endereço de uma função virtual particular quando você a chama em seu código. e assim. Métodos Dinâmicos IAmADynamic é um métod dinâmico. Eles possibilitam que você altere o comportamento de um método de descendente para descendente. assim métodos virtuais usam mais memória do que métodos dinâmicos. métodos dinâmicos são utilizam menos memória do que métodos virtuais. override. junto com os endereços dos métodos. Métodos Virtuais IAmAVirtual é um método virtual. apenas use a diretiva override no lugar de virtual ou dynamic em sua classe descendente. Para reescrever um método. para construir uma Tabela de Métodos Dinâmicos (Dynamic Method Table . Métodos de mensagem são usados para criar uma resposta automática para as mensagens do Windows. Uma VMT do objeto contém todos os metodos virtuais de seus ancestrais assim como os que foram declarados nele. Métodos Dinâmicos são basicamente métodos virtuais com sistema diferente de execução. contudo eles não podem ser redefinidos em classes descendentes para fornecer polimorfismo.

Sobrecarga de Métodos Assim como procedimentos e funções regulares. Self é passada pelo compilador como um parâmetro oculto para todos os métodos. end. você pode querer adicionar um método a uma de suas classes para para substituir um método de mesmo nome em um ancestral de sua classe. end. procedure UmMetodo(S: string). Neste caso. message wm_AlgumaMensagem. A diretiva override troca a entrada do método original na VMT com o novo método. reintroduce. end. Se você tiver redeclarado EuSouUmVirtual e EuSouUmDinamico com as palavras reservadas virtual ou dynamic no lugar de override. . Também. use a diretiva reintroduce sobre o método na classe ancestral. O código a seguir demonstra o uso correto da diretiva reintroduce: type TAlgumaBase = class procedure Cooper. Para anular este erro. se você tentar reescrever um método estático em uma classe descendente. overload. métodos podem ser sobrecarregados de forma que uma classe possa conter múltiplos métodos com o mesmo nome com diferente listas de parâmetros. Métodos sobregarrecados devem ser marcados com a diretiva overload. O código exemplo a seguir mostra uma classe contendo três métodos sobrecarregados: type TAlgumaClasse = class procedure UmMetodo(I: Integer). end. overload. você verá que o compilador produzirá um alerta explicando que o novo método oculta um método de mesmo nome em uma classe base. procedure UmMetodo(D: Double). você terá que criar novos métodos em vez de reescrever os métodos ancestrais. o método estático no novo objeto trocará totalmente o método na classe ancestral. você não quer cancelar o método do ancestral mas sim obscurecer e suplantar completamente o método da classe base. overload. TAlgumaClasse = class procedure Cooper. Self Uma variável implícita chamada Self está disponível dentro dos métodos de todos os objetos. Self é um apontador para a instância da classe que foi usada para chamar o método.61 procedure EuSouUmDinamico. Se você simplesmente adicionar o método e compilar. Reintroduzindo Nomes de Métodos Ocasionalmente. embora o uso da diretiva na primeira instância do nome de um método em uma classe hierárquica seja opcional. override. procedure EuSouUmaMensagem(var M: TMessage).

function ProtejaMe: Byte. public. begin if AlgumValor <> UmValor then AlgumValor := UmValor. Isto é útil por duas razões: Primeiro. O exemplo seguinte ilustra um Object simples com uma propriedade: TMeuObjeto = class private AlgumValor: Integer. ele lê o valor de AlgumValor. Valor é um acesso para o campo AlgumValor. public property Valor: Integer read AlgumValor write AlteraAlgumValor.62 Propriedades Pode ser mais fácil de pensar em propriedades como campos de acesso especiais que permitem que você modifique dados e execute o código contido dentro de suas classes. published property UmaPropriedade read UmaVariavelPrivada write .AlteraAlgumValor(UmValor: Integer). end. Especificadores de Visibilidade Object Pascal oferece a você controle sobre o comportamento de seus objetos permitindo que você declare campos e métodos com diretivas tais como protected. private. Quando você tentar alterar o valor da propriedade Valor. Para componentes. um método—um procedimento chamado AlteraAlgumValor. protected procedure UmProcedimentoProtegido. published e automated. você pode permitir que o usuário reescreva os métodos de acesso em classes descendentes por comportamentos polimórficos. Segundo. OutraVariavelPrivada: Boolean. permite que você conceda ao usuário da classe uma variável simples sem fazer com que ele se preocupe com os detalhes de implementação da classe. TMeuObjeto é um objeto que contém o seguinte: um campo—um inteiro chamado AlgumValor. procedure AlteraAlgumValor(UmValor: Integer). destructor UmAssassinoPublico. end. A propriedade Valor na verdade não contém nenhum dado. e uma propriedade chamada Valor. procedure TMeuObjeto. public constructor UmConstrutorPublico. A sintaxe para o uso dessas palavras é a seguinte: TAlgumObjeto = class private UmaVariavelPrivada: Integer. quando você pergunta a Valor qual número ele contém. Valor chama AlteraAlgumValor para modificar o valor de AlgumValor. propriedades são aquelas coisas que aparecem na janela do Object Inspector quando são editados. O único propósito do procedimento AlteraAlgumValor é alteara o valor do campo AlgumValor.

usuários de seu objeto não serão capazes de modificar o valor de AlgumValor diretamente. e elese terão que utilizar a interface fornecida pela propriedade Valor para modificar os dados do objeto. published Runtime Type Information (RTTI) para ser gerada pela porção pública de seus objetos permite que outras partes de sua aplicação obtenham informação sobre as partes published de seus objetos.63 UmaVariavelPrivada. Você pode colocar quantos campos ou métodos você quiser sob cada uma das diretivas. Agora. Esta capacidade permite que você oculte os detalhes de implementação de seu objeto dos usuários enquanto ainda fornece máxima flexibilidade para descendentes de seus objeto. end. Use esta diretiva para ocultar detalhes de implementação de seus objetos de usuários e para evitar que usuários modifiquem diretamente membros sensíveis de seu objeto. com diretivas adicionadas para melhorar a integridade do objeto: TMeuObjeto = class private AlgumValor: Integer.AlteraAlgumValor(UmValor: Integer). automated O especificador automated é obsoleto mas permanece para manter a compatibilidade com Delphi 2. begin if AlgumValor <> UmValor then AlgumValor := UmValor. procedure TMeuObjeto. O Object Inspector usa RTTI para construir sua lista de propriedades. public Esses campos e métodos são acessíveis de qualquer lugar em seu programa. A seguir o significado dessas diretivas: private Estas partes do seu objeto são acessíveis apenas pelo código na mesma unit da implementação de seu objeto. então. Classes "Amigas" . Construtores e destrutores de objetos sempre devem ser public. procedure AlteraAlgumValor(UmValor: Integer). está o código para a classe TMeuObjeto que foi introduzida anteriormente. Para manter o estilo é melhor que você indente o especificador da mesma forma que você indenta o nome da classe. Aqui. end. protected Membros protegidos de seu objeto podem ser acessados por objetos descendentes. end. published property Valor: Integer read AlgumValor write AlteraAlgumValor.

tipo ou até mesmo se ela é herdade de uma classe particular.64 A linguagem C++ tem um conceito de classes amigas—isto é. class function InitInstance(Instance: Pointer): TObject. para olhos destreinados. por exemplo. todas as classes tem alguns métodos que são inerente de TObject. class function ClassNameIs(const Name: string): Boolean. e o compilador do Object Pascal está "ciente" de TObject. out Obj): Boolean. uma classe mostra-se como uma variável estática. métodos ou propriedades dentro de uma classe. class function ClassInfo: Pointer. O que isto significa. como um programador de aplicações. é que diferentemente de C++. Assim. class function MethodAddress(const Name: ShortString): Pointer. Toda classe tem a habilidade de. Você apenas tira vantagens da funcionalidade que ele propicia! TObject é um objeto especial porque sua definição vem da unit System. ele permite funcionalidade parecida. class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry. Object Pascal oferece nenhuma forma razoável de alocar uma classe de um segmento de dado da aplicação fora do heap. virtual. function GetInterface(const IID: TGUID. estritamente falando. Embora. A beleza disso é que você. class function MethodName(Address: Pointer): ShortString. O código a seguir ilustra a definição da classe TObject: type TObject = class constructor Create. procedure Free. TObject: A Mãe dos Objetos Como tudo descende de TObject. class function InheritsFrom(AClass: TClass): Boolean. class function GetInterfaceTable: PInterfaceTable. class function ClassName: ShortString. function ClassType: TClass. Isto é efetuado em C++ usando a palavra friend. Object Pascal não temnha uma palavra reservada similar. Todos os objetos declarados dentro de uma mesma unit são considerados "amigos" e possuem permisão de acesso a informação privada localizada em outros objetos naquela unit. informar a você o seu nome. procedure CleanupInstance. não tem que se preocupar em que tipo de mágica o compilador faz para que isto aconteça. function FieldAddress(const Name: ShortString): Pointer. classes que podem acessar os dados privados e funções em outras classes. class function ClassParent: TClass. . entretanto. class function InstanceSize: Longint. o compilador automaticamente efetua um pequeno abracadabra que gera o código para identificar aquele apontador para você. Dentro dos Objetos Todas as instâncias de classes em Object Pascal são na verdade armazenadas com apontadores de 32 bits para dados localizados na memória heap. ExceptAddr: Pointer): HResult. function SafeCallException(ExceptObject: TObject. e você pode fazer algumas suposições especiais sobre as capacidades de um objeto. Quando você acessa campos.

Como você pode ver. A diferença principal é que uma interface pode opcionalmente ser associada com um . end. class function NewInstance: TObject.65 procedure AfterConstruction. function _Release: Integer. end. procedure DefaultHandler(var Message). virtual. IUnknown é definida na unit System como segue: type IUnknown = interface [‘{00000000-0000-0000-C000-000000000046}’] function QueryInterface(const IID: TGUID. destructor Destroy. Em particular. procedure FreeInstance. a sintaxe para definir uma interface é muito similar àquela de uma classe. Neste tutorial você aprenderá sobre os elementos de linguagem de interfaces. É o trabalho de uma classe que suporta uma interface implementar cada uma das funções e procedimentos da interface. virtual. ou você obterá um erro de compilação. stdcall. out Obj): Integer. Você encontrará cada um desses métodos documentados na ajuda do Delphi. o qual foi introduzido no Delphi 3. procedure Dispatch(var Message). Isto é similar em conceito à classe VIRTUAL PURA do C++. virtual. Uma classe pode implementar múltiplas interfaces. A definição de uma dada interface é conhecida tanto pelo implementador quanto pelo cliente da interface—atuando como um contrato dizendo como uma interface será definida e usada. uma interface define apenas uma interface pela qual objeto e clientes se comunicam. function _AddRef: Integer. stdcall. virtual. apesar de tudo. fornecendo múltiplas "faces" pelas quais um cliente pode controlar um objeto. virtual. Tenha cuidado. Interfaces Talvez a mais significante adição à linguagem Object Pascal no passado recente foi o suporte nativo para interfaces. note os métodos que são precedidos pela palavr class. Esta é uma interessante funcionalidade que foi emprestada de funções estáticas do C++. todas as interfaces são implicitamente derivadas de uma interface chamada IUnknown. De forma simples. uma interface define um conjunto de funções e procedimentos que podem ser usados para interagir com um objeto. stdcall. Acrescentar a palavra class a um método permite que ele seja chamado como um procedimento ou função normal sem na verdade ter uma instânca da classe da qual o método é um membro. virtual. procedure BeforeDestruction. Como seu nome implica. de não fazer um método da classe depender da informação de alguma instância. virtual. Definindo Interfaces Assim como todas as classes do Delphi são implicitamente descendentes de TObject.

IBar. end.F1: Integer. IBar) function F1: Integer. end. function F2: Integer. begin Result := 0. A definição de IUnknown vem da especificação de Component Object Model (COM) fornecida pela Microsoft.F2: Integer. function TFooBar.66 Globally Unique Identifier (GUID). que descende de IFoo: type IBar = interface(IFoo) [‘{2137BF61-AA33-11D0-A9BF-9A4537A42701}’] function F2: Integer. que implementa um método chamado F1(): type IFoo = interface ['{2137BF60-AA33-11D0-A9BF-9A4537A42701}'] function F1: Integer. begin Result := 0. IFoo. Definir uma interface customizada é fácil se você entender como criar se criam classes no Delphi. Note que múltiplas interfaces podem ser listadas depois da classe ancestra na primeira linha da declaração da classe a fim de implementar múltiplas interfaces. Dica: O IDE do Delphi produzirá novos GUIDs para suas interfaces quando você usar a combinação de teclas Ctrl+Shift+G. O código seguinte define uma nova interface. A ligação de uma função de interface a uma função particular na classe acontece quando o compilador combina a . Implementando Interfaces O seguinte pedaço de código demonstra como implementar IFoo e IBar em uma classe chamada TFooBar: type TFooBar = class(TInterfacedObject. function TFooBar. end. end. o qual é único para a interface. end. O código a seguir define uma nova interface chamada IFoo.

que permite que você delegue a implementação dos métodos da interface para uma outra classe ou interface. A Diretiva implements Novo para o Delphi 4 é a diretiva implements. end. TFooBar = class(TInterfacedObject. IFoo.F1 = BarF1. property Foo: TFoo read GetFoo imlements IFoo. begin Result := 0. function TFooBar. // enchimento end. function IBar. end. end. IBar) // métodos apelidados function IFoo. end.FooF1: Integer. IFoo) // enchimento function GetFoo: TFoo. . end.F1 = FooF1. function TFooBar. Implements é usada como a última diretiva sobre uma propriedade da classe ou da interface como a seguir: type TSomeClass = class(TInterfacedObject. Um erro de compilação ocorrerá se uma classe declarar que ela implementa uma interface mas faltar com a implementação de um ou mais métodos da interface. você deve apelidar os métodos com mesmo nome como mostrado no pequeno exemplo a seguir: type IFoo = interface ['{2137BF60-AA33-11D0-A9BF-9A4537A42701}'] function F1: Integer. begin Result := 0. Esta técnica é algumas vezes chamada de implementação por delegação. function BarF1: Integer. // métodos da interface function FooF1: Integer. IBar = interface [‘{2137BF61-AA33-11D0-A9BF-9A4537A42701}’] function F1: Integer.BarF1: Integer. Se uma classe implementa múltiplas interfaces que tem métodos de um mesmo nome.67 assinatura de um método na interface com a assinatura compatível na classe.

Você provalvelmente não gostaria de implementar aquela interface o tempo todo “só por garantia” porque isto poderia ser um desperdício de recursos. A diretiva implements traz duas vantagens importantes em seu desenvolvimento. digamos que houvesse uma interface cuja implementação requer a alocação de um bitmap de 1MB. . elas são referenciadas. uma referência é automaticamente adicionada quando você obtém uma interface. é automaticamente liberado end. Usando implements. Isto significa que elas são sempre inicializadas com nil. begin // I é inicializado com nil I := FunctionReturningAnInterface. //Se 0. Por exemplo. Primeiro. Utilizando Interfaces Umas poucas regras importantes da linguagem aplicam-se quando você está usando variáveis de tipos de interface em suas aplicações. // contador de referência de I // é incrementado I.SomeFunc. Você podem também fornecer uma lista delimitada por vírgulas de interfaces seguindo a diretiva implements. Agrupamento é um conceito de COM relacionado a combinação de múltiplas classes para um propósito único. // contador de referencia de I é decrementado. mas aquela interface é raramente requerida pelos clientes. o código a seguir é legal usando a classe TFooBar definida anteriormente: procedure Test(FB: TFooBar) var F: IFoo. você poderia criar a classe para implementar a interface sob demanda no método que acessa a propriedade.68 O uso de implements no código exemplo anterior instrui o compilador a procurar na propriedade Foo por métodos que implementem a interface IFoo. A principal regra a lembrar é que interfaces são tipos autosuficientes. Por exemplo. ela permite que você adie o consumo de recursos necessários para implementar uma interface até que eles sejam absolutamente necessários. Uma outra regra única de variáveis de interface é que uma interface é compatível de atribuição com classes que implementam a interface. ela permite que você efetue agrupamentos em uma maneira não tumultuada. Segundo. na qual cada tipo da propriedade deve conter os métodos para implementar as múltiplas interfaces. . O código exemplo a seguir ilustra o gerenciamento de memória de uma variável de interface: var I: ISomeInterface. O tipo da propriedade deve ser uma classe que contém métodos de IFoo ou uma interface do tipo IFoo ou um descendente de IFoo. e elas são automaticamente liberadas quando saem do escopo ou quando recebem o valor nil. // legal porque FB suporta IFoo . begin F := FB.

uses Classes. begin FB := TFooBar.3 demonstra como usar o tratamento de exceções durante E/S de arquivos. Program FileIO. Isto faz com que exceções sejam tão fáceis de implementar e usar em suas aplicações quanto qualquer outra classe. finally CloseFile(F). 'FOO. Finalmente.69 . B: IBar.0. end. Isto é ilustrado a seguir: var FB: TFooBar. uma exceção será gerada. divisão por zero. overflow e underflow numérico e erros de E/S de arquivos. try Reset(F). begin AssignFile(F. . . Delphi contém exceções predefinidas para condições de erro comuns. exceções são uma parte da API Win32. A Listagem 2. S: string. S). exceções foram implementadas na linguagem Object Pascal. // QueryInterface F para IBar . mas a partir do Delphi 2. F: IFoo. Dialogs. o operador de typecas pode ser aproveitado para QueryInterface de uma dada variável de interface para uma outra interface.Create F := FB.TXT'). .0. No Delphi 1. E/S de arquivos usando tratamento de exceções. {$APPTYPE CONSOLE} var F: TextFile. Se a interface requerida não é suportada. tais como falta de memória. Delphi também permite que você defina suas próprias classes de exceções da forma que você achar conveniente em suas aplicações. try ReadLn(F. Listagem 2. O que torna as exceções em Object Pascal fáceis de se usar é que elas são como classes que contém informação sobre o local e a natureza de um erro particular. Tratamento Estruturado de Exceções Tratamento estruturado de exceções (Structured exception handling -SEH) é um método de tratamento de erros que permite que sua aplicação se recupere elegantemente de qualquer forma de condições de erros fatais.3. // legal porque FB suporta IFoo B := F as IBar.

Depois que o arquivo é fechado no bloco finally. Isto significa que o arquivo será fechado e o erro poderá ser tratado adequadamente não importando que erros ocorrerem. Write('Entre com outro número real: ').é On EInOutError do . Em muitos casos. Você apenas efetua algumas tarefas quando eles ocorrem saindo elegantemente de uma situação apertada. programa. Nota: As instruções depois do bloco try. Na Listagem 2. tente executar as instruções entre try e finally.. Uma das principais vantagens que tratamento de exceções fornece a mais que o método tradicional de manipulação de exceções é a capacidade de separar distintamente o código de detecção do erro do código de correção do erro.except. ReadLn(R1). Listagem 2. Quando você usa um bloco try. Writeln('Eu agora dividirei o primeiro número pelo segundo.4. Esteja certo de que o código no seu bloco finally não assume que uma exceção ocorreu. isso significa que você não se preocupa com quais exceções podem ocorrer. Você pode capturar exceções específicas usando o bloco try. begin while True do begin try Write('Entre com um número real: '). O outro bloco try. porque eles sempre são prejudicados no caso de um erro.. contudo.finally interno é usado para garantir que o arquivo é fechado não importando se ocorrerá uma exceção ou não. Se uma exceção ocorrer.except. end. O fato de que você não pode capturar qualquer exceção usando o bloco try. Isto é uma coisa boa principalmente porque ele torna seu código mais fácil de ler e manter permitindo que você concentre sobre um aspecto distindo do código de cada vez.finally executam sem se importar se uma exceção ocorreu ou não.4. Se você finalizá-los ou esbarrar com uma exceção. end. ReadLn(R2).. R2: Double. o bloco except propõe uma mensagem informando o usuário que um erro de E/S foi encontrado.'). execute as instruções entre o finally e o end...3.. Program HandleIt. {$APPTYPE CONSOLE} var R1. vá adiante para o próximo bloco de tratamento de exceções".finally é importante.. o qual é ilustrado novamente na Listagem 2. O bloco finally é um lugar ideal para liberar qualquer recurso que você alocou (tais como arquivos ou recursos do Windows). O que este bloco significa em Português é "Ei. você necessita de algum tipo de tratamento de erro que é capaz de responder diferentemente dependendo do tipo de erro que ocorre.. E o fluxo de execução de seu programa continuará até o próximo manipulador de exceção. o bloco try.except é usado para tratar as exceções a medida que elas ocorrerem no programa..70 except on EInOutError do ShowMessage('Erro de Acesso ao Arquivo!'). Um bloco de tratamento de exceções try.finally em seu código.

O elemento importante do objeto Exception é a propriedade Message. e este objeto é definido como segue: type Exception = class(TObject) private FMessage: string.. você também pode pegar outras exceções adicionando a cláusula pega-tudo else para esta construção.. Esses objetos são instanciados quando uma exceção ocorre e são destruídos quando uma exceção é tratada. O objeto de exceção base é chamado Exception. tais como falta de memória ou outra exceção de tempo de execução. end. constructor CreateResHelp(Ident: Integer..except.except. const Args: array of const). constructor CreateResFmtHelp(Ident: Integer. end. const Args: array of const. .else é a seguinte: try Instruções except On EAlgumaException do AlgumaCoisa. A informação fornecida por Message depende do tipo de exceção que ocorreu. constructor CreateFmtHelp(const Msg: string. FHelpContext: Integer. constructor CreateHelp(const Msg: string.. constructor CreateResFmt(Ident: Integer. como mostrado neste exemplo: try Instruções except ManipulaExceção // quase o mesmo que a instrução else end. Classes de Exceção Exceções são simplesmente instâncias especiais de objetos.. Você deve sempre causar novamente (reconstruir) a exceção quando você a capturar com um manipulador de exceção não qualificado.. public constructor Create(const Msg: string). Embora você possa capturar exceções específicas com o bloco try. Você pode alcançar o mesmo efeito da construção try. AHelpContext: Integer). constructor CreateRes(Ident: Integer). Cuidado: Quando usar a construção try. AHelpContext: Integer). você deve estar ciente de que a parte else capturará todas as exceções—mesmo as exceções que você poderia não estar esperando. Isto é explicado na seção "Reconstruindo uma Exceção". const Args: array of const).except. end.except. property Message: string read FMessage write FMessage.. constructor CreateFmt(const Msg: string.71 Writeln('Este não é um número válido!'). AHelpContext: Integer). e use a cláusula de forma econômica. Message fornece mais informação ou explicação sobre a exceção.else não especificando a classe de exceção no bloco try. end. else { faz algum tratamento default } end.else. Tenha cuidado quando usar a cláusula else. property HelpContext: Integer read FHelpContext write FHelpContext.except. const Args: array of const. que é uma string. AHelpContext: Integer).. A sintaxe da construção try.

o identificador (E neste caso) torna-se a instância da exceção causada atualmente.Message). Existem duas maneiras de fazer isso: usar um identificador opcional com o construtor on ESomeException. tal como o que é fornecido pela sua propriedade Message. ou usar a função ExceptObject(). tal como EZeroDivide e EOverflow. e será tratada pelo manipulador default localizado dentro da biblioteca de tempo de execução (runtime library) do Delphi . contudo. esteja certo de que você derivou-o de um objeto de exceção conhecido tal como Exception ou um de seus descendentes. A desvantagem para ExceptObject(). O manipulador default irá propor uma mensagem informando o usuário que uma exceção ocorreu. Você pode também usar a função ExceptObject(). EMathError é o objeto ancestral para uma variedade de exceções relacionadas à matemática. Cuidado: Se você definir seu próprio objeto de exceção. Você pode capturar qualquer uma dessas exceções colocando um manipulador para EMathError como mostrado aqui: try Instruções except on EMathError do // capturará EMathError ou qualquer descendente TrataExceção end. Qualquer exceção que você não manipular explicitamente em seu programa eventualmente ocorrerá. como segue: try Algo except on E:ESomeException do ShowMessage(E. A razão para isto é que assim os manipuladores de exceção genéricos serão capazes de capturar sua exceção. você algumas vezes necessita acessar a instância do objeto de exceção a fim de encontrar mais informação sobre a exceção. Por exemplo. A sintaxe para isto é introduzir o tipo da exceção com um identificador e uma vírgula.72 Quando você trata um tipo específico de exceção em um bloco except. Você pode inserir um identificador opcional na porção on ESomeException de um bloco except e ter a descrição do identificador para uma instância da exceção causada atualmente. Este idenficador é sempre do mesmo tipo da exceção que ele introduz. aquele manipulador também capturará qualquer exceção que forem descendentes da exceção especificada. Quando se manipula uma exceção. end. Neste caso. O exemplo seguinte mostra o uso desta função: try AlgumaCoisa except . que retorna uma instância da exceção causada atualmente. é que ele retorna um TObject que você deve então converter para o objeto exceção de sua preferência.

você deve usar esta sintaxe: Raise EBadStuff.5. que chama Proc2(). Dialogs.finally até que a exceção é finalmente manipulada dentro de Button1Click(). A função ExceptObject() retornará Nil se não houverem exceções ativas. type TForm1 = class(TForm) Button1: TButton. o método Button1Click() chama Proc1(). private { Private declarations } public { Public declarations } end. Para provocar uma exceção definida pelo usuário chamada EBadStuff. a sequência de execução de seu programa propaga até o próximo manipulador de exceção até que a instância de exceção for finalmente manipulada e destruída.Message). então age sobre todo a extensão do programa (e não apenas dentro de um procedimento ou de uma unit). Listagem 2. Sequência de Execução Depois que uma exceção ocorre.DFM} . Esta listagem é a unit principal de uma aplicação do Delphi que consiste de um form com um botão sobre o form. que em fila chama Proc3(). Messages.Create('Algum mau negócio aconteceu. Este processo é determinado por chamada a pilha. var Form1: TForm1. Dica: Quando você roda seu este programa da IDE do Delphi.5 ilustra a sequência de execução de um programa quando uma exceção ocorre.. por exemplo.73 on ESomeException do ShowMessage(ESomeException(ExceptObject). end. unit Principal. Graphics. procedure Button1Click(Sender: TObject). A Listagem 2. Classes. Controls. A sintaxe para provocar uma exceção é similar a sintaxe para criar uma instância de objeto. implementation {$R *. StdCtrls. Unit principal para o projeto de propagação de exceção. Quando o botão é clicado. você será capaz de ver a sequência de execução melhor se desabilitar o manipulador de exceções integrado do debugger desmarcando Tools | Debugger Options | Preferences | Break on Exception.'). Windows. Forms. e você pode ver a sequência de execução propagar através de cada bloco try. interface uses SysUtils. Uma exceção ocorre em Proc3().

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

A Listagem 2. Ela permite que você molde um objeto de baixo nível para um descendente e causará uma exceção se o typecast for inválido. except // bloco externo sempre efetuará tratamento default on ESomeException do AlgumaCoisa. use a técnica chamada reconstrução de exceção. A palavra reservada as é uma nova forma de typecast de tipo protegido. mas não é apenas um processo acadêmico que ocorre nas sombras do IDE. Suponha que você queira . is e as. A tabela a seguir lista alguns dos métodos de TObject que usam RTTI encontrar informação sobre uma instância particular do objeto.75 bloco. end. try // este é o bloco externo { instruções } { instruções } ( instruções } try // este é o bloco interno especial { alguma instrução que pode requerir tratamento especial } except on ESomeException do begin { tratamento especial para a instrução do bloco inter raise. contém um apontador para sua RTTI e tem muitos métodos embutidos que permitem que você obtenha alguma informação útil dentro da RTTI. RTTI é também a chave para a ligação entre componentes do Delphi e sua incorporação dentro da IDE do Delphi. Esta definição de função poderia ser definida como Procedure Foo(UmObjeto: TObject). Se você quiser fazer algo útil com UmObjeto depois neste procedimento. Reconstruindo uma exceção.6. que permitem comparações e typecast de objetos via RTTI. // reconstrói a exceção para o bloco exter end. Listagem 2. você provavelmente terá que moldá-lo para um objeto descendente. end.6 demonstra um exemplo de reconstrução de uma exceção. Objetos. Função 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 instância Um apontador para a RTTI do objeto na memória Object Pascal fornece dois operadores. em virtude de serem descendentes de TObject. Runtime Type Information Runtime Type Information (RTTI) é uma característica da linguagem que dá a uma aplicação Delphi a capacidade de encontrar informação sobre seus objetos em tempo de execução. Suponha que você tenha um procedimento para o qual você quer ser capaz de passar qualquer tipo de objeto.

Você deveria ter um claro entendimento de POO. tratamento de exceções e RTTI. operadores. tipos. incluindo variáveis. Isto porque uma certo custo elevado é envolvido no uso de RTTI. e porque a primeira linha já determinou que Foo é um TEdit. interfaces. Este texto foi adaptado de Delphi User's Guide .'. Resumo Muita coisa foi coberta neste tutorial. e você quer mudar o texto que ele contém (um TEdit é um controle de edição da VCL do Delphi).'. campos. funções. Você aprendeu a sintaxe básica e a semântica da linguagem Object Pascal. Note que você não usou o operador as para efetuar o typecast neste exemplo. Você pode usar o seguinte código: (Foo as TEdit). procedimentos. métodos. Por exemplo. Você pode usar o operador de comparação Booleana is para checar se dois objetos são de tipos compatíveis. você poderia querer checar para ver se UmObjeto é compatível com TEdit antes de tentar fazer um typecast nele: If (Foo is TEdit) then TEdit(Foo). você pode otimizar efetuando um typecast de apontador na segunda linha. TObject. propriedades. objetos. construtores e estilo. Use o operador is para comparar um objeto desconhecido a um tipo ou instância conhecida para determinar quais propriedades e comportamentos você pode assumir sobre o objeto desconhecido.Text := 'Hello World.Text := 'Hello World.76 assumir que UmObjeto é um descendente de TEdit.

Sign up to vote on this title
UsefulNot useful