Curso básico de C++
DicasBCB
http://www.dicasbcb.com.br
Conteúdo
O bê-a-bá da programação ............................................................................................................................. 5
Afinal, o quê é C++?...................................................................................................................................... 5
Um pouco de história ..................................................................................................................................... 5
Conceitos........................................................................................................................................................ 6
O bit e o byte................................................................................................................................................ 12
Cuidados gerais ............................................................................................................................................ 12
Documentar o trabalho................................................................................................................................. 13
O programa main()....................................................................................................................................... 15
Primeiro programa ....................................................................................................................................... 15
Imprimindo dados nos componentes ........................................................................................................... 28
Comentários ................................................................................................................................................. 31
Tipos fundamentais ...................................................................................................................................... 32
O tipo inteiro ................................................................................................................................................ 33
Os tipos ponto flutuante ............................................................................................................................... 36
O tipo caracter.............................................................................................................................................. 36
Modificadores de tipos................................................................................................................................. 38
Variáveis ...................................................................................................................................................... 40
Atribuição de valores a variáveis ................................................................................................................. 43
Variáveis signed e unsigned......................................................................................................................... 45
Excedendo o limite de uma variável ............................................................................................................ 46
Operações matemáticas com unsigned ........................................................................................................ 47
AnsiString .................................................................................................................................................... 48
Funções que modificam strings ................................................................................................................... 61
Funções que modificam strings ................................................................................................................... 63
AnsiString continuação... (dstring.h) ........................................................................................................... 65
AnsiString continuação... ............................................................................................................................. 66
AnsiString continuação... ............................................................................................................................. 67
A palavra-chave typedef .............................................................................................................................. 67
A diretiva #define ........................................................................................................................................ 68
A palavra-chave const.................................................................................................................................. 69
Operadores matemáticos .............................................................................................................................. 73
Expressões.................................................................................................................................................... 75
Entendendo melhor o C++Builder ............................................................................................................... 78
#include <vcl.h> .......................................................................................................................................... 78
#pragma hdrstop........................................................................................................................................... 83
“Unit1.h”...................................................................................................................................................... 84
“#pragma package(smart_init)” ................................................................................................................... 85
#pragma resource ......................................................................................................................................... 87
_fastcall, __fastcall ...................................................................................................................................... 88
TComponent ................................................................................................................................................ 88
TComponent::Owner ................................................................................................................................... 89
Operadores de incremento e decremento ..................................................................................................... 90
Operadores relacionais................................................................................................................................. 91
O comando if................................................................................................................................................ 92
Apostila de C++ Builder
Pagina -2-
O comando else............................................................................................................................................ 95
if ...else - Continuação ................................................................................................................................. 95
Comandos aninhados e Indentação .............................................................................................................. 97
Operadores lógicos....................................................................................................................................... 98
O operador condicional ternário .................................................................................................................. 99
Funções ...................................................................................................................................................... 100
Chamada de Funções ................................................................................................................................. 102
Definição de uma função ........................................................................................................................... 103
Protótipos de funções ................................................................................................................................. 105
Variáveis locais e globais........................................................................................................................... 107
A palavra-chave extern .............................................................................................................................. 109
A palavra-chave static................................................................................................................................ 111
Parâmetros das funções.............................................................................................................................. 114
O comando return ...................................................................................................................................... 115
Valores Default .......................................................................................................................................... 117
Funções inline ............................................................................................................................................ 118
O comando goto ......................................................................................................................................... 119
O loop while............................................................................................................................................... 119
break e continue ......................................................................................................................................... 122
O loop while sem fim................................................................................................................................. 122
loop while - continuação ............................................................................................................................ 123
O loop do... while....................................................................................................................................... 124
O loop for................................................................................................................................................... 126
loop for - continuação ................................................................................................................................ 127
Omissão e aninhamento no loop for .......................................................................................................... 128
O comando switch...................................................................................................................................... 130
métodos Canvas para desenhar objetos gráficos........................................................................................ 138
propriedades Canvas .................................................................................................................................. 140
TPen::Width............................................................................................................................................... 143
TPen::Mode................................................................................................................................................ 145
TPenRecall ................................................................................................................................................. 146
Usando brushes .......................................................................................................................................... 146
TBrush::Color ............................................................................................................................................ 148
TBrush::Style ............................................................................................................................................. 148
TBrush::Bitmap.......................................................................................................................................... 150
TBrushRecall ............................................................................................................................................. 151
Ler e inserir pixels ..................................................................................................................................... 151
TCanvas::Pixels ......................................................................................................................................... 152
TBitmap::ScanLine .................................................................................................................................... 153
Propriedades e métodos comuns de Canvas .............................................................................................. 154
Conhecendo arrays..................................................................................................................................... 165
Excedendo o limite de um array ................................................................................................................ 167
Matrizes multidimensionais ....................................................................................................................... 168
Arrays de caracteres ................................................................................................................................... 172
Estruturas ................................................................................................................................................... 174
Ponteiros .................................................................................................................................................... 176
A reutilização de um ponteiro.................................................................................................................... 180
Apostila de C++ Builder
Pagina -3-
Ponteiros apontando para outros ponteiros ................................................................................................ 181
Ponteiros apontando para structs ............................................................................................................... 182
O nome do array......................................................................................................................................... 184
Variáveis dinâmicas ................................................................................................................................... 186
Mais problemas ... ponteiros soltos............................................................................................................ 190
Ponteiros & constantes............................................................................................................................... 193
Referências................................................................................................................................................. 195
Reatribuição de uma referência ................................................................................................................. 196
Apostila de C++ Builder
Pagina -4-
O bê-a-bá da programação
Não fique impressionado, se um dia você descobrir que o aplicativo que você está usando para ler ou
tratar algum dado em seu computador foi desenvolvido em linguagem de programação C++ (pronuncia-se
"cê mais mais"). Mesmo porque, em poucos dias, você já estará desenvolvendo um processador de texto
que poderá, inclusive, ser usado para substituir o bloco de notas do seu Windows.
Na escolha de uma linguagem de programação, deve-se considerar não apenas a facilidade de
aprendizado, mas principalmente aquilo que se quer e que se pode fazer com ela. Uma escolha errada
pode nos trazer prejuízos e frustrações.
Certamente C++ não é a linguagem cujos conceitos mais facilmente são assimilados, mas também não é
uma linguagem difícil. Na verdade, trata-se de uma linguagem de programação poderosa, para qualquer
tipo de desenvolvimento computacional, e quando se começa compreender os seus conceitos fica bastante
interessante e divertido o seu aprendizado. E não há nenhum inconveniente ou dificuldade em se aprender
C++ como primeira linguagem de programação. O que é importante é que existam a vontade de aprender,
a dedicação e a familiaridade com computadores.
Afinal, o quê é C++?
Segundo Bjarne Stroustrup (criador de C++), "C++ é uma linguagem de uso geral com uma tendência
para a programação de sistemas que: é uma C melhor; suporta abstração de dados; suporta
programação orientada a objetos; e suporta programação genérica".
C++ é um superset de C. Portanto quaisquer códigos escritos para a linguagem C devem funcionar
normalmente em C++, porém, não raras vezes, C++ oferece uma maneira melhor, ou mais de uma
maneira, de se obter o mesmo resultado, o quê nos proporciona grande flexibilidade em nosso trabalho.
C++ é uma linguagem de programação usada para a criação de uma infinidade de programas como
sistemas operacionais, processadores de texto como o Microsoft Word, planilhas eletrônicas como o
Excel, comunicação, automação industrial, bancos de dados, jogos, games, aplicativos multimídia,
navegadores Web como o Internet Explorer etc; é uma linguagem que permite acesso aos recursos do
hardware e vai por aí afora, sem deixar de lado os sistema embutidos usados nos automóveis, celurares
etc.
Um pouco de história
Ocorreu em junho de 1983, a primeira utilização de C++ fora de uma organização de pesquisa. Podemos
considerar C++ um resultado evolutivo da linguagem de programação BCPL, criada por Martin Richards
Apostila de C++ Builder
Pagina -5-
e que rodava num computador DEC PDP-7, com sistema operacional UNIX. Ken Thompson, em 1970,
efetuou algumas melhorias na linguagem BCPL e a chamou de linguagem "B". Em 1972, Dennis M.
Ritchie, no Centro de Pesquisas da Beel Laboratories, implementou diversas melhorias na linguagem "B"
que, considerada uma sucessora de "B", foi chamada de "C", rodando pela primeira vez num DEC PDP11, em sistema operacional UNIX.
O poder da linguagem "C" logo foi demonstrado, em sua primeira aplicação de peso, quando foi usada
para reescrever o sistema operacional UNIX, até então escrito em linguagem assembly.
Com o tempo, a linguagem "C" tornou-se bastante popular e importante. Contribuiriam para o sucesso o
fato de essa linguagem possuir tanto características de baixo nível quanto de alto nível; a portabilidade da
linguagem, ou seja, poder ser usada em máquinas de diferentes portes e diferentes sistemas operacionais;
bem como o fato de, em meados de 1970, o sistema operacional UNIX ser liberado para as Universidades,
deixando de ficar restrito aos laboratórios. Por volta de 1980, várias empresas já ofereciam diversas
versões de compiladores "C", compatíveis com outros sistemas operacionais, além do original UNIX.
Bjarne Stroustrup criou C++. Claramente, como pondera Stroustrup, C++ deve muito a "C" que foi
mantida como um subconjunto. Também foi mantida a ênfase de "C" em recursos que são suficientemente
de baixo nível para enfrentar as mais exigentes tarefas de programação de sistemas. Outra fonte de
inspiração para C++ foi Simula67, da qual C++ tomou emprestado o conceito de Classes.
Desde 1980, versões anteriores da linguagem, conhecidas como "C com Classes" têm sido utilizadas. O
nome C++, criado em 1983 por Rick Mascitti, representa as mudanças evolutivas a partir de "C", onde
"++" é o operador de incremento em "C".
Bjarne Stroustrup projetou C++ basicamente para poder programar sem ter de usar Assembler, "C" ou
outras linguagens de alto nível. Seu principal objetivo era tornar a escrita de bons programas mais fácil e
mais agradável para o programador individual.
Em março de 1998, o American National Standards Institute (ANSI) aprovou e publicou um padrão para
a linguagem C++. A padronização melhora a portabilidade e a estabilidade dos programas. Usando a
biblioteca Standart de C++, podemos, rapidamente, construir aplicações confiáveis, bem como mantê-las
com menos custo e esforço.
Desde seu desenvolvimento por Dr. Bjarne Stroustrup, C++ foi extensamente usado na construção de
grandes e complexas aplicações como telecomunicações, finanças, negócios, sistemas embutidos e
computação gráfica. A padronização final da biblioteca de C++ torna mais fácil o seu aprendizado,
facilitando seu uso por uma grande variedade de plataformas, o que, por outro lado, significa garantia de
colocação profissional permanente para os bons programadores da linguagem.
Conceitos
Podemos classificar as linguagens de programação em dois grupos: aquelas consideradas de baixo nível e
aquelas consideradas de alto nível.
Apostila de C++ Builder
Pagina -6-
1º. Linguagens de baixo nível.
Quando nascemos, à medida que crescemos, nossa percepção e o contato com a realidade do mundo, nos
anexa às coisas como elas já foram sedimentadas na cultura de um povo. Assim também ocorre com o
idioma e, de um modo geral, com algumas ciências que assimilamos em sequer darmos conta disso.
Por exemplo, quando uma criança conta para outra que possui trinta ou quarenta figurinhas, não percebe,
mas está fazendo uso do sistema numérico decimal (base 10). E o sistema numérico decimal está
incrustado em nosso cotidiano. Via de regra, todos os valores que usamos e todas as contas que fazemos
têm esse sistema numérico como base. Por exemplo, quando escrevo 543, você entende 543 e não tem
dúvidas, pois sabe o que 543 significa. Raciocinando em sistema numérico decimal, também não é difícil
compreender que:
543 = (5 x 10²) + (4 x 10¹) + (3 x 10º)
pois
543 = (5 x 100) + (4 x 10) + (3 x 1)
pois
543 = 500 + 40 + 3.
Infelizmente, os sistemas computacionais não representam valores usando o sistema numérico decimal.
Internamente, os computadores representam valores usando dois níveis de voltagem (normalmente 0v e
+5v). Com esses dois níveis de voltagem, nós podemos representar dois valores. Por convenção, adota-se
o zero e o um. Então, para todos os efeitos, os computadores só conhecem uma linguagem que é
constituída por zeros e uns, ou código de máquina, mais conhecida por linguagem binária. Nesse contexto,
se quisermos representar o número 543, devemos escrever:
1000011111
Qualquer quantia de dígitos binários com valor igual a zero pode anteceder um número binário sem
alterar-lhe o valor. Por exemplo, podíamos representar o número 543 assim:
00000000000000000000000000000000001000011111.
Ante o exposto, não fica difícil compreender que a menor unidade de dados num computador é um dígito
binário. Um dígito binário é o mesmo que um bit, abreviatura de binary digits.
Complicado? No início da era dos computadores (aqueles gigantes à válvula), os programas eram escritos
basicamente em binário, mas, felizmente, longe se vão os dias em que se precisava programar diretamente
em binário, embora ainda exista quem trabalhe nesta base.
O sistema numérico binário tem um modo de funcionamento semelhante ao sistema numérico decimal,
porém com duas diferenças básicas:
Apostila de C++ Builder
Pagina -7-
1ª. o sistema numérico binário contém apenas os dois valores 0 e 1, em vez de 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9; e
2ª. o sistema numérico binário usa potências de 2, em vez de potências de 10.
Embora um bit seja capaz de assumir apenas dois valores (zero e um), o número de elementos que se pode
representar com um único bit é infinitamente grande. Por exemplo, pode-se representar quaisquer dois
valores diferentes com um único bit: falso ou verdadeiro, maior ou menor, redondo ou quadrado e assim
por diante, numa lista incomensurável. Todavia, esse tipo de construção não é usual.
Um sério problema com o sistema binário é o tamanho de suas representações. Veja bem, enquanto a
versão decimal de 543 contém apenas três dígitos: 5, 4 e 3, a representação binária do mesmo valor
contém dez dígitos: 1000011111. Disso decorre que, ao se trabalhar com valores grandes, os números
binários rapidamente se tornam difíceis de ser controlados. Embora seja possível converter binários em
decimal e vice-versa, tal tarefa não é simples nem trivial. Mas é essa a linguagem que a máquina entende.
Alternativamente, tentou-se programar em hexadecimal, que pode representar dezesseis valores entre 0 e
15 decimais. Os números hexadecimais possuem duas características positivas:
1ª. sua conversão para binário é relativamente simples; e
2ª. são razoavelmente compactos.
O sistema hexadecimal é uma base de numeração que começa a ser contado no "0" e termina em "F":
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F.
O sistema hexadecimal possui uma relação simples com o sistema binário, uma vez que para cada grupo
de quatro dígitos binários temos um em hexadecimal.
Apostila de C++ Builder
Pagina -8-
veja a tabela abaixo:
decimal
binário
hexadecimal
1
0001
1
2
0010
2
3
0011
3
4
0100
4
5
0101
5
6
0110
6
7
0111
7
8
1000
8
9
1001
9
10
1010
A
11
1011
B
12
1100
C
13
1101
D
14
1110
E
15
1111
F
16
0001 0000
10
17
0001 0001
11
18
0001 0010
12
19
0001 0011
13
20
0001 0100
14
21
0001 0101
15
22
0001 0110
16
23
0001 0111
17
24
0001 1000
18
25
0001 1001
19
26
0001 1010
1A
27
0001 1011
1B
28
0001 1100
1C
29
0001 1101
1D
30
0001 1110
1E
31
0001 1111
1F
32
0010 0000
20
Apostila de C++ Builder
Pagina -9-
Existem algumas convenções que devem ser adotadas para se diferenciar os sistemas numéricos:
1ª. pode-se colocar um sufixo "d" num valor decimal;
2ª. a letra "b" deve ser colocada no final de todo e qualquer valor binário. Exemplo: 1011 0001b
3ª. a letra "h" deve ser colocada ao final de todo e qualquer valor hexadecimal. Exemplo 4D5Fh;
Não há necessidade de enfatizar que a programação em hexadecimal, embora menos complexa que a
binária, continuava sendo demasiadamente complicada e demorada.
Nesse contexto, surge uma nova linguagem de programação: o Assembly, uma linguagem de símbolos
designados mnemónicas, que são instruções Assembly. Cada mnemónica tem a sua correspondência em
um comando elementar inteligível pelo computador. Nesse tipo de programação, o programador trabalha
diretamente com registradores da CPU e com a memória, entre outras coisas.
Programar em Assembly significa conversar quase que diretamente com a CPU, tal qual os sistemas
binário e hexadecimal. Ou seja, nesses tipos de programação, o programa é escrito em uma linguagem
muito próxima àquela que a máquina entende, e por isso são as linguagens ditas de baixo nível.
Abaixo, exemplo de instrução em Assembly:
a 100
MOV AX, 20
MOV BX, 30
ADD AX, BX
NOP
Do exemplo, AX e BX são registradores internos da CPU. O comando “a 100” determina o endereço
inicial para o programa em 0100h; o programa executa o seguinte: move para o registrador AX o valor 20;
move para o registrador BX o valor 30; soma o conteúdo do registrador AX com o conteúdo do
registrador BX e deixa o resultado em AX; e a instrução NOP finaliza o programa.
2º. Linguagens de alto nível
Quando nos referimos às linguagens de alto nível, estamos falando daquelas que são escritas em um
distanciamento relativo da linguagem máquina. Ou seja, as linguagens de alto nível são aquelas que se
aproximam da linguagem humana, como a Linguagem Basic, C++, Pascal, Java etc.
Por exemplo, Para fazer uma frase aparecer na tela no DarkBASIC, basta digitar:
Apostila de C++ Builder
Pagina -10-
PRINT " Esta frase que está entre aspas será exibida na tela! "
Veja agora, a mesma frase em linguagem C:
main()
{
printf (" Esta frase que está entre aspas será exibida na tela! ");
exit (0);
}
Em Pascal:
program
begin
write (" Esta frase que está entre aspas será exibida na tela! ");
end
Visualmente, o que diferencia estas linguagens de programação daquelas ditas de baixo nível é justamente
o fato de nestas linguagens podermos encontrar algum sentido naquilo que estamos lendo ou escrevendo.
Um simples programa como este que foi apresentado nessas linguagens de alto nível, seria praticamente
ininteligível para nós, se fosse escrito em binário.
De uma forma simplista, podemos dizer que nas linguagens de alto nível, o próprio programa tradutor
(compilador ou interpretador) que usamos se encarrega de conversar com a máquina, transformando todos
os comandos que inserimos no programa para uma linguagem que a máquina compreenda qual a resposta
a ser dada. Ou seja, o tradutor converte o código-fonte em um código executável pela máquina.
Apostila de C++ Builder
Pagina -11-
O bit e o byte
O tipo de dado mais utilizado pelos chips da família 80x86 não é o bit, e sim o byte. Um byte equivale a
um grupo de oito bits. Para facilitar a leitura e a compreensão, costumam-se adotar algumas convenções:
1ª. completar com zeros os números binários para que eles se tornem um múltiplos de quatro ou de oito
bits.
Exemplo: 543 = 001000011111;
2ª. cada grupo de quatro bits deve ser separado por espaço.
Exemplo: 0010 0001 1111.
Um byte pode representar até 256 valores diferentes, pois são oito bits em potência de dois:
28 = 256.
Geralmente um byte é usado para representar valores compreendidos entre 0 e 255 não sinalizados ou
valores entre -127 a 128 sinalizados.
Para os tipos de dados, como os caracteres (letras ou símbolos), que não possuem mais do que 256
elementos, um byte normalmente é suficiente.
Embora sejam visualizadas como letras e símbolos, as constantes caracteres são armazenadas
internamente pelo computador como um número inteiro entre 0 e 255. O caracter A, por exemplo, tem
valor 65; o B, 66; o C, 67; e assim por diante. Os valores numéricos dos caracteres estão padronizados em
uma tabela chamada de American Standard Code for Information Interchange Table ou simplesmente
tabela ASCII.
Um grupo de 16 bits denomina-se word, ou palavra. Com 16 bits podemos representar até 65.536 (216)
valores diferentes: 0 a 65.535 não sinalizados e -32.767 a 32.767 sinalizados. Um dos principais usos para
a palavra são para os valores de inteiros.
Um grupo de 32 bits denomina-se double words. Com 32 bits podemos representar uma infinidade de
tipos de dados, como por exemplo valores ponto flutuante de 32 bits. São 4.294.967.296 (232) valores
diferentes: 0 a 4.294.967.295 não sinalizados e -2.147.483.647 a 2.147.483.647 sinalizados.
Cuidados gerais
Ao escrever um código-fonte em C++, algumas precauções extras devem ser adotadas.
Apostila de C++ Builder
Pagina -12-
1. Deixar o código o mais claro e compreensível possível, inserindo comentários e usando-os
adequadamente, bem como tomar bastante cuidado com a grande liberalidade na forma que o formato
livre admite.
Documentar o trabalho
Em princípio, comentários são textos que inserimos no programa para esclarecer qual é a tarefa que
determinado comando realiza. Comentários são muito importantes em nossos códigos C++. A linguagem
C++ suporta dois estilos de comentários:
a) comentário no estilo C: /* ...*/
/* Neste tipo de comentário, tudo aquilo que estiver inserido entre a
chave de abertura do comentário (barra e asterisco) e a chave de fechamento
do comentário (asterisco e barra) será desconsiderado pelo compilador,
independentemente da quantidade de linhas usadas. */
b) comentário no estilo C++: //
//
//
//
//
//
Neste tipo de comentário, tudo aquilo que estiver inserido à direita
do comentário (barras duplas), até o final da linha
será desconsiderado pelo compilador.
Neste estilo, a cada nova linha,
precisamos iniciar um novo comentário.
Outra aplicação que podemos dar aos comentários, é a de tirar, temporariamente, a validade de uma parte
do programa:
cout << " Esta mensagem aparece na tela! \n";
// cout << " Esta mensagem não aparece na tela! \n";
/* cout << Esta mensagem não aparece na tela! \n"; *
Uma vez iniciado um comentário no estilo C, não podemos inserir outro comentário dentro deste, pois o
compilador considerará apenas a primeira chave de fechamento, o quê ocasionará um erro:
/* Abrimos
fechamento
na verdade
comentário
compilador
um comentário no estilo C; /* até a primeira chave de
não há problemas, pois a abertura desse novo comentário,
não ocorreu, sendo considerada parte integrante do
inicial */ aqui começa a parte ilegal do comentário, e o
deve acusar o erro */
Cuidado com o formato livre
Há uma grande liberalidade na forma em que podemos escrever um código C++, o que significa que o
posicionamento de alguns comandos ou caracteres dentro do texto, de regra, não influenciarão no
resultado da compilação. Esclarecendo melhor, espaços, tabulações, retornos ou comandos escritos em
novas linhas são caracteres desconsiderados pelo compilador.
Abaixo transcrevemos um mesmo programa escrito de três formas diferentes:
Apostila de C++ Builder
Pagina -13-
void main(void)
{
cout << " C++ possui formato livre!!!\n";
getch();
}
void
main
(
void
)
{
cout
<<
" C++ possui formato livre!!!\n"
;
getch
(
)
;
}
void main(void){cout << " C++ possui formato livre!!!\n";getch();}
O resultado apresentado pelo programa acima é rigorosamente o mesmo em qualquer das três hipóteses.
Evidentemente, se, quando trabalharmos num projeto, não tivermos cuidado de escrever um bom e legível
código, o resultado poderá apresentar-se bagunçado, dificultando sua compreensão e, até mesmo, a
localização e conserto de bugs (erros no código que afetam diretamente o resultado) no programa.
Certamente em pouco tempo você já estará desenvolvendo sua própria técnica para escrever bons
programas, usando o formato livre a seu favor.
2. Cuidado com o uso de letras maiúsculas e minúsculas
C++ é Case Sensitive
C++ é Case Sensitive, isto é, letras maiúsculas e minúsculas são interpretadas de forma diferente pelo
compilador. Dentre outras implicações, isso significa que se, por acaso, declararmos uma variável com o
nome Temp, os seguintes nomes terão significados diferentes: TemP; TEMP; temp; tEmp; TeMp. Atenção
especial devemos ter quando escrevemos os comandos da linguagem nas instruções dos códigos-fontes,
Apostila de C++ Builder
Pagina -14-
bem como com algumas funções pré-existentes. Boa parte desses comandos são digitados com letras
minúsculas, como por exemplo: cout, cin, if, else, while, do ... while, for, switch ... case, include,
iostream.h, conio.h, open(), getch(), define, undef, \n, \t, \a etc. Mas essa regra, dependendo do
compilador, não é absoluta e comporta exceções, como algumas funções ou classes internas do C++
Builder, como ShowMessage() ou AnsiString ou quando, por exemplo no IBM-PC, trabalhamos com os
128 caracteres adicionais da tabela ASCII, pois neste tipo de dado, trabalhamos com o código do símbolo
na base hexadecimal.
É bastante comum erros de compilação por causa da sensitividade de C++, mas esse não é um erro difícil
de se corrigir.
O programa main()
Todo programa C++ deve ter uma função principal denominada main(), não podendo haver outra função
com esse mesmo nome, sendo que a execução do programa sempre será iniciado em main().
O menor programa possivel em C++ é:
main() {}
Ele define a função main, que não recebe nenhum argumento e não devolve nada. O programa logo
encerra e o máximo que se percebe é uma janela piscando rapidamente na tela.
Em se tratando aplicações tradicionais Windows, necessário far-se-á entendermos a função WinMain. Em
capítulos posteriores, estudaremos essa função com detalhes
Primeiro programa
Diversos programas-exemplo serão apresentados durante este curso, sendo que os mesmos foram
processados no c++builder 5.02, c++builder 3, c++builder 4 e no c++builder 6, todos da borland. Alguns
desses programas foram escritos com o intuito de ilustrar os tópicos apresentados; outros, para deixar o
curso mais interessante. Sendo assim, procuraremos apresentar tutoriais sobre a confecção de programas,
que irão se completando aos poucos, à medida que avançamos no curso. No final, tudo deverá fazer
sentido.
O código-fonte de um programa C++ pode ser escrito em qualquer editor de texto simples que possa
salvar seu conteúdo em formato de texto puro. Podemos, inclusive, usar o Bloco de Notas do Windows.
Porém, no lugar da extensão .txt, devemos salvar os códigos do programa com extensão .cpp. Contudo, a
forma mais rápida e sensata de se escrever um programa C++ é usando o próprio editor de códigos que
acompanha o ambiente de desenvolvimento C++. Esclarecemos aqui que muitos exemplos deste livro
estão redigidos com fonte formatada (itálica, negrito, colorida etc) apenas para fins didáticos e para
facilitar uma eventual localização no mesmo. Um código-fonte C++ não deverá ser escrito com este
formato e sim, conforme já exposto, em formato de texto puro, não formatado. Se você está procurando
um bom editor de códigos para seus programas, nos atrevemos a indicar o ConTEXT. Usamos esse editor
grandemente na elaboração destas páginas. Com ele ficou muito fácil padronizar a visualização dos
Apostila de C++ Builder
Pagina -15-
códigos fontes, bem como exportar os mesmos, já formatados, para o padrão .html ou .rtf. Para maiores
informações, consulte http://www.fixedsys.com/context
Inicialmente, para compreendermos melhor o conceito dos comandos e das instruções em C++,
trabalharemos um programa definido como aplicação Console Win32, que gera programas parecidos com
o DOS quando estão rodando, mas, na realidade, trata-se de programas de 32 bits executados no ambiente
Windows.
Bem, chega de conversa e vamos por as mãos na obra:
1. inicialize o C++Buider;
2. dê um clique no menu File;
3. dê um clique em New...
4. Na janela que se abrir, selecione a opção Console Wizard e clique em OK;
5. selecione Console em Window Type e clique em finish.
Apostila de C++ Builder
Pagina -16-
O Builder C++, abrirá o editor de textos para você. É nesta janela que digitaremos o código-fonte de
nosso programa.
Observe o próprio BuiderC++, por default (padrão), já digita parte do código que usaremos. Porém essa
sugestão da Borland, neste tipo de aplicação Console Wizard, em princípio, pode ser ignorada, conforme
demonstramos abaixo. Você pode optar por qualquer uma das duas formas para desenvolver seus
programas:
Apostila de C++ Builder
Pagina -17-
Na sugestão da Borland:
#pragma hdrstop
#include <condefs.h>
//---------------------------------------------------------------#pragma argsused
int main(int argc, char* argv[])
{
return 0;
}
devemos digitar o código-fonte de nosso programa imbricando-o no código pré-estabelecido acima.
Outra opção que temos é deletar todo esse código e simplesmente iniciar nosso programa a partir do
código já visto do menor programa possível em C++:
main()
{
}
Esse modelo é aconselhável, se você estiver usando outro tipo de compilador. Caso haja algum conflito
entre o exemplo apresentado e o seu compilador, procure ajuda no manual do mesmo.
Nosso primeiro programa, ao ser compilado, escreverá na saída de vídeo uma string (cadeia de caracteres)
com um espaço de tabulação na segunda linha da tela, levará o cursor para a terceira linha da tela, emitirá
um beep e, por fim, aguardará que uma tecla qualquer seja pressionada para encerrar a aplicação.
Delete toda a sugestão da Borland e, no editor de textos, digite:
#include <iostream>
#include <conio>
main()
{
std::cout << "\n\tEstou fazendo o meu primeiro programa\n\a";
getch();
}
Se preferir manter a sugestão da Borland, o código deverá ficar assim:
#pragma hdrstop
Apostila de C++ Builder
Pagina -18-
#include <condefs.h>
#include <iostream>
#include <conio>
//--------------------------------------------------------------------------#pragma argsused
int main(int argc, char* argv[])
{
std::cout << "\n\tEstou fazendo o meu primeiro programa\n\a";
getch();
return 0;
}
Para Salvar, clique o menu File, escolha a opção Save e, na pasta de sua preferência, salve o programa
com o nome PrimProgram.bpr.
Agora vamos rodar o programa para ver o resultado.
Compilar significa converter o código-fonte em linguagem que o processador (CPU) entenda, ou seja, em
código de máquina, reunindo todos os comandos em um só arquivo. Linkeditar é transformar esse código
fonte em um executável - um .exe (programa pronto).
Para executar o programa, nós podemos pressionar a tecla F9, ou podemos dar um clique em Run no
menu Run, ou podemos clicar a seta verde (Run F9) sob o botão Open Project.
Feito isso, seu primeiro programa produzirá um executável semelhante à figura abaixo:
Apostila de C++ Builder
Pagina -19-
Muito bem, salvado o programa, encerre o aplicativo C++Builder. Abra a pasta onde você salvou o
projeto PrimProgram.bpr e observe que o compilador colocou lá cinco arquivos:
PrimProgram.bpr
PrimProgram.cpp
PrimProgram.exe
PrimProgram.obj
PrimProgram.tds
Ora, mas vejam só, nós já temos um arquivo .exe (PrimProgram.exe) em nossa pasta! Inicialize-o e
perceba que o nosso primeiro programa já roda sem auxílio do compilador. Pois bem, pressione uma tecla
qualquer para encerrar o aplicativo e dê um (ou dois) clique(s) em PrimProgram.bpr para ver o que
acontece: ... Estamos de volta no ambiente de desenvolvimento do primeiro programa.
A seguir, procuraremos entender como conseguimos criar esse programa. Na explanação, aparecerão
nomes como Classes, objetos, macros, funções etc, que talvez lhe pareçam estranhos e incompreensíveis.
Não se preocupe, pois, no momento certo, tudo ficará claro.
Apostila de C++ Builder
Pagina -20-
Diretivas de pré-processador
O pré-processador C++ é um programa que fornece mecanismos para inclusão de arquivos-textos,
cabeçalhos padrão e compilação condicional baseada em macros. Esse programa também pode ser usado
para definir e usar macros.
Talvez você esteja se perguntando: afinal, o quê quer dizer esse monte de coisas?
Por enquanto, apenas para elucidar nosso primeiro programa sem tumultuar nosso entendimento, vamos
nos contentar com uma explicação bem singela:
O pré-processador C++ é um programa que roda antes do compilador e, baseando-se em certas instruções
denominadas diretivas de compilação, examina o programa fonte e implementa-lhe algumas
modificações.
As diretivas de compilação mais comuns são:
#include
#define
#line
#ifdef
#ifndef
#if
#else
#elif
#endif
#undef
#error
#pragma
Apostila de C++ Builder
Pagina -21-
Algumas particularidades:
1ª. todas as diretivas iniciam com #;
2ª. devemos respeitar a regra de colocar somente uma diretiva em cada linha. Ou seja, não podemos
colocar outro comando qualquer na linha em que há uma diretiva.
a diretiva #include
Imagine que muitos dos resultados que esperamos obter de nossos programas já estão, de certa forma,
prontos, na biblioteca padrão, para ser inseridos em nosso código-fonte. Ou seja, não temos necessidade
de desenvolver determinados arquivos (classes, funções ou gabaritos) porque eles já existem e podem ser
usados em qualquer momento que precisemos deles.
Quando o pré-processador encontra a diretiva #include em nosso código fonte, substitui a mesma pelo
conteúdo do arquivo indicado (no nosso exemplo, o conio e o iostream). O resultado é que o compilador
encontrará e processará o arquivo implementado, e não a diretiva de compilação.
A diretiva #include é um mecanismo de tratamento do código-fonte que reúne, em um único arquivo de
compilação, códigos (arquivos de cabeçalho ou hearder files, ou arquivos que contenham funções ou
definições de dados) que se encontram dispersos em vários arquivos.
Para incluir os herder files em nosso código-fonte, devemos usar os símbolos < e > ou " e " para
envolver o arquivo de cabeçalho:
#include <iostream>
ou
#include "iostream.h"
Se deixarmos espaço entre o caracter < e o nome do arquivo de cabeçalho, ou entre o caracter " e o nome
do arquivo de cabeçalho, o compilador indicará erro:
< iostream.h> // [C++ Error] Project2.cpp(4): E2209 Unable to open include file ' iostream'.
Convencionou-se usar para os arquivos de cabeçalho o sufixo .h e para outros tipos de arquivos que
contenham funções ou definições de dados, o sufixo .hpp, .c, ou .cc, .cpp, .cxx, .C, conforme o
compilador. No caso do C++Builder, o arquivo que se encontra associado é o .hpp, responsável pela
interface com a VCL, que foi escrita em Object Pascal.
Historicamente, o sufixo .h era padrão para todos os arquivos de cabeçalho. Entretanto, para evitar
problemas de compatibilidade com cabeçalhos de versões redefinidas de biblioteca padrão, ou com novos
recursos de biblioteca que foram surgindo, resolveu-se suprimir o sufixo .h dos nomes de cabeçalho
padrão (podemos dizer que cabeçalho padrão é o arquivo de cabeçalho definido na biblioteca padrão). A
Apostila de C++ Builder
Pagina -22-
partir de então, nenhum sufixo é necessário para cabeçalhos da biblioteca padrão: <iostream>, <conio>,
<map> etc.
A principal diferença entre a biblioteca padrão e a não padrão é que, enquanto uma biblioteca não padrão
tem de ser incorporada manualmente, a padrão é incorporada automaticamente pelo compilador. É
importante saber, ainda, que os recursos da biblioteca padrão estão definidos num ambiente de nomes
chamado std e apresentados como uma série de cabeçalhos.
O arquivo de cabeçalho iostream
Para o nosso programa imprimir a string Estou fazendo o meu primeiro programa no vídeo, nós
providenciamos para que a Classe iostream fosse chamada à compilação através da diretiva #include. A
biblioteca iostream contém as declarações necessárias ao uso de streams de entrada (teclado) e saída
(vídeo), como, por exemplo, o objeto cout e o operador de inserção <<. Obtém-se o operador de inserção
<< digitando-se o caracter "menor que" duas vezes.
O objeto cout é o responsável pela saída, no console ou na tela, da string que digitamos: Estou fazendo o
meu primeiro programa.
O operador de inserção << (envie para) escreve o argumento da direita (a string) no da esquerda (no
cout).
Além do cout e do operador de inserção << a Classe iostream contém outras definições de impressão e
leitura (I/O), que estudaremos mais adiante.
Você deve ter notado que demonstramos a inclusão dos arquivos de cabeçalho da Classe iostream de três
modos diferentes:
#include <iostream> // Cabeçalho padrão sem sufixo .h
#include "iostream.h" // Cabeçalho de biblioteca não padrão
#include <iostream.h> // Cabeçalho padrão com sufixo .h
Não há necessidade de inserirmos nenhum sufixo para cabeçalhos da biblioteca padrão, porque os
símbolos < > já denotam o chamamento de cabeçalhos padrão. Contudo, as declarações dos cabeçalhos
mais antigos, são colocadas no ambiente de nomes global, carecendo do sufixo .h.
Os cabeçalhos colocados entre aspas " " determinam que o pré-processador procure o cabeçalho primeiro
no diretório atual, e depois na biblioteca padrão (Include).
Em nosso programa: #include<iostream> instrui o pré-processador a incluir os recursos de entrada e saída
de iostream padrão, e std::cout determina a inclusão do cout padrão.
Apostila de C++ Builder
Pagina -23-
Se quisermos, podemos tornar todos os nomes std globais, o que nos poupa da tarefa de ter que digitar
constantemente o prefixo std:: antes de cout:
#include <iostream>
using namespace std;
Se algum dos nossos programas não rodar, podemos usar uma versão mais tradicional. Para isso
colocamos o sufixo .h nos cabeçalhos e removemos o prefixo std::. Exemplo:
#include <iostream.h>
#include <conio.h>
main()
{
cout << "\n\tEstou fazendo o meu primeiro programa\n\a";
getch();
}
Boa parte dos programadores, talvez por praticidade, parece preferir essa segunda versão. Daqui para
frente, escolha é sua!
O arquivo de cabeçalho conio
Nosso programa possui outro cabeçalho, o arquivo conio. Trata-se de um arquivo de inclusão necessário à
utilização da função getch(). Quando descrevemos o que o nosso programa faria, afirmamos que, por fim,
ele aguardaria que uma tecla qualquer fosse pressionada para encerrar a aplicação. Essa tarefa foi
desempenhada pela função getch() que retorna o caracter lido no teclado, sem imprimi-lo no vídeo. Se
quisermos que o caracter lido seja impresso no vídeo, podemos usar a função getche(), também integrante
de a conio.
Lembra-se do menor programa possível em C++, quando afirmamos que tudo o que veríamos seria um
breve piscar da janela na tela e o encerramento do programa? Pois bem, poderíamos usar getch() para
segurar a janela na tela até que uma tecla fosse pressionada!
Caracteres especiais
Antes que nossa string fosse imprimida no vídeo, o programa pulou uma linha (caracter de nova linha) e
imprimiu uma caracter de tabulação horizontal. Depois da string, o programa colocou o cursor na linha de
baixo (nova linha) e emitiu um beep (sinal sonoro).
Como fizemos isso?
Apostila de C++ Builder
Pagina -24-
Alguns caracteres não podem ser inseridos diretamente pelo teclado. Por exemplo, se, durante a
elaboração do código-fonte, no meio da string, pressionarmos a tecla Enter pensando que estaremos
inserindo uma nova linha, faremos com que o compilador apresente uma mensagem de erro.
A solução é usarmos algumas combinações de caracteres que realizam essas tarefas. Essas combinações,
conhecidas como seqüência de escape, são representadas por uma barra invertida \ e um caracter. Por
exemplo, a combinação \n representa nova linha (Enter), \t representa tabulação horizontal (Tab) e \a
representa o beep. Essas seqüências de escape, nós já conhecemos. Vejamos outras:
Escape
Nome
\a
Bell (alerta)
007
emite um som do computador
\b
Backspace
008
retrocede um caracter
\n
New Line
010
envia o curso para a próxima linha
Carriage
013
move o cursor para o início da linha
Horizontal
009
põe o cursor para a próxima tabulação
\r
Valor ASCII
Descrição
return
\t
Tab
\xff
\0
representação hexadecimal
Null
000
exibe um caracter nulo
\"
Aspas duplas
034
exibe aspas duplas (“)
\'
apóstrofe
039
exibe apóstrofe (')
\?
Interrogação
063
exibe ponto de interrogação
\\
Barra invertida
092
exibe barra invertida
String(s) de texto
Constante caracter é uma letra ou símbolo colocado entre apóstrofes: 'a', 'b', '~', 'A', 'B', '\n, '\r', '\xa0',
'\xa2', 'xa3' etc.
Uma importante atividade nos programas em geral é a manipulação de textos como palavras, frases,
nomes etc. Na realidade, esses textos são constantes string(s), que se compõem de um conjunto de
caracteres colocados entre aspas: "Estou fazendo o meu primeiro programa",
"constante string", "\tOuviram do Ipiranga as margens pl\xa0"
"cidas\n\tde um povo her\xa2ico o brado retumbante\n\te o sol da
liberdade em raios f\xa3lgidos,\n\tbrilhou no c\x82u da P\xa0tria nesse
instante.\n"
Apostila de C++ Builder
Pagina -25-
Funções
Funções são a base de qualquer programa C++. Elas compõem, juntamente com as Classes, os blocos
construtivos da linguagem. Dentro das funções, encontramos todos os comandos executáveis, sendo que
as mesmas são a maneira típica de se obter alguma execução em C++.
Vamos reescrever o primeiro programa. Por enquanto, não queira compreender todos os detalhes da
alteração, uma vez que, por ora, nossa preocupação é apenas esclarecer, sem pormenores, quais são os
elementos básicos de uma função. Em outra seção deste curso, abordaremos as funções de uma forma
mais abrangente e minuciosa.
Inicialize PrimProgram.bpr, caso não esteja com ele aberto. No compilador, clique em Edit e depois em
Select All; Clique novamente em Edit e depois em Delete. Agora copie o código abaixo no editor de
textos do compilador.
/*Dica: não use o recurso de copiar e colar códigos-fontes
quando estiver estudando.
Procure digitar todo o código e entender o resultado que cada. instrução
conhecida produzirá no programa.*/
#include <iostream>
#include <conio>
using namespace std; // torna todos o nomes std globais
void ModifPrimProg(void); // declara a finção ModifPrimProg
int main() // estamos em main()
{
ModifPrimProg(); // chama a função ModifPrimProg
return 0;
} // fim de main()
void ModifPrimProg(void) // cabeçalho da definição da função
{
cout << "\n\t\tModificando o primeiro programa."
"\n\n\n\t\tDigite dois caracteres mas veja "
"s\xa2 o primeiro: ";
getche(); // retorna e imprime o caracter lido no vídeo
cout << '\a'; // beep - sinal sonoro
getch(); // retorna o caracter sem imprimi-lo no vídeo
} // fim de ModifPrimProg()
Execute o programa e volte aos estudos.
Com essa pequena alteração no código, podemos visualizar melhor alguns detalhes sobre as funções.
Uma função deve ter a seguinte forma geral:
Apostila de C++ Builder
Pagina -26-
tipo nome(declaração dos parâmetros)
{
Primeira_Instrução;
Segunada_Instrução;
Etc_instrução;
}
Nosso programa agora possui quatro funções: main(), ModifPrimProg(), getche() e getch(). O nome
delas é: main, ModifPrimProg, getche e getch respectivamente. A função main(), como sabemos, é a
principal, é aquela onde a execução do programa se inicia; ModifPrimProg() é uma função que não
estava em biblioteca alguma, tendo sido criada por nós; getche() e getch() nós também já conhecemos do
arquivo de cabeçalho conio.
Precedendo o nome da função main() temos o tipo int, que informa ao compilador que main() pode
retornar um tipo int. Já a função ModifPrimProg() é do tipo void. O tipo void significa que a função
não possui valor de retorno. Quando nenhum tipo de retorno for especificado, presume-se o valor de
retorno int para a função. As funções retornam valores por meio do comando return.
Após o nome de qualquer função temos parênteses de abertura "(" e de fechamento ")". Nesses parênteses,
informamos ao compilador a lista de parâmetros que a função pode receber. Logo,
ModifPrimProg(void) não pode receber nenhum parâmetro por causa do tipo void.
Entende-se por cabeçalho da definição da função o conjunto tipo nome(declaração dos parâmetros) que
antecede a chave "{" de abertura do corpo da função. Entre a chave de abertura "{" e a de fechamento "}"
do corpo da função, encontramos as tarefas (instruções) que a mesma realiza.
Instruções são as tarefas que determinados comandos realizam. Toda instrução é encerrada com um ponto
e vírgula ";":
cout << "uma instrução!";
getch();
return 0;
A falta do ponto e vírgula ensejará mensagem de erro pelo compilador. Porém, não se coloca ponto e
vírgula após as chaves de abertura e fechamento das funções.
Apostila de C++ Builder
Pagina -27-
Não se preocupe em entender, por ora, todos os conceitos apresentados, posto que os abordaremos
novamente no correr do curso. Até aqui, tivemos apenas a intenção de colocá-los em contado com o
ambiente de programação C++Builder e com os fundamentos da linguagem C++.
Imprimindo dados nos componentes
Se você ainda não se sente familiarizado com o C++Builder, ou não se sente seguro para a respeito da
forma correta de se colocar componentes nos formulários (Forms), antes de prosseguir no curso, dê uma
olhada nas seguintes lições:
Introdução ao BCB(http://www.dicasbcb.com.br/Iniciando_o_BCB/Inicia_o_BCB.htm)
Introdução ao BCB - 2ª parte(http://www.dicasbcb.com.br/Iniciando_o_BCB2/Inicia_o_BCB2.htm)
Podemos inserir palavras, textos ou frases em diversos componentes através códigos ou de suas
propriedades no Object Inspector. Essas propriedades, uma vez demarcadas, determinam o modo que o
programa iniciará a sua execução. Podemos, posteriormente, em tempo de execução, alterar essas
propriedades de diversas formas, conforme o caso.
Exemplos: No Object Inspector, mude o texto da propriedade Caption do Form, de um Label, de um
botão, e assim por diante e visualize, já no projeto, o resultado da alteração.
Outros componentes, derivados de outros ramos, possuem outras formas de ser alterados. Por exemplo,
altere a propriedade Text de um Edit, ou de um MaskEdit; ou a propriedade Items de um ListBox; ou as
propriedades Text e Items de um ComboBox etc.
Vejamos algumas formas de inserir textos em componentes, através do código, em tempo de execução:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Canvas->TextOut( Canvas->PenPos.x, Canvas->PenPos.y,
"Imprimindo um texto no form com o objeto Canvas" );
Label1 -> Canvas->TextOut( Canvas->PenPos.x, Canvas->PenPos.y,
"Imprimindo um texto no Label com o objeto Canvas" );
Canvas->TextOut( Canvas->PenPos.x = 100, Canvas->PenPos.y = 100,
"Imprimindo um texto nas coordenadas X = 100 e Y = 100 do Form" );
}
Outro exemplo:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
Apostila de C++ Builder
Pagina -28-
{
/* Este exemplo deve ser testado com dois cliques.
O primeiro chamará a propriedade Caption; o segundo, o objeto Canvas */
Label1 -> Caption = "Testando o Caption";
/* Na linha de código abaixo, precisamos determinar uma coordenada diferente
da default (0, 0), para que esse texto não venha a sobrescrever o Caption de Label */
Label1 -> Canvas->TextOut( Canvas->PenPos.x = 100, Canvas->PenPos.y = 100,
"Imprimindo um texto nas coordenas X = 100 e Y = 100 do Label" );
}
Outro exemplo:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1 -> Caption = "Testando propriedades";
Edit1->Text = "Continuando o teste ...";
ListBox1->Items-> Add(Label1 -> Caption);
ListBox1->Items-> Add("Testando ...");
ComboBox1 -> Text = "Fala, Grande!";
ComboBox1 -> Items -> Add(Label1 -> Caption);
ComboBox1 -> Items -> Add("Fala, Grande...");
ComboBox1 -> Items -> Add(Edit1->Text);
}
Outro exemplo:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
ShowMessage("Alô, Thérbio!");
ShowMessage("Alô, Thérbio!"
"\nEstou na segunda caixa de mensagem,\n e na terceira linha");
MessageBox(0, "Agora uma caixa de mensagens \nm\n\ta\n\t\ti\n\t\t\ts\neditada!",
"Caixa de Aprendizagem...", 64);
}
Nota introdutória. No avançar deste curso, você perceberá que, propositadamente, usamos alguns nomes,
comandos ou tipos de dados com os quais você poderá não estar habituado a trabalhar. Não se assuste e
nem se preocupe em querer entender imediatamente todos os detalhes apresentados, pois tudo deverá ficar
bastante claro no tempo certo. Concentre-se, basicamente, em entender a manipulação dos dados
referentes ao tópico apresentado.
Apostila de C++ Builder
Pagina -29-
Conhecendo melhor as propriedades que capturam dados
Nos exemplos anteriores, conhecemos um modo de inserir textos ou strings nos componentes, sem nos
preocuparmos de que modo isso nos pode ser útil. Agora aprofundaremos um pouco mais o estudo para
podermos entender que essas propriedades não estão limitadas e pode produzir alguns resultados mais
úteis:
void __fastcall TForm1::Button1Click(TObject *Sender)
/* Neste exemplo, iremos corrigir uma falha referente à cor do primeiro
exemplo em que usamos o objeto Canvas do Form. Como você deve ter notado,
as cores da frase sairam com um fundo branco (clWhite), enquento que o form
manteve a cor original clBtnFace, visto que não procedemos a nenhuma alteração
nela. Para visualizar melhor essa situação, dê uma olhada na propriedade
Color do Form no Object Inspector. Pois bem, vamos corrigir a cor do fundo
da frase para igualá-la ao Form.*/
{
// Ok. A cor do fundo igual à cor do Form
Canvas -> Brush -> Color = clBtnFace;
/* A linha de código abaixo nos mostra como podemos usar
propriedades matemáticas diretamente no Form*/
Canvas->TextOut( Canvas->PenPos.x = 10,
Canvas->PenPos.y = 100, (5 * 4) + (60 - 50) );
/* Podemos chamar funções que nos mostrem a data e a hora...*/
Canvas->TextOut( Canvas->PenPos.x, Canvas->PenPos.y, Date()/* +Time()*/);
}
Evidentemente, outros componentes também são férteis para trabalharmos com dados:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
/*Uma caixa de mensagem que pode nos mostrar a data, a hora ou ambas.*/
ShowMessage( Time() /* + Date()*/ );
}
Por certo, as operações abaixo já não causam muitas surpresas em você:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
ShowMessage("Alô, Thérbio!\nVocê sabe qual é o resultado de "
"286 multiplicado por 132 somado com 412?");
Apostila de C++ Builder
Pagina -30-
ShowMessage((286 * 132) + 412);
Label1 -> Caption = (5 * 4) + 13; // imprime 33 no label
AnsiString a = 2 + 12; // AnsiString pode funcionar como inteiro ou ponto flutuante
/* neste exemplo, o sinal + neste local do código, serve para
concatenar os dados. Só ocorre a concatenação porque usamos o
tipo AnsiString. Se tivéssemos usado uma variável inteira ou
ponto flutuante, o resultado seria diferente*/
Label2 -> Caption = "Multiplicação de 14 X 14 = " + a * a;
/* produz o mesmo resultado no Label. O mesmo resultado teríamos se
tivéssemos usados variáveis inteiras ou ponto flutuante*/
Edit1 -> Text = "****Elimina os quatro primeiros caracteres" + 4;
}
Vamos aproveitar para conhecer a base do código que será usado para construirmos uma calculadora.
Usaremos um Label e um Botão qualquer no Form.
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
/* O código abaixo determina que o programa coloque no Caption de Label1
tudo aquilo que já está nele e mais o número 1. */
Label1 -> Caption = Label1 -> Caption + 1;
}
Comentários
Quando estamos empenhados na tarefa de escrever uma aplicação, é fundamental tomarmos algumas
providências para que não nos percamos nas diversas linhas de código que por ventura utilizaremos. Uma
coisa muito útil que podemos, ou melhor, que devemos fazer é colocar comentários que nos esclareçam a
utilidade das diversas partes do código. Evidentemente, você já tem percebido que, junto às linhas de
código, nós colocamos algumas frases esclarecedoras dentro de um par de barras com asterisco /* */, ou
imediatamente após duas barras //. Essas frases são os comentários.
C++ aceita esses dois estilos de comentários:
/* Esse estilo de comentário é derivado da linguagem C. Ele é aberto
com uma barra seguida de um asterisco. Tudo aquilo que estiver após
essa chave de abertura, até encontrar uma chave de fechamento (um
asterisco e uma barra) será desconsiderado pelo compilador */
//
//
//
//
//
Essas duas barras significam comentários no estilo C++.
Uma vez aberto, esse comentário não precisa ser fechado.
Mas ele só vale para a linha em que foi aberto,
a partir do ponto em que foi aberto
e até o final da linha nada mais será considerado
Apostila de C++ Builder
Pagina -31-
Comentários também podem ser úteis para tirar o efeito de determinadas partes de código, quando
estamos fazendo experiências. Você já viu tal fato, nos exemplos que colocamos o código para apresentar
a data ou a hora. Se você retirar as chaves de abertura e de fechamento, perceberá que o resultado do
programa será diferente.
Abaixo apresentamos um exemplo de como podemos usar comentários para retirar efeitos de um código
que já usamos anteriormente. Compile e execute para ver que o resultado do programa será outro:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// Label1 -> Caption = "Testando propriedades";
// Edit1->Text = "Continuando o teste ...";
/* ListBox1->Items-> Add(Label1 -> Caption);
ListBox1->Items-> Add("Testando ..."); */
ComboBox1 -> Text = "Fala, Grande!";
ComboBox1 -> Items -> Add(Label1 -> Caption);
ComboBox1 -> Items -> Add("Fala, Grande...");
ComboBox1 -> Items -> Add(Edit1->Text);
}
Tipos fundamentais
A linguagem C++ possui tipos fundamentais de dados que representam valores como caracteres, inteiros,
pontos flutuantes e booleanos. Os tipos inteiro (int) e pontos flutuantes (float e double) representam os
números de um modo geral. Caracteres (char) representam as letras ou símbolos e o tipo booleano (bool)
oscila entre dois valores, geralmente para representar duas situações inversas como, por exemplo, falso ou
verdadeiro. Podemos, ainda, considerar um tipo básico de dados, o tipo enum, ou enumerados, o qual nos
permite definir conjuntos de constantes, normalmente do tipo int, chamados tipos de dados enumerados.
As variáveis declaradas deste tipo poderão adquirir somente entre os valores definidos.
Coloque um Memo e um Button no Form. No evento OnClick do botão digite:
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
char ValChar = 'a';
int ValInt = 100;
float ValFloat = 100.78; // problemas na precisão
double ValDoub = 9.9876543;
bool ValBool = (2.0000 == (20 - 18)); //Verdade: dois = a vinte menos dezoito
Memo1->Lines->Strings[0] = ValChar; // 1ª linha do Memo imprime: a
Memo1->Lines->Strings[1] = ValInt; // 2ª linha do Memo imprime: 100
Memo1->Lines->Strings[2] = ValFloat; // 3ª linha do Memo imprime: 100.78...
Memo1->Lines->Strings[3] = ValDoub; // 4ª linha do Memo imprime: 9.9876543
// Se a afirmação feita na declaração de ValBool é verdadeira
if( ValBool == True)
/*5ª linha de Memo imprime: Afirmação Verdadeira*/
Memo1->Lines->Strings[4] = "Afirmação Verdadeira";
Apostila de C++ Builder
Pagina -32-
else // senão - ou seja, se a afirmação é falsa
/* Quinta linha de Memo imprime: Afirmação Mentirosa */
Memo1->Lines->Strings[4] = "Afirmação Mentirosa";
}
Nota: Se o programa acima não imprimir todas as linhas, pressione a tecla Enter alguma vezes para criar
linhas vazias no Memo. Desta forma, as strings serão colocadas no espaço correto. Depois (no tópico
Variáveis)
veremos
outra
solução
mais
adequada
para
o
problema!
A linguagem C++ ainda nos oferece um tipo especial que serve para indicar, justamente a ausência de
tipo: o void. No exemplo acima percebemos o uso deste tipo junto à função do evento chamado,
indicando que tal função não devolverá nenhum valor. O void também será é usado em funções que não
requerem parâmetros.
O tipo inteiro
O tipo inteiro é um número de valor inteiro, ou seja, não fracionário. De um modo geral, trata-se de
seqüências de dígitos que representam números inteiros, que podem ser escritos na base 8 (octal), na base
10 (decimal) ou na base 16 (hexadecimal).
Um inteiro decimal é formado pela seqüência de dígitos decimais: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. Exemplos
válidos para decimais são:
0
12345
1
- 89
Os números a seguir não são exemplos válidos para decimais:
00
123.4
1,0
89-8
12 34
045
OBS. Embora alguns compiladores aceitem declarações do tipo:
int i = 123.4;
todos os números colocados depois do ponto fracionário (nesse caso, o 4) serão desconsiderados.
Um inteiro hexadecimal é formado pela seqüência de dígitos decimais: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
combinados com letras que vão de "A" a "F" (A, B, C, D, E, F ou a, b, c, d, e, f). Como não há regras que
impeçam que um hexadecimal possua só valores decimais em sua composição, devemos diferenciá-los
dos inteiros decimais inserindo 0x em seus primeiros dígitos. Exemplos válidos para hexadecimais são:
0x0
0x1234
0x1
0xA
0xaBcD 0x1A9F
Os números a seguir não são válidos para valores hexadecimais:
00x
Apostila de C++ Builder
0x123.4
0x1,0
0xAGB
Pagina -33-
OxaB cD
FF12
Um inteiro octal é formado pela seqüência de dígitos decimais: 0, 1, 2, 3, 4, 5, 6, 7. Devemos diferenciálos dos inteiros decimais inserindo 0 em seu primeiro dígito. Exemplos válidos para octais são:
00
012345
01
- 056
Os números a seguir não são exemplos válidos para octais:
0
0123.4
01,0
089
012 34 45
Via de regra, os dados inteiros são devolvidos pelo programa no formato decimal. Desta forma, podemos
testar uma aplicação, onde colocaremos valores inteiros em vários formatos, porém os mesmos sempre
serão exibidos em formato decimal. Você poderá testar valores válidos e valores não válidos para cada
base de dados e visualizar os resultados.
Coloque um Label e um Button no Form. No evento OnClick do botão digite:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// declara um valor inteiro decimal cujo nome será i
int i = 43; // podemos substituir o 43 por hexadecimais,
//octais ou outros valores decimais
Label1 -> Caption = i; // coloca i no Caption de Label1
}
Compile e execute a aplicação. O Label imprimirá o número 43. Feche a aplicação e substitua o valor 43
(decimal) no Editor de Códigos por 0x2b (hexadecimal). Rode o programa e dê um clique no botão para
ver o resultado. Agora faça a mesma experiência substituindo o 0x2b por 053 (octal). Se você fez tudo
direitinho, qualquer dos testes que você executar deverá imprimir o número 43 no Label do Form. Isso
porque esses valores são equivalentes. Baseando-se nas explicações introdutórias sobre esses valores
inteiros e na tabela abaixo, faça mais alguns testes para compreender melhor o que ocorre a nível
equivalência desses dados.
veja uma tabela inicial:
Apostila de C++ Builder
decimais
octais
hexadecimais
0
00
0x0
1
01
0x1
2
02
0x2
3
03
0x3
4
04
0x4
5
05
0x5
6
06
0x6
7
07
0x7
Pagina -34-
8
010
0x8
9
011
0x9
10
012
0xA
11
013
0xB
12
014
0xC
13
015
0xD
14
016
0xE
15
017
0xF
16
020
0x10
17
021
0x11
18
022
0x12
19
023
0x13
20
024
0x14
21
025
0x15
22
026
0x16
23
027
0x17
24
030
0x18
25
031
0x19
26
032
0x1A
27
033
0x1B
28
034
0x1C
29
035
0x1D
30
036
0x1E
31
037
0x 1F
32
040
0x20
Tudo pode estar parecendo muito complicado, mas não se preocupe porque o uso de valores octais ou
hexadecimais não é comum. Além do mais, no avançar do curso (ou talvez na seção de tutoriais), nós
aprenderemos a construir uma calculadora que nos apresentará como resultado o valor de conversão entre
esses dados. Por enquanto, contentemo-nos com a calculadora do Windows que pode realizar esse tipo de
cálculos.
Apostila de C++ Builder
Pagina -35-
Os tipos ponto flutuante
De um modo geral, podemos afirmar que os tipos ponto flutuante são representados pelos números
fracionários na base decimal. Ou seja, trata-se de números, de regra, constituídos por um ponto fracionário
que não pode ser substituído por uma vírgula.
Um número de ponto flutuante pode apresentar-se na forma de notação científica. Por exemplo: 4.32 vezes
dez elevado a quarta. Neste caso, o vezes dez ( X 10) é substituído por e ou E. Logo o número
supramencionado será representado por 4.32e4, que vale 43200.
OBS. Embora o C++Builder aceite a notação científica para inteiros, todos os valores que estiverem
situados após um eventual ponto fracionário, no resultado, serão desconsiderados.
Exemplos válidos para ponto flutuante são:
-1.e4
12.345
.1
1.23e-2
1.23E+2
Não podemos colocar vírgulas para representar pontos flutuantes: 12,345 // ilegal.
Os números ponto flutuante se dividem basicamente nos tipos float e double. Posteriormente
entenderemos melhor a que se referem esses nomes.
Eis um código para visualizarmos a matéria tratada neste tópico. Para executar esse código, precisaremos
de um Button e dois Label(s) no Form. De quebra, visualizaremos um problema de precisão com o tipo
float.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
float f = 3.65e-2;
double d = 3.65e-2;
Label1 -> Caption = f; // problemas de precisão com o tipo float
Label2 -> Caption = d;
}
O tipo caracter
O tipo caracter é uma letra ou um símbolo colocado entre aspas simples. Embora sejam visualizados
como letras ou símbolos, é importante ter em mente que, internamente, os computadores armazenam os
caracteres como um número inteiro entre 0 e 255. Por exemplo, a letra a é associada ao número 97, b ao
98, c ao 99, d ao 100 e assim sucessivamente. Os valores numéricos dos caracteres estão padronizados em
uma tabela chamada de American Standard Code for Information Interchange Table ou simplesmente
tabela ASCII. Então concluímos que um char tanto pode ser interpretado como um número entre 0 e 255,
Apostila de C++ Builder
Pagina -36-
quanto pode ser interpretado como um elemento de um conjunto de caracteres da tabela ASCII. O tipo
char geralmente tem um byte, o que é suficiente para conter os 256 valores mencionados.
Exemplos válidos para caracteres são:
'a'
'A'
'z'
'5'
'§'
'%'
''
'*'
Seqüências de escape
Certos caracteres da tabela ASCII devem ser representados pela combinação especial de outros
caracteres. Essas combinações, conhecidas como seqüência de escape, são representadas por uma barra
invertida ( \ ) e outro caracter. Esse grupo de dois caracteres é interpretado como uma seqüência simples.
Por exemplo, nós já usamos em exemplo anterior, o '\n' e o '\t'. Essas combinações são interpretadas como
nova linha (Enter) e Tab, respectivamente.
Abaixo, representaremos algumas seqüências de escape:
Controle/Caracter
Sequencia de escape
Valor ASCII
nulo (null)
\0
00
campainha (bell)
\a
07
retrocesso (backspace)
\b
08
tabulacao horizontal
\t
09
nova linha (new line)
\n
10
tabulacao vertical
\v
11
alimentacao de folha (form feed)
\f
12
retorno de carro (carriage return)
\r
13
aspas (")
\"
34
apostrofo (')
\'
39
interrogacao (?)
\?
63
barra invertida (\)
\\
92
É bom notarmos que, embora alguns desses caracteres possam ser compilados sem a barra invertida ( \ )
em algumas aplicações pelo C++Builder (", ?), noutras a barra poderá fazer falta.
Apostila de C++ Builder
Pagina -37-
Coloque um Label no Form e no evento OnClick do Label digite:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
/*Declara uma variável int chamada StrInt e atribui a ela o valor que
será digitado no InputBox pelo usuário*/
int StrInt = StrToInt /*converte AnsiString para int*/
(InputBox("Converte int para char","Digite um valor entre 0 e 255...",""));
Label1 -> Caption = char(StrInt)/*converte int(StrInt) para char*/;
}
Agora rode a aplicação e faça algumas experiências, dando um clique em Label para inserir valores entre
0 e 255 e visualizar a equivalência de valores entre os números e os elementos da tabela ASCII.
Modificadores de tipos
Podemos alterar o significado de um tipo básico de dados, adaptando-o a uma necessidade específica, por
meio de modificadores de tipos precedendo os tipos na declaração:
signed (com sinal);
unsigned (sem sinal);
long (máxima precisão);
short (menor precisão).
Tipo
bytes
possíveis valores
char
1
-128 a 127
unsigned char
1
0 a 255
signed char
1
-128 a 127
int
4
-2147483648 a 2147483647
unsigned int
4
0 a 4294967295
signed int
4
-2147483648 a 2147483647
Apostila de C++ Builder
Pagina -38-
Tipo
bytes
possíveis valores
short int
2
-32768 a 32767
unsigned short int
2
0 a 65535
signed short int
2
-32768 a 32767
long int
4
-2147483648 a 2147483647
signed long int
4
-2147483648 a 2147483647
unsigned long int
4
0 a 4294967295
float
4
-(3.4e38) a 3.4e38
double
8
1.7e308 a -(1.7e308)
long double
10
1.7e308 a -(1.7e308)
O exemplo a seguir usa um Edit, um Memo e um Button no Form. Quando o usuário dá um clique no
botão, um loop começa a contar uma seqüência de valores numéricos de caracteres para a variável
unsigned char c e só a encerra quando contar o último caracter, o de número 254. No corpo desse loop,
nós declaramos uma variável do tipo char, que converterá os valores numéricos da variável c para os
elementos correspondentes aos números na tabela ASCII, atribuindo esses valores a ch. Feito isso os
valores obtidos serão imprimidos no Edit, exibindo as correspondências encontradas. Concomitantemente,
quando o dedo do usuário deixa de fazer pressão com o mouse sobre o botão, configurando o evento
OnMouseUp, o conteúdo do Edit (no caso os valores 0 a 254 da tabela ASCII e mais alguns caracteres
para facilitar a visualização e a leitura) é adicionado a Memo1.
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
/* Declara e inicializa com 0 uma variável do tipo unsigned char
dentro de um loop for; estabelece o limite de 254 para o loop;
determina o incremento da variável. */
for(unsigned char c = 0; c < 255; c++)
{
/*Declara uma variável que converterá os
números para o caracter da tabela ASCII*/
char ch = c;
/* Edit1 imprime os números, os caracteres correspondentes da
tabela ASCII e mais alguns caracteres para facilitar a leitura*/
Edit1 -> Text = Edit1 -> Text + c + ' ' + '=' + ' ' + ch + ' ' + ' ';
}
}
//------------------------------------------------------------------
Apostila de C++ Builder
Pagina -39-
void __fastcall TForm1::BitBtn1MouseUp(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
// Adiciona o conteúdo de Edit1 a Memo1
Memo1->Lines->Add(Edit1->Text);
}
O exemplo acima pode ser modificado para exibir somente alguns caracteres ASCII. Por exemplo, após
rodar a aplicação, o programa apresentará para você o número de cada caracter. Com base nestas
informações, você poderá, facilmente, montar um programa que imprima somente as letras minúsculas do
alfabeto etc.
Variáveis
Podemos entender um microcomputador como um sistema de cinco unidades de funcionamento: unidade
de entrada (teclado, mouse, drive de CD-ROM, drive de disquetes etc), unidade de saída (impressora,
monitor etc), unidade de memória (memória RAM -escrita e leitura-, memória ROM - leitura), e as
unidades aritmética e lógica que se encontram agrupadas na CPU (Unidade Central de Processamento, o
processador).
O chip responsável pelo controle de todo o computador é o processador. Outro circuito de extrema
importância é a memória RAM, que podemos imaginar como um grupo de células usadas para
armazenamento temporário das instruções e dos dados que são acessados e processados pelo
microprocessador em altíssima velocidade. Trata de uma memória volátil pois seus dados perdem-se no
momento em que são desligadas, o que não chega a ser um problema, visto que esses dados, de regra, após
salvos, ficam guardados em algum disco de armazenamento permanente, como os discos rígidos ou os
disquetes, sendo copiados novamente para a memória na ocasião de seu processamento.
A memória RAM é constituída por uma imensa seqüência de células de armazenamento (localizações)
com o tamanho de oito bits (um byte) cada, o que permite que cada uma dessas localizações possa assumir
um entre 256 valores diferentes. Ressalte-se, ainda, que cada célula possui um endereço único e
inconfundível, expresso por um valor numérico que define a exata localização desse byte, bem como que,
apesar do limitado tamanho de cada célula, podemos acessar dois bytes consecutivos (word) ou quatro
bytes consecutivos (doubleword) simultaneamente com um único endereçamento.
Disso decorre que durante a execução de um programa, as instruções e os dados processados ficam
armazenados na memória do computador. Cada informação é representada por certo grupo de bytes (char
- 1 byte, float - 4 bytes, double - 8 bytes etc) e possui um local determinado na memória, um endereço que
pode ser expressado por um valor hexadecimal. Não há necessidade de o programador conhecer o
endereço absoluto de cada dado, pois o compilador relaciona o nome de cada variável com sua posição na
memória, cuidando dessa tarefa da melhor maneira possível.
Para facilitar o entendimento, podemos imaginar a memória do computador como um enorme armário de
gavetas. Cada gaveta (célula de armazenamento) é numerada seqüencialmente e possui o tamanho de 1
byte. Esse número seqüencial é o endereço da gaveta. Conforme o tipo de variável declarada (char = 1
Apostila de C++ Builder
Pagina -40-
byte; int = 4 bytes; float = 4 bytes; etc), o compilador reservará uma ou mais gavetas seqüencialmente
para armazenar o valor correspondente, pois cada tipo de dado possui um tamanho próprio.
Imagine um armário imenso, com um milhões de gavetas iguais. Seria demasiadamente complicado
localizar determinado objeto numa dessas gavetas se não possuíssemos uma forma de diferenciá-las entre
si. Então, o nome da variável funciona como uma inscrição que individualiza a gaveta (endereço na
memória).
Por exemplo, a declaração:
long double LgDbl;
informa ao compilador que ele deverá reservar 10 bytes seqüenciais (dez gavetas dispostas em seqüência)
para uma variável do tipo long double cujo nome é LgDbl. Observe que as "gavetas" estão reservadas,
porém nenhum "objeto" foi colocado nelas.
Um ponto bastante importante sobre o tamanho das variáveis é que seu tamanho pode variar de máquina
para máquina, ou sistema operacional para sistema operacional. O tipo int, por exemplo, ocupa 2 bytes no
sistema operacional MS-DOS e 4 bytes no Windows.
Com esses conceitos, já temos informações suficientes para entender o que vem a ser uma variável.
Podemos definir uma variável como um local na memória do computador que a cada momento pode
possuir um valor diferente, porém do mesmo tipo de dados.
Por exemplo, a declaração:
char ch;
faz com que o compilador reserve espaço suficiente para um caracter. Já a declaração abaixo é um pouco
mais completa, pois inicializa a variável, colocando um caracter A no espaço reservado:
char ch = 'A';
O conceito de variáveis decorre justamente do fato de que podemos substituir o "conteúdo" dessas
"gavetas":
ch = 'b'; // ch agora possui outro valor
O exemplo a seguir usa um SpeedButton no Form. Declararemos uma variável do tipo char num lugar
onde ela será visível tanto pelo evento OnMouseDown quanto pelo evento OnMouseUp de
SpeedButton1. Quando o botão do mouse for pressionado sobre o botão, o evento OnMouseDown será
Apostila de C++ Builder
Pagina -41-
ativado para atribuir o caracter A à variável ch, exibindo esse valor no Caption de SpeedButton1. Quando
o botão do mouse for liberado, o evento OnMouseUp será ativado para alterar o valor da variável para b,
exibindo esse valor no Caption de SpeedButton1.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------// declara uma variável char visível pelos eventos de SpeedButton1
char ch;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::SpeedButton1MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
ch = 'A'; // atribui A para a variável ch
SpeedButton1 -> Caption = ch;
}
//--------------------------------------------------------------------------void __fastcall TForm1::SpeedButton1MouseUp(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
ch = 'b'; // atribui b para a variável ch
SpeedButton1 -> Caption = ch;
}
//---------------------------------------------------------------------------
O C++Builder implementa a função sizeof() que nos permite visualizar o tamanho, em bytes, de uma
variável ou de um tipo de dados:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
double Dbl;
Edit1 -> Text = AnsiString(sizeof(Dbl)) + " bytes"; // retorna pelo nome da variável
/* retorna o valor pelo tipo de dados*/
Edit2 -> Text = AnsiString(sizeof(bool)) + " byte";
RichEdit1 -> Clear(); // apaga o conteúdo de RichEdit1
for (int linha = 0; linha <= 3; linha ++) // Em breve estudadremos o loop for
Apostila de C++ Builder
Pagina -42-
RichEdit1 -> Lines -> Insert(linha, "");
RichEdit1
RichEdit1
RichEdit1
RichEdit1
->
->
->
->
Lines->
Lines->
Lines->
Lines->
Strings[0]
Strings[1]
Strings[2]
Strings[3]
=
=
=
=
"Tamanho
"Tamanho
"Tamanho
"Tamanho
// prepara RichEdit1 para receber
// as quatro linhas de strings
de
de
de
de
char = " + String(sizeof(char));
int = " + String(sizeof(int));
String = " + String(sizeof(String));
AnsiString = " +
AnsiString(sizeof(AnsiString));
ShowMessage(sizeof(long double));
}
//---------------------------------------------------------------------------
Atribuição de valores a variáveis
Conforme você já deve ter percebido, para criarmos uma variável, precisamos declarar o seu tipo (char,
int, float, ...), seguido pelo nome da variável e por um símbolo de ponto e vírgula:
int i;
Com o operador de atribuição = atribuímos valores às variáveis:
i = 43;
Nada impede que, no momento da declaração, também inicializemos a variável com algum valor:
int i = 43;
Podemos, inclusive, declarar mais de uma variável na mesma instrução, bem como misturar declarações
com inicializações:
// declararamos 5 variáveis das quais inicializamos duas (i3 e i4)
int i, i2, i3 = 10, i4 = 15, i5;
O nome de uma variável deve ser sugestionável, nos indicando o tipo de dados com o qual ela trabalhará.
Por exemplo, suponhamos um problema onde será calculada a área de um retângulo de 10 metros de
comprimento por 7 metros de largura. Poderíamos definir as variáveis assim:
int compr = 10;
int larg = 7;
int total = compr * larg; // atribui o resultado da multiplicação a total
Apostila de C++ Builder
Pagina -43-
Existem algumas regras que devem ser respeitadas: Os nomes das variáveis só podem começar com letras
(a, A, b, B, c, C, d, D, e, E, f, ...) ou por caracter de sublinhar ( _ ); depois de começado o nome,
podemos colocar letras, números ou caracter de sublinhar no nome:
int i, i1, i_, _i, _2, i_2 ; // Ok. Todas as variáveis possuem nomes aceitáveis
float 7_i; // nome inválido, pois começa com número
char _AF$G; // nome inválido - caracter ilegal $
Coloque um botão BitBtn no Form sem alterar-lhe o tamanho. O exemplo exibirá o valor área do botão
que será ampliada a cada clique do mouse. Eis o código:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------int altr = 25, larg = 75; // declara e inicializa duas variáveis globais
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
/* O código deste bloco será executado imediatamente
(e somente) quando o aplicativo for inicializado*/
int total = altr * larg; // declara e inicializa uma variável local
/*Coloca no caption de BitBtn1 o título Área =, mais o valor
da variável total convertido em AnsiString*/
BitBtn1 -> Caption = "Área = " + AnsiString(total);
}
//--------------------------------------------------------------------------void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
altr = altr + 10; //incrementa a variável altr com + 10, alterando-lhe o valor
larg = larg + 30; //incrementa a variável larg com + 30, alterando-lhe o valor
BitBtn1 -> Height = altr; //atribui a BitBtn1 -> Height o novo valor de altr;
BitBtn1 -> Width = larg; //atribui a BitBtn1 -> Width o novo valor de larg;
//atualiza o Caption de BitBtn1
BitBtn1 -> Caption = "Área = " + AnsiString(altr * larg);
}
//---------------------------------------------------------------------------
Nota: No exemplo acima, essa não é a melhor forma de se conseguir o resultado, pois o exemplo foi
apenas didático para ilustrar a declaração, a inicialização e a substituição de valores em variáveis. Como
exercício, procure conseguir o mesmo resultado sem declarar nenhuma variável, o que poupará memória
durante a execução do programa. Você já possui conhecimentos suficientes para tanto!
Apostila de C++ Builder
Pagina -44-
Variáveis signed e unsigned
(com e sem sinal)
Quando tratamos dos modificadores de tipos, tivemos oportunidade de observar que em C++ os tipos char
e int possuem as variedades signed (com sinal) e unsigned (sem sinal). Tais modificadores permitem-nos
filtrar alguns valores que poderão ser desnecessários para determinados tipos de aplicação, como por
exemplo os números negativos, ou os valores estendidos da tabela ASCII. Os tipos inteiros (int, short e
long) e os char, de regra, podem armazenar valores positivos ou negativos. Todavia se declarados com o
modificador de tipos unsigned, ficarão restritos a números iguais ou maiores do que zero.
A tabela abaixo exibe os valores possíveis para os tipos de dados, com ou sem os modificadores de tipo:
Tipo
bytes
possíveis valores
char
1
-128 a 127
unsigned char
1
0 a 255
signed char
1
-128 a 127
int
4
-2147483648 a 2147483647
unsigned int
4
0 a 4294967295
signed int
4
-2147483648 a 2147483647
short int
2
-32768 a 32767
unsigned short int
2
0 a 65535
signed short int
2
-32768 a 32767
long int
4
-2147483648 a 2147483647
signed long int
4
-2147483648 a 2147483647
unsigned long int
4
0 a 4294967295
Observe que o tipo não tem o seu tamanho alterado (número de bytes) em virtude da presença dos
modificadores signed ou unsigned. O resultado direto desse fato é que, para um mesmo tipo de dados, o
valor máximo que pode ser atribuído a um unsigned é o dobro do maior valor positivo que pode ser
atribuído a um signed.
O programa abaixo mostra-nos alguns limites de valores:
Apostila de C++ Builder
Pagina -45-
void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
for (char ch = -126; ch > -127; ch++) Memo1->Lines->Add(ch);
Memo1 -> Lines -> Add("");
for (signed char Sch = 1 ;Sch > 0; Sch++) Memo1->Lines->Add(Sch);
Memo1 -> Lines -> Add("");
for (unsigned char Uch = -1000; Uch > 0; Uch++) Memo1->Lines->Add(Uch);
Memo1 -> Lines -> Add("");
for (int i = 2147483600; i > 0; i++) Memo1->Lines->Add(i);
Memo1 -> Lines -> Add("");
for (unsigned int ui = 4294967200; ui > 0; ui++) Memo1->Lines->Add(ui);
Memo1 -> Lines -> Add("");
for (signed short int SSi = 32768; SSi < -32000; SSi++) Memo1->Lines->Add(SSi);
}
Excedendo o limite de uma variável
Se tentarmos atribuir um valor fora da faixa que a variável pode conter, ela continuará a partir do zero, se
for unsigned; porém, se for do tipo signed, continuará a armazenar dados a partir do menor valor
negativo.
O Exemplo abaixo leva um SpeedButton e um ComboBox no Form. Quando o usuário clicar o botão, o
programa mostrará o resultado de se tentar atribuir valores não contemplados pela variável.
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ComboBox1 -> Text = "Excedendo limites de variáveis ...";
}
//--------------------------------------------------------------------------void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
ComboBox1 -> Text = "Veja abaixo os novos valores ...";
int i = 2147483647;
ComboBox1 -> Items ->
ComboBox1 -> Items ->
ComboBox1 -> Items ->
ComboBox1 -> Items ->
unsigned int
ComboBox1 ->
ComboBox1 ->
ComboBox1 ->
Add("Limite para int = " + String(i));
Add("Limite + 1 = " + String(i + 1));
Add("Limite + 2 = " + String(i + 2));
Add("");
Ui = 4294967295;
Items -> Add("Limite para unsigned int = " + String(Ui));
Items -> Add("Limite + 1 = " + String(Ui + 1));
Items -> Add("Limite + 2 = " + String(Ui + 2));
Apostila de C++ Builder
Pagina -46-
ComboBox1 -> Items -> Add("");
signed int Si = 2147483647;
ComboBox1 -> Items -> Add("Limite para signed int = " + String(Si));
ComboBox1 -> Items -> Add("Limite + 1 = " + String(Si + 1));
ComboBox1 -> Items -> Add("Limite + 2 = " + String(Si + 2));
ComboBox1 -> Items -> Add("");
char ch =
ComboBox1
ComboBox1
ComboBox1
ComboBox1
127;
-> Items
-> Items
-> Items
-> Items
->
->
->
->
Add("Limite para char = " + String(int(char(ch))));
Add("Limite + 1 = " + String(int(char(ch + 1))));
Add("Limite + 2 = " + String(int(char(ch + 2))));
Add("");
signed char Sch = 127;
ComboBox1 -> Items -> Add("Limite para signed char = " + String (Sch));
ComboBox1 -> Items -> Add("Limite + 1 = " + String(int(char(Sch + 1))));
ComboBox1 -> Items -> Add("Limite + 2 = " + String(int(char(Sch + 2))));
ComboBox1 -> Items -> Add("");
unsigned char Uch = 255;
ComboBox1 -> Items -> Add("Limite para unsigned char = " + String (Uch));
ComboBox1 -> Items -> Add("Limite + 1 = " + String(int(char(Uch + 1))));
ComboBox1 -> Items -> Add("Limite + 2 = " + String(int(char(Uch + 2))));
ComboBox1 -> Items -> Add("");
}
//---------------------------------------------------------------------------
Operações matemáticas com unsigned
Já sabemos que uma variável unsigned não pode ser atribuída com valores negativos. Se, numa operação
matemática, tivermos um resultado menor do que zero para uma variável unsigned, no momento em que
o cálculo atingir o primeiro valor negativo (-1) a operação é remetida para o maior valor do tipo do dado,
de onde o programa dará continuidade aos cálculos com os valores restantes.
O exemplo a seguir usa um Label e um Timer no Form. São declaradas duas variáveis globais do tipo
unsigned int, sendo que uma funcionará como base do cálculo da subtração e a outra como valor a ser
subtraído. O Label ficará piscando no Form, ou melhor, o Timer fará com que o Label fique visível e
depois de alguns instantes invisível, e depois visível, e depois invisível e assim sucessivamente.
Ajustamos o intervalo de tempo para 500, mas você pode alterar o valor para mais ou menos, no Code
Editor ou no Object Inspector. Haverá um decremento (diminuição do valor em razão de 1) do número
apresentado no Label, a cada piscada. Ou seja, o primeiro valor apresentado será 10; o segundo, 9; o
terceiro, 7; e assim por diante. Observe bem o que acontecerá depois que 0 (zero) for apresentado no
Label, onde, equivocadamente, poderíamos estar esperando algum valor negativo:
Apostila de C++ Builder
Pagina -47-
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------unsigned int uiBase = 9; /* declara e inicializa a variável base (observe que
poderia ser uma constante)*/
unsigned int uiAcresce = 1; /* declara e inicializa a variável que será
incrementada na subtração */
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
if (Label1->Visible == true) // se label estiver visivel
{
Label1->Visible=false; // Label1 ficará invisível
/* e altera-se o Caption de Label1, com o resultado da subtração,
cada vez que ele ficar invisível*/
Label1 -> Caption = uiBase - uiAcresce;
/* incrementa a variável uiAcresce (ela vale 1,
depois 2, depois 3, 4, 5, etc)*/
uiAcresce = uiAcresce + 1;
}
else // ou então ( se Label1 estiver invisível)
Label1->Visible=true; // Label1 ficará visível
}
//---------------------------------------------------------------------------
AnsiString
O C++Builder implementa o tipo AnsiString como uma classe. AnsiString é projetado para funcionar
como o tipo long string Delphi. Adequadamente, AnsiString fornece as seguintes características de
tratamento de strings que são requeridas quando você chama funções do tipo VCL que usam qualquer
tipo long string Delphi:
• reference count
• string length
Apostila de C++ Builder
Pagina -48-
• data
• null string terminator
Se você não fornecer um valor inicial, as variáveis AnsiString são iniciadas com instância zero.
Podemos conceituar uma string como um caracter, ou uma seqüência de caracteres colocados entre aspas
duplas:
"Alô, Thérbio!"
"Guarulhos é a terra das Pizzarias..."
"Oba!"
"ô"
A classe AnsiString possui um bom nível de independência e flexibilidade nos controles onde é usada,
uma vez que não é descendente de Tobject, permitindo-nos realizar diversas operações úteis com strings.
A implementação desse arquivo pode ser visualizada no diretório include/vcl/dstring.h da pasta de
instalação de seu compilador:
#ifndef DSTRING_H
#define DSTRING_H
#pragma delphiheader begin
#include <sysmac.h>
namespace System
{
class
TVarRec;
class RTL_DELPHIRETURN Currency;
class RTL_DELPHIRETURN WideString;
/////////////////////////////////////////////////////////////////////////////
// AnsiString: String class compatible with Delphi's Native 'string' type
/////////////////////////////////////////////////////////////////////////////
class RTL_DELPHIRETURN AnsiString
{
friend AnsiString __fastcall operator +(const char*, const AnsiString& rhs);
public:
// the TStringFloatFormat enum is used by FloatToStrF
enum TStringFloatFormat
{sffGeneral, sffExponent, sffFixed, sffNumber, sffCurrency};
static AnsiString __fastcall StringOfChar(char ch, int count);
static AnsiString __fastcall LoadStr(int ident);
static AnsiString __fastcall LoadStr(HINSTANCE hInstance, int ident);
static AnsiString __fastcall FmtLoadStr(int ident, const TVarRec *args,
int size);
AnsiString& __fastcall
LoadString(HINSTANCE hInstance, int ident);
// Delphi style 'Format'
//
static AnsiString __fastcall Format(const AnsiString& format,
const TVarRec *args, int size);
Apostila de C++ Builder
Pagina -49-
// C++ style 'sprintf' (NOTE: Target buffer is the string)
//
AnsiString& __cdecl sprintf(const char* format, ...); // Returns *this
int __cdecl printf(const char* format, ...); // Returns formatted length
//
static AnsiString __fastcall FormatFloat(const AnsiString& format,
const long double& value);
static AnsiString __fastcall FloatToStrF(long double value,
TStringFloatFormat format, int precision, int digits);
static AnsiString __fastcall IntToHex(int value, int digits);
static AnsiString __fastcall CurrToStr(Currency value);
static AnsiString __fastcall CurrToStrF(Currency value,
TStringFloatFormat format, int digits);
// Constructors
__fastcall AnsiString(): Data(0) {}
__fastcall AnsiString(const char* src);
__fastcall AnsiString(const AnsiString& src);
__fastcall AnsiString(const char* src, unsigned char len);
__fastcall AnsiString(const char* src, unsigned int len);
__fastcall AnsiString(const wchar_t* src);
__fastcall AnsiString(char src);
__fastcall AnsiString(short);
__fastcall AnsiString(unsigned short);
__fastcall AnsiString(int src);
__fastcall AnsiString(unsigned int);
__fastcall AnsiString(long);
__fastcall AnsiString(unsigned long);
__fastcall AnsiString(__int64);
__fastcall AnsiString(unsigned __int64);
__fastcall AnsiString(double src);
__fastcall AnsiString(const WideString &src);
// Destructor
__fastcall ~AnsiString();
// Assignments
AnsiString& __fastcall operator =(const AnsiString& rhs);
AnsiString& __fastcall operator +=(const AnsiString& rhs);
// Comparisons
bool __fastcall
bool __fastcall
bool __fastcall
bool __fastcall
bool __fastcall
bool __fastcall
int __fastcall
int __fastcall
operator ==(const AnsiString& rhs) const;
operator !=(const AnsiString& rhs) const;
operator <(const AnsiString& rhs) const;
operator >(const AnsiString& rhs) const;
operator <=(const AnsiString& rhs) const;
operator >=(const AnsiString& rhs) const;
AnsiCompare(const AnsiString& rhs) const;
AnsiCompareIC(const AnsiString& rhs) const; //ignorecase
// Accessing character at specified index
char __fastcall operator [](const int idx) const
{
ThrowIfOutOfRange(idx); // Should Range-checking be optional to avoid overhead ??
return Data[idx-1];
}
Apostila de C++ Builder
Pagina -50-
#if defined(ANSISTRING_USE_PROXY_FOR_SUBSCRIPT)
// The use of a proxy class optimizes the case where Unique() must be called
// when accessing the string via the subscript operator. However, the use of
// of the proxy class has some drawbacks. First, it breaks code that apply
// operators to the return value. For example, &MyString[i]. Second, it
// fails in cases where a implicit conversion was relied upon. For example,
//
callFuncThatTakesAnObjectWithACharCtr(MyString[i]);
// In that case, two implicit conversions would be required...
// The first issue can be remedied by enhancing the proxy class to support
// all valid operators. The second issue can be lessened but not completely
// eliminated. Hence, the use of the PROXY class is not the default!
//
private:
class TCharProxy;
friend TCharProxy;
class TCharProxy
{
public:
TCharProxy(AnsiString& strRef, int index) : m_Ref(strRef), m_Index(index) {}
TCharProxy& operator=(char c)
{ m_Ref.Unique(); m_Ref.Data[m_Index-1] = c; return *this; }
operator char() const
{ return m_Ref.Data[m_Index-1]; }
protected:
AnsiString&
int
};
m_Ref;
m_Index;
public:
TCharProxy __fastcall operator [](const int idx)
{
ThrowIfOutOfRange(idx); // Should Range-checking be optional to avoid overhead ??
return TCharProxy(*this, idx);
}
#else
char& __fastcall operator [](const int idx)
{
ThrowIfOutOfRange(idx); // Should Range-checking be optional to avoid overhead ??
Unique();
// Ensure we're not ref-counted
return Data[idx-1];
}
#endif
// Concatenation
AnsiString __fastcall operator +(const AnsiString& rhs) const;
// C string operator
char* __fastcall c_str() const { return (Data)? Data: "";}
// Query attributes of string
int __fastcall Length() const;
bool __fastcall IsEmpty() const;
// Make string unique (refcnt == 1)
Apostila de C++ Builder
Pagina -51-
AnsiString&
__fastcall Unique();
// Modify string
AnsiString& __fastcall Insert(const AnsiString& str, int index);
AnsiString& __fastcall Delete(int index, int count);
AnsiString& __fastcall SetLength(int newLength);
int __fastcall Pos(const AnsiString& subStr) const;
AnsiString
__fastcall LowerCase() const;
AnsiString
__fastcall UpperCase() const;
AnsiString
__fastcall Trim() const;
AnsiString
__fastcall TrimLeft() const;
AnsiString
__fastcall TrimRight() const;
AnsiString
__fastcall SubString(int index, int count) const;
int
int
double
__fastcall ToInt() const;
__fastcall ToIntDef(int defaultValue) const;
__fastcall ToDouble() const;
// Convert to Unicode
int
__fastcall WideCharBufSize() const;
wchar_t*
__fastcall WideChar(wchar_t* dest, int destSize) const;
// MBCS support
enum TStringMbcsByteType
{mbSingleByte, mbLeadByte, mbTrailByte};
TStringMbcsByteType __fastcall ByteType(int index) const;
bool __fastcall IsLeadByte(int index) const;
bool __fastcall IsTrailByte(int index) const;
bool __fastcall IsDelimiter(const AnsiString& delimiters, int index) const;
bool __fastcall IsPathDelimiter(int index) const;
int
__fastcall LastDelimiter(const AnsiString& delimiters) const;
int
__fastcall AnsiPos(const AnsiString& subStr) const;
char* __fastcall AnsiLastChar() const;
protected:
void ThrowIfOutOfRange(int idx) const;
private:
char *Data;
};
extern AnsiString __fastcall operator +(const char*, const AnsiString& rhs);
#if defined(VCL_IOSTREAM)
ostream& operator << (ostream& os, const AnsiString& arg);
istream& operator >> (istream& is, AnsiString& arg);
#endif
/////////////////////////////////////////////////////////////////////////////
// SmallStringBase
/////////////////////////////////////////////////////////////////////////////
template <unsigned char sz> class SmallStringBase
{
protected:
Apostila de C++ Builder
Pagina -52-
unsigned char Len;
char Data[sz];
};
/////////////////////////////////////////////////////////////////////////////
// SmallString
/////////////////////////////////////////////////////////////////////////////
template <unsigned char sz> class SmallString : SmallStringBase<sz>
{
#if defined(VCL_IOSTREAM)
friend ostream& operator <<(ostream& os, const SmallString& arg)
{
os << AnsiString(arg);
return os;
}
friend istream& operator >>(istream& is, SmallString& arg)
{
AnsiString s;
is >> s;
arg = s;
return is;
}
#endif
public:
__fastcall SmallString() { Len = 0; }
__fastcall SmallString(const SmallString& src);
__fastcall SmallString(const char* src);
__fastcall SmallString(const AnsiString& src)
{
long len = src.Length();
Len = (unsigned char)((len > sz)? sz: len);
strncpy(Data, src.c_str(), Len);
}
char& __fastcall operator [](const unsigned char idx)
{return Data[idx-1];}
SmallString& __fastcall operator =(const SmallString& rhs);
__fastcall operator AnsiString() const;
};
// used when SmallStrings are in unions (can't have a ctor)
// must cast DummySmallString to SmallString to do anything useful
template<unsigned char sz> __fastcall
SmallString<sz>::SmallString(const char* src)
{
long len = strlen(src);
Len = (unsigned char)((len > sz)? sz: len);
strncpy(Data, src, Len);
}
Apostila de C++ Builder
Pagina -53-
template<unsigned char sz> __fastcall
SmallString<sz>::SmallString(const SmallString& src)
{
Len = src.Len;
for (int i = 0; i < Len; i++)
Data[i] = src.Data[i];
}
template<unsigned char sz> SmallString<sz>& __fastcall
SmallString<sz>::operator =(const SmallString& rhs)
{
if (this != &rhs)
{
Len = rhs.Len;
for (int i = 0; i < Len; i++)
Data[i] = rhs.Data[i];
}
return *this;
}
template<unsigned char sz>
inline __fastcall SmallString<sz>::operator AnsiString() const
{
return AnsiString(Data, Len);
}
}
using namespace System;
// The following is provided for backward compatibility.
// Otherwise, the new IntToStr(__int64) causes ambiguity for old code
// that used 'bool', uint and DWORD.
//
namespace Sysutils
{
extern PACKAGE AnsiString __fastcall IntToStr(int Value)/* overload */;
extern PACKAGE AnsiString __fastcall IntToStr(__int64 Value)/* overload */;
}
#pragma option push -w-inl
inline AnsiString __fastcall IntToStr(bool value)
{
return Sysutils::IntToStr(int(value));
}
inline AnsiString __fastcall IntToStr(unsigned int value)
{
return Sysutils::IntToStr(int(value));
}
inline AnsiString __fastcall IntToStr(unsigned long value)
{
return Sysutils::IntToStr(int(value));
}
#pragma option pop
#pragma delphiheader end.
#endif // DSTRING_H
Apostila de C++ Builder
Pagina -54-
Por enquanto não tente entender os fundamentos deste arquivo, pois nele aparecem muitos termos que nos
são novidades ainda. No avançar do curso, muitos desses fundamentos serão abordados, tornando a
compreensão de partes do arquivo mais ou menos intuitiva.
Todavia, vamos verificar alguns tópicos, por dois motivos:
1º - É importante que tenhamos conhecimento de determinadas facilidades que AnsiString nos
proporciona;
2º - aprendermos a operar determinadas funções da classe, por intuição.
Nota: Caption é o rótulo que pode ser estampado no componente, suportando mudanças em
tempo de execução. Controles que exibem textos, fazem-no através da propriedade Caption
ou da propriedade Text. A propriedade a ser usada dependerá do tipo do controle. De um
modo geral, Caption é usado por textos que aparecem como títulos de uma janela ou um
rótulo (estampa), enquanto Text é usado por textos que aparecem como conteúdo de um
controle. Via de regra, Text podem ser editados pelo usuário, enquanto Caption é uma
propriedade que não recebe o foco da aplicação, tendo como característica a finalidade básica
de enviar uma informação ao usuário.
Muitos controles usam propriedades da classe AnsiString. Por exemplo, todos os controles que possuem
rótulo (forms, edits, panels, labels) usam AnsiString através da propriedade Caption. Outros controles
como o EditBox usam a classe AnsiString como base de seus textos (propriedade Text). Se repararmos
bem, notaremos que nós já temos usado e implementado objetos AnsiString sem qualquer espécie de
declaração. Em outra situação qualquer, a declaração e inicialização de uma string sempre será necessária
antes do uso respectivo.
A declaração de uma String é análoga à declaração de um tipo básico, porém usando a palavra
AnsiString seguida de um nome válido C++. Eis um exemplo:
AnsiString Pais;
Em dstring.h podemos observar que AnsiString é uma classe com seu próprio construtor e destruidor:
//
// Constructors
__fastcall AnsiString(): Data(0) {}
__fastcall AnsiString(const char* src);
__fastcall AnsiString(const AnsiString& src);
__fastcall AnsiString(const char* src, unsigned char len);
__fastcall AnsiString(const char* src, unsigned int len);
__fastcall AnsiString(const wchar_t* src);
__fastcall AnsiString(char src);
__fastcall AnsiString(short);
__fastcall AnsiString(unsigned short);
__fastcall AnsiString(int src);
Apostila de C++ Builder
Pagina -55-
__fastcall
__fastcall
__fastcall
__fastcall
__fastcall
__fastcall
__fastcall
AnsiString(unsigned int);
AnsiString(long);
AnsiString(unsigned long);
AnsiString(__int64);
AnsiString(unsigned __int64);
AnsiString(double src);
AnsiString(const WideString &src);
// Destructor
__fastcall ~AnsiString();
Logo você também pode declarar uma variável dela com parênteses vazios, determinando a chamada do
construtor da classe. Eis um exemplo:
AnsiString Animal();
Há dois modos principais para você iniciar uma variável AnsiString. Depois de declará-la, pode-se
determinar o valor desejado para a variável usando o nome escolhido. Eis um exemplo:
AnsiString Especie;
Especie = "Cachorro";
também podemos, tal qual nos tipos básicos, inicializar a variável String na sua declaração, fornecendo o
valor desejado para o nome escolhido. Eis um exemplo:
AnsiString Raca("Pastor Alemão");
Uma vez definida, a String poderá ser usada, por exemplo, para alterar o Caption de controles:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Meu_Cao;
Meu_Cao = " O Tobby é um vira-lata, que só sabe latir, latir e latir...";
Label1->Caption = Meu_Cao;
}
//---------------------------------------------------------------------------
Também poderá ser usada para preencher um controle Edit:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Pre_Hist = "Tiranossauro ou 286???";
Edit1->Text = Pre_Hist;
}
//---------------------------------------------------------------------------
Um texto introduzido num Edit box, por exemplo, é, por padrão, considerado uma String, visto que o
compilador não pode deduzir que espécie de dado ou valor o usuário deseja manipular. Por essa razão,
Apostila de C++ Builder
Pagina -56-
depois que um valor foi fornecido para um controle que fundamenta-se na classe AnsiString como base
de seu conteúdo, para executar qualquer operação matemática com tal String, devemos convertê-la para o
tipo de dado desejado. Para essa finalidade podemos, por exemplo, usar a função StrToInt():
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1->Caption = "111";
int i = StrToInt(Label1->Caption) - 11;
Edit1->Text = i;
}
Conforme apresentado, a classe AnsiString provê um grande número de construtores que nos permitem a
criação de strings de qualquer espécie:
Por exemplo, podemos usar:
um caracter:
AnsiString Simbolo = 'T';
um inteiro
AnsiString Int = 120;
um long int
AnsiString LongoInt = -73495745;
um valor ponto flutuante:
AnsiString PtoFlut = 675.15;
Um double:
AnsiString DplPrec = 2.15e28;
um string
AnsiString AloTher = "Alô, Thérbio";
Qualquer dessas variáveis pode ser declarada usando os construtores equivalentes:
//--------------------------------------------------------------------------AnsiString Simbolo('T');
AnsiString Int(120);
AnsiString AloTher("Alô, Thérbio");
AnsiString PtoFlut(675.15);
AnsiString LongoInt(-73495745);
AnsiString DplPrec(2.15e28);
//---------------------------------------------------------------------------
Baseado na configuração dos construtores AnsiString, podemos converter qualquer valor e torná-lo
disponível para um controle que use as propriedades AnsiString. Por exemplo, podemos converter e
exibir:
Apostila de C++ Builder
Pagina -57-
um caracter:
char Letra = 't';
Edit1 -> Text = AnsiString(Letra);
um inteiro:
Integer Numero = 256;
Edit1->Text = AnsiString(Numero);
um long integer:
long Valor = 4949431;
Panel1->Caption = AnsiString(Valor);
um valor ponto flutuante:
Single Distancia = 5698.03;
Label1->Caption = AnsiString(Distancia);
um double:
Double Tamanho = 24588;
Edit1->Text = AnsiString(Tamanho);
uma string
AnsiString Servico = "Limpador de Fogões";
Button2->Caption = AnsiString(Servico);
Nota: Você deve ter notado o uso de nomes de tipos diferentes dos apresentados anteriormente.
Por exemplo:
chamamos o tipo int de Integer, o tipo float de Single; o tipo double de Double ...
Por que será?
No início do arquivo dstring.h vemos a inclusão do arquivo de cabeçalho sysmac.h:
#include <sysmac.h>
NOTA: Logo abordaremos o uso da palavra typedef para criar outro nome para um tipo de
dados:
Vejamos um trecho do arquivo sysmac.h:
class PACKAGE TVarArray;
Apostila de C++ Builder
Pagina -58-
typedef TVarArray *PVarArray;
typedef bool
Boolean;
//
typedef int
Integer;
// -2147483648..2147484647
typedef char
Char;
// 0..255
typedef wchar_t
WideChar;
// Unicode character
typedef signed char
Shortint;
// -128..127
typedef short
Smallint;
// -32768..32767
typedef unsigned char
Byte;
// 0..255
typedef unsigned short
Word;
// 0..65535
typedef unsigned long
DWord;
// 0..4294967295
typedef void*
Pointer;
//
typedef char
AnsiChar;
//
typedef signed long
Longint;
// -2147483648..2147484647
typedef unsigned int
Cardinal;
// 0..2147484647
typedef long double
Extended;
// 10 byte real
typedef float
Single;
// 4 byte real
typedef double
Double;
// 8 byte real
typedef char* const
Openstring;
typedef void*
file;
//
typedef void*
Text;
//
typedef Text
TextFile;
//
typedef char*
PChar;
//
typedef char*
PAnsiChar;
//
typedef wchar_t*
PWideChar;
//
typedef unsigned char
ByteBool;
//
Apostila de C++ Builder
Pagina -59-
// D16 string/D32 shortstring formalparm
typedef unsigned short
WordBool;
//
typedef Cardinal
LongBool;
//
typedef AnsiString
String;
//
typedef SmallStringBase<255> ShortStringBase;
//
typedef SmallString<255>
ShortString;
//
typedef ShortString*
PShortString;
//
typedef AnsiString*
PAnsiString;
//
typedef PAnsiString
PString;
//
typedef WideString*
PWideString;
//
typedef Extended*
PExtended;
//
typedef Currency*
PCurrency;
//
typedef Variant*
PVariant;
//
typedef __int64
LONG64;
// !! obsolete
typedef GUID
TGUID;
//
typedef TGUID*
PGUID;
//
typedef HRESULT
HResult;
//
typedef Integer __fastcall (*TThreadFunc)(Pointer Parameter);
typedef void (*TModuleUnloadProc)(HINSTANCE hinstance);
typedef bool (*TEnumModuleFunc)(HINSTANCE hinstance, void *data);
typedef struct TVarData *PVarData;
Os nomes à direita representam os sinônimos que podem ser usados em substituição aos tipos básicos. Por
exemplo, Boolean pode ser usado em substituição ao tipo bool.
Nas próximas lições abordaremos várias implementações de AnsiString.
Apostila de C++ Builder
Pagina -60-
Nota: voltamos a insistir para que você não se preocupe quando se deparar com nomes de tipos de dados ainda não estudados
(como classe, por exemplo) visto que no momento apropriado do curso abordaremos tais tópicos com a profundidade
necessária.
Funções que modificam strings
Agora conheceremos algumas implementações do tipo AnsiString. Embora tenhamos, para facilidade de
compreensão, nos referido às instâncias de classes como variáveis, tecnicamente isso está incorreto. Na
verdade, a instância de uma classe (AnsiString, por exemplo) é denominada objeto. E os objetos possuem
funções-membro que facilitam sua manipulação. Essas funções-membro podem ser acessadas através do
operador ponto “.”.
Vejamos alguma funções-membro utilizadas para modificar strings.
// Modify string
AnsiString& __fastcall Insert(const AnsiString& str, int index);
AnsiString& __fastcall Delete(int index, int count);
AnsiString& __fastcall SetLength(int newLength);
int __fastcall Pos(const AnsiString& subStr) const;
AnsiString
__fastcall LowerCase() const;
AnsiString
__fastcall UpperCase() const;
AnsiString
__fastcall Trim() const;
AnsiString
__fastcall TrimLeft() const;
AnsiString
__fastcall TrimRight() const;
AnsiString
__fastcall SubString(int index, int count) const;
Insert()
AnsiString& __fastcall Insert(const AnsiString& str, int index);
insere uma string especificada dentro de AnsiString, iniciando a inserção na posição determinada pela
variável index.
O exemplo abaixo leva um Label no Form. Quando o usuário dá um clique no Label, o programa
providencia a inserção de uma string dentro de outra.
void __fastcall TForm1::Label1Click(TObject *Sender)
{
AnsiString test = "O_ _está_feito";
Label1->Caption = test.Insert("grande_teste", 3);
}
Delete()
AnsiString&
__fastcall Delete(int index, int count);
Apostila de C++ Builder
Pagina -61-
Remove um número especificado de caracteres de uma string. Inicia a contagem para a remoção na
variável especificada por index, encerrando a exclusão com a remoção do último caracter contado para
completar count:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
AnsiString test = "O_grande_teste_está_feito";
Label1->Caption = test.Delete(3, 12);
Label1->Caption = test.Insert(" ", 3);
}
SetLength()
AnsiString&
__fastcall SetLength(int newLength);
Determina um novo tamanho para a string, especificado por newLength, desde que esse novo
comprimento seja menor do que o tamanho inicial. SetLength não pode aumentar o tamanho da string:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
AnsiString test = "O_grande_teste_está_feito";
Label1->Caption = test.SetLength(14);
}
Pos()
int __fastcall Pos(const AnsiString& subStr) const;
a posição do primeiro caracter de uma substring especificada na string. Se a substring não for
encontrada na string, Pos() retorna “zero”.
void __fastcall TForm1::Label1Click(TObject *Sender)
{
AnsiString test = "O_grande_teste_está_feito";
Apostila de C++ Builder
Pagina -62-
Label1->Caption = test.Pos("está");
}
LowerCase() e UpperCase()
AnsiString
AnsiString
__fastcall LowerCase() const;
__fastcall UpperCase() const;
LowerCase() transforma todas as letras da string para letras minúsculas e UpperCase() transforma
todas para maiúsculas:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
AnsiString test = "O_grande_TESTE_está_FEITO";
Label1->Caption = test;
Label2->Caption = test.LowerCase();
Label3->Caption = test.UpperCase();
}
Funções que modificam strings
AnsiString ... continuação
AnsiString
AnsiString
AnsiString
AnsiString
__fastcall
__fastcall
__fastcall
__fastcall
Trim() const;
TrimLeft() const;
TrimRight() const;
SubString(int index, int count) const;
Trim(), TrimLeft()e TrimRight()
AnsiString
AnsiString
AnsiString
__fastcall Trim() const;
__fastcall TrimLeft() const;
__fastcall TrimRight() const;
Podemos usar essas funções-membro para eliminar caracteres em branco no início (TrimLeft()), no
final (TrimRight()) e no início e no final da string (Trim()):
Apostila de C++ Builder
Pagina -63-
void __fastcall
{
Label1->Color =
Label2->Color =
Label3->Color =
Label4->Color =
AnsiString test
Label1->Caption
Label2->Caption
Label3->Caption
Label4->Caption
TForm1::Label1Click(TObject *Sender)
clYellow;
clYellow;
clYellow;
clYellow;
= "
= test;
= test.Trim();
= test.TrimLeft();
= test.TrimRight();
O grande teste está feito
";
}
SubString()
AnsiString
__fastcall SubString(int index, int count) const;
Retorna uma substring especificada de uma string. A substring inicia a contagem dos caracteres em
index e termina de contá-los em count.
O exemplo abaixo possui um Button, um Edit e um Label no Form. Quando o usuário der um clique no
botão, a função membro SubString() da classe AnsiString será chamada para acessar uma substring
(parte da frase) contida na variável Frase:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
// Coloca uma string em Edit1
Edit1 -> Text = "Meu pai se chama Julio Alves";
// Declara e inicializa a variável Frase com a string contida no texto de em Edit1
AnsiString Frase = Edit1 ->Text;
/* Declara e inicializa a variável MeuPai com a string que começa no caracter 18
e termina no 28 de Frase*/
AnsiString MeuPai = Frase.SubString(18, 28);
// Concatena string com MeuPai, imprimindo no Label1
Label1 -> Caption = "Papai se chama " + MeuPai;
}
//---------------------------------------------------------------------------
Nota: Como você deve ter observado, já temos utilizado AnsiString em diversas oportunidades. Também
já utilizamos a denominação String que é análoga ao uso de AnsiString (Lembre-se da palavra-chave
typedef).
Como sabemos, o C++Builder possui várias funções para manipulação de strings. Veja abaixo um
exemplo com AnsiPos(), uma função que retorna a posição de um caracter dentro de uma String:
Apostila de C++ Builder
Pagina -64-
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Teste = "abcdefghijlmnopqrstuvxz";
ShowMessage((String)"A posição de 'q' é: " + AnsiPos('q', Teste));
}
AnsiString continuação...
(dstring.h)
Agora já conheceremos diversas funções-membro de AnsiString. Como exercício tente entender a lógica
de alguma funções localizadas no arquivo dstring.h. Evidentemente você deverá consultar o HELP do
C++Builder. Para tanto, você pode proceder da seguinte maneira. Escolha uma função-membro. Por
exemplo:
static AnsiString __fastcall IntToHex(int value, int digits);
Copie o nome da função para o Editor de Códigos do C++Builder:
IntToHex
Dê um clique para que o cursor fique situado sobre o nome da função e tecle F1.
O HELP do BCB deverá abrir-se automaticamente mostrando as eventuais opções para essa função. Eis
uma delas:
AnsiString::IntToHex
AnsiString
Description
Converts a number into a string containing the number's hexadecimal (base 16) representation.
static AnsiString __fastcall IntToHex(int value, int digits);
Value is the number to convert. Digits indicates the minimum number of hexadecimal digits.
Percebemos que a função em questão trata da conversão de valores inteiros para hexadecimais,
devolvendo AnsiString, onde int value é o valor a ser convertido e int digits é o número de
dígitos devolvido pela função na conversão:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
Label1->Caption = IntToHex(100000, 10);
}
Apostila de C++ Builder
Pagina -65-
Percebeu como é fácil?!? Sem dúvida alguma, a melhor fonte de pesquisas para compreendermos o
funcionamento do C++Builder é o seu próprio HELP. Se você realmente ambiciona aprender programar
C++ usando esse excelente compilador, entendemos que não existe melhor fonte de pesquisas. Grande
problema que parte dos programadores iniciantes encontram diz respeito ao idioma. Nem todos tem
facilidades com inglês. Confesso que eu também não possuo grande facilidade. Procuro supri-la com
programas que podem ser baixados na própria NET. Particularmente, eu prefiro o Babylon (considero as
versões antigas melhores. Aliás, tenho o ótimo hábito de não atualizar aplicativos com os quais eu me
identifico. Várias vezes me arrependi de atualizá-los, em face de “pequenas armadilhas” embutidas nas
novas versões do tipo: deixar de ser freeware. Nesses casos, se você não tomou o cuidado de fazer um
backup do programa de instalação antigo, a dor de cabeças e o arrependimento poderá ser considerável).
Um dos trabalhos que estaremos disponibilizando, sempre que possível, neste site é a tradução de partes
que consideramos fundamentais no HELP do BCB.
AnsiString continuação...
(operadores relacionais)
Embora ainda não tenhamos abordado o tema (fato reservado para lições futuras), já tivemos oportunidade
de apreciar exemplos com os comandos if e else. Por enquanto tenhamos em mente que esses comandos
são usados basicamente para efetuar comparações do tipo:
Se “A” é maior do que “B”, faça isso;
Senão, faça aquilo.
No caso acima, o termo maior está representando o operador relacional “>”. Para mais detalhes, dê uma
olhadinha no tópico que trata tal assunto.
Ansistring implementa métodos para tratamento de operadores relacionais:
// Comparisons
bool __fastcall
bool __fastcall
bool __fastcall
bool __fastcall
bool __fastcall
bool __fastcall
int __fastcall
int __fastcall
operator ==(const AnsiString& rhs) const;
operator !=(const AnsiString& rhs) const;
operator <(const AnsiString& rhs) const;
operator >(const AnsiString& rhs) const;
operator <=(const AnsiString& rhs) const;
operator >=(const AnsiString& rhs) const;
AnsiCompare(const AnsiString& rhs) const;
AnsiCompareIC(const AnsiString& rhs) const; //ignorecase
Vejamos outra implementação de AnsiString:
// Query attributes of string
int __fastcall Length() const;
Apostila de C++ Builder
Pagina -66-
Length() retorna o número de caracteres de um AnsiString. Logo podemos escrever um código
mesclando operadores relacionais com essa função:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Str = "Esta é a primeira string";
AnsiString Str2 = "Esta é a segunda string";
if(Str.Length() > Str2.Length())
ShowMessage((String)"A primeira string é maior. Ela possui " +
Str.Length() + " caracteres");
else
ShowMessage((String)"A segunda string é maior. Ela possui " +
Str2.Length() + " caracteres");
}
Depois que você estudar os comandos if e else, e os operadores relacionais, procure fazer alguns
exercícios implementando outros tipos de comparações suportadas por AnsiString , com outros
operadores relacionais.
AnsiString continuação...
(concatenation)
// Concatenation
AnsiString __fastcall operator +(const AnsiString& rhs) const;
Podemos usar o operador + para concatenar strings (colocar uma após a outra):
void __fastcall TForm1::Label1Click(TObject *Sender)
{
AnsiString AS1 ("\tTestando");
AnsiString AS2 ("\n\tAnsiString");
String S1 ("\n\nTestando");
String S2 ("\nString");
Label1 -> Caption = AS1 + AS2 + "\n\t\t\tOutro teste" + S1 + S2 + " !!!!";
}
A partir de agora assumimos que você já conhece razoavelmente a classe AnsiString. Porém ainda há
muito mais a conhecer sobre strings. Dedique algumas horas ao estudo, sempre procurando documentar
os estudos.
A palavra-chave
typedef
Podemos usar a palavra-chave typedef para criar um sinônimo para um tipo de dados existente qualquer.
Isso poderá nos auxiliar quando estivermos lidando com nomes de tipos muito longos e, portanto, sujeito a
erros de digitação. Por exemplo, na área dos cabeçalhos, digitamos:
Apostila de C++ Builder
Pagina -67-
typedef signed short int ssint;
e, a partir daí, todas as vezes que quisermos declarar uma variável signed short int poderemos fazê-lo
através da declaração: ssint:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// cria um sinônimo (ssint) para signed short int
typedef signed short int ssint;
// declara e inicializa variáveis ssint
ssint Altura = 15, Largura = 20, Comprimento = 26;
ssint Volume;
Volume = Altura * Largura * Comprimento;
// centraliza o caption de Label1
Label1 -> Alignment = taCenter;
// altera o estilo da font de Label1 para negrito, itálico e sublinhado
Label1->Font->Style = Label1->Font->Style<<fsBold<<fsItalic<<fsUnderline;
Label1 -> Caption = "******Cálculo do volume de um cubo******\n"
"Altura = " + String(Altura) + "\n"
"Largura = " + String(Largura) + "\n"
"Comprimento = " + String(Comprimento) + "\n"
"Volume = " + String(Volume);
}
A diretiva
#define
Podemos criar um nome para definir valores constantes através da diretiva de preprocessador #define.
Esse nome é conhecido por constante simbólica.
#define Kelvin -273
Embora Kelvin não tenha sido declarado como nenhum tipo em particular (char, float, AnsiString etc), o
compilador saberá lidar com os dados da melhor forma possível em virtude do estilo da declaração. Por
exemplo:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
#define Kelvin -273
#define Frase "Ibirarema é uma pequena cidade do Estado de São Paulo!"
#define pi 3.141592653589
#define letra 'A'
/* Essas constantes não são tão constantes afinal.
Apostila de C++ Builder
Pagina -68-
Se, mantendo a declaração anterior, fizéssemos uma
nova declaração para pi:
#define pi 10
o compilador aceitaria esse novo valor desconsiderando o anterior!!!!!*/
Label1 -> Caption = Frase "\n" + AnsiString(Kelvin * pi) + "\n" + letra;
}
Além da habilidade de definir constantes simbólicas, a diretiva #define pode ser usada para definir
macros, desde que se lhe forneça algum argumento:
#define SOMA(X, Y) (X + Y)
Em capítulos posteriores, estudaremos essa diretiva com detalhes.
A palavra-chave
const
Outra forma (melhor) que a linguagem C++ nos oferece para definir constantes é através da palavra-chave
const.
A diferença básica entre os dois tipos de declaração é o fato de que naquela (#define), é o tipo de
declaração quem informa ao compilador qual será o provável tipo, enquanto nesta (const), o tipo (char,
int etc) é declarado:
const int Kelvin = -273;
const double pi = 3.141592653589;
Este método é o mais recomendado na maioria dos casos, pois além de tornar o código mais fácil de ler e
manter, dificulta a introdução de bugs, posto que o compilador pode checar se a constante está sendo
usada de acordo com seu tipo.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
const int km = 170;
const AnsiString Itap = "Em Itapetininga faz muito frio!!!!";
const float mtr = 17;
const char alf = 'm';
// Erro .... Aqui estamos diante de uma verdadeira constante
Itap = "tentando alterar uma constante...";
Label1 -> Caption = Itap + "\n" + AnsiString(km + mtr) + "\n" + alf;
}
Apostila de C++ Builder
Pagina -69-
O tipo enum
O tipo enum cuida tratar de um tipo cuja sintaxe é bastante elaborada, para conter um conjunto de valores
definidos pelo usuário chamados dados de tipos enumerados ou enumeração.
Usando a palavra-chave enum, podemos definir um conjunto de constantes agrupadas sob um nome como
um novo tipo de dados, somando-se uma nova maneira às duas formas apresentadas anteriormente para
definição de constantes. A essa coleção de constantes, dá-se o nome de constantes enumeradas.
As constantes enumeradas possibilitam-nos a criação de novos tipos de dados, bem como a definição de
variáveis desses tipos, sendo que os valores assumidos ficam restritos a determinada coleção de valores.
Podemos, por exemplo, declarar uma enumeração representando os meses do ano:
enum Meses_do_Ano
{
janeiro,
fevereiro,
marco,
abril,
maio,
junho,
julho,
agosto,
setembro,
outubro,
novembro,
dezembro
}; // Fim de enum Meses_do_Ano
Feito isso, podemos definir variáveis do tipo Meses_do_Ano, que podem assumir apenas valores inteiros:
enum Meses_do_Ano
{
janeiro = 0,
fevereiro = 1,
marco = 2,
abril = 3,
maio = 20,
junho = 21,
julho = 22,
agosto = 56,
setembro = 57,
outubro = 58,
novembro = 200,
dezembro = 201
}
Apostila de C++ Builder
Pagina -70-
Observe que cada constante enumerada possui um valor inteiro e que podemos atribuir um valor
específico a determinada constante. Todavia se não especificarmos tal valor, a primeira constante
assumirá o valor 0; a segunda, 1; a terceira, 2; a quarta, 3; e assim sucessivamente. Como, em nosso
exemplo optamos por atribuir valores aleatórios às constantes, percebemos que as constantes subseqüentes
assumem valores com incremento de 1 a partir das que receberam um valor qualquer (em nosso exemplo,
os meses de maio, agosto e novembro).
Internamente as constantes enumeradas são representadas como sendo do tipo int.
O exemplo a seguir contém um Label no Form. Quando o usuário dá um clique no Label, uma caixa de
inserção de dados aparece para que seja digitado um valor entre 1 e 12. Dependendo do valor digitado,
teremos a resposta dos mês correspondente no Label:
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
enum Meses_do_Ano
{
janeiro = 1, // especifica o valor 1 para a primeira constante
fevereiro,
// vale 2
marco,
// vale 3 ...
abril,
maio,
junho,
julho,
agosto,
setembro,
outubro,
novembro,
dezembro
}; // Fim de enum Meses_do_Ano
// declara variável do tipo Meses_do_Ano
Meses_do_Ano Mes;
//declara variável int
int iMes;
iMes = StrToInt(InputBox ("Caixa para colheita de Valores",
"Digite um número entre 1 e 12", ""));
Mes = Meses_do_Ano(iMes);
switch(Mes)
{
case 1:
Label1 -> Color = clGreen; /*escolha uma cor diferente para cada mês*/
Label1 -> Font -> Name = "Arial"; /*e uma font para cada mês*/
Label1 -> Font -> Color = clYellow; /*e uma cor de font para cada mês*/
Label1 -> Caption = AnsiString(iMes) + " equivale a janeiro...";
break;
case 2:
Label1
Label1
Label1
Label1
->
->
->
->
Apostila de C++ Builder
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a fevereiro...";
Pagina -71-
break;
case 3:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a março...";
case 4:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a abril...";
case 5:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a maio...";
case 6:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a junho...";
case 7:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a julho...";
case 8:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a agosto...";
case 9:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a setembro...";
case 10:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a outubro...";
Apostila de C++ Builder
Pagina -72-
case 11:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a novembro...";
case 12:
Label1
Label1
Label1
Label1
break;
->
->
->
->
Color =
Font ->
Font ->
Caption
clGreen;
Name = "Arial";
Color = clYellow;
= AnsiString(iMes) + " equivale a dezembro...";
default:
ShowMessage(AnsiString(iMes) + " não é um valor válido...");
break;
}
}
//---------------------------------------------------------------------------
Operadores matemáticos
A linguagem C++ possui cinco operadores matemáticos binários representados pelos seguintes
operadores:
+ (adição);
- (subtração);
* (multiplicação);
/ (divisão); e
% (módulo);
e possui um operador unário:
- (menos unário).
Os operadores binários operam sobre dois operandos e o unário sobre um.
Os operadores de adição, subtração, multiplicação e divisão funcionam normalmente como nos cálculos
matemáticos usuais. Já o operador módulo fornece-nos o resto de uma divisão inteira (sem eventuais
partes fracionárias) como resultado. Por exemplo, quando fazemos a divisão de 9 por 5, o resultado é 1 e o
resto é 4.
O operador menos unário serve para trocar o sinal de seu operando (positivo para negativo ou negativo
para positivo):
Apostila de C++ Builder
Pagina -73-
int i = 440;
i = -i;
Label1 -> Caption = i; // Agora o valor de i é - 440
O exemplo a seguir (calculadora básica) possui, no Form, três componentes Edit e cinco RadioButton(s)
dentro de um RadioGroup. Todas as propriedades do programa serão geradas em tempo de execução (no
código), e não diretamente no Object Inspector. O usuário deverá digitar um valor no Edit1, outro valor
no Edit2 e escolher uma operação matemática no RadioGroup para visualizar o resultado no Edit3.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Form1 -> Caption = "Calculadora Básica";
Form1 -> BorderStyle = bsSizeToolWin; // altera a borda de Form1
Edit1 ->
Edit2 ->
Edit3 ->
Edit3 ->
soltar)
Text = "";
Text = "";
Text = "";
ReadOnly =
// esvazia Edit1
// Esvazia Edit2
// esvazia Edit3
true; // Edit3 não aceita dados via teclado ou mouse (arrastar e
RadioGroup1 -> Caption = "Escolha uma operação";
RadioButton1 -> Caption = "+ (adição)";
RadioButton2 -> Caption = "- (subtração)";
RadioButton3 -> Caption = "* (multiplicação)";
RadioButton4 -> Caption = "/ (divisão)";
RadioButton5 -> Caption = "% (módulo)";
}
//--------------------------------------------------------------------------void __fastcall TForm1::RadioButton1Click(TObject *Sender)
{
/*Na operação de adição precisamos declarar variáveis e realizar as respectivas
coversões para evitar erros de concatenação de strings em face do operador + */
double primeiro = StrToFloat(Edit1 -> Text);
double segundo = StrToFloat(Edit2 -> Text);
Edit3->Text = primeiro + segundo; //realiza o cálculo por via indireta
Edit1->SetFocus(); //chama a função SetFocus() para colocar o foco em Edit1
}
//--------------------------------------------------------------------------void __fastcall TForm1::RadioButton2Click(TObject *Sender)
{
Apostila de C++ Builder
Pagina -74-
Edit3->Text = double((Edit1->Text) - (Edit2->Text)); //realiza o cálculo por via
direta
Edit1->SetFocus(); //chama a função SetFocus() para colocar o foco em Edit1
}
//--------------------------------------------------------------------------void __fastcall TForm1::RadioButton3Click(TObject *Sender)
{
Edit3->Text = double((Edit1->Text) * (Edit2->Text)); //realiza o cálculo por via
direta
Edit1->SetFocus(); //chama a função SetFocus() para colocar o foco em Edit1
}
//--------------------------------------------------------------------------void __fastcall TForm1::RadioButton4Click(TObject *Sender)
{
Edit3->Text = double((Edit1->Text) / (Edit2->Text)); //realiza o cálculo por via
direta
Edit1->SetFocus(); //chama a função SetFocus() para colocar o foco em Edit1
}
//--------------------------------------------------------------------------void __fastcall TForm1::RadioButton5Click(TObject *Sender)
{
/* Essa parte do código foi corrigido com o comando if ...else pois
apresentava um bug nas operações em que o divisor era maior que o dividendo*/
//se o valor convertido para ponto flutuante de Edit1 for maior do que o de Edit2
if (StrToFloat(Edit1 -> Text) > StrToFloat(Edit2 -> Text))
// Edit3 apresentará o resto da divisão de Edit1->Text por Edit2->Text
Edit3 -> Text = double((Edit1 -> Text) % (Edit2 -> Text));
// caso contrário
else
// Edit3 apresentará o valor 0 (zero) em seu texto
Edit3 -> Text = 0;
Edit1 -> SetFocus(); // chama a função SetFocus() para colocar o foco em Edit1
}//---------------------------------------------------------------------------
Expressões
Todo comando que ao ser efetuado retorna um valor, é considerado uma expressão em C++.
São expressões:
-273; // Retorna o valor -273
x = (a + b) - ((c * d) / e);
e assim por diante...
Observe as expressões no exemplo a seguir que leva apenas um Label no Form e será usado para calcular
o resultado de uma equação de segundo grau:
Apostila de C++ Builder
Pagina -75-
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include <math.h> // Declarar essa biblioteca para funções matemáticas
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------AnsiString a, b, c;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Form1->BorderStyle = bsNone; //retira as bordas e barra de títulos do programa
Label1 -> Caption = "*****CALCULA EQUAÇÃO DE SEGUNDO GRAU*****\n"
"Dê um clique aqui para digitar os valores \"a\", \"b\" e \"c\" ";
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
Form1 -> AutoSize = true; // ajusta o tamanho do form ao label
try
{
retornoA:
a = InputBox("Caixa de Entrada de Valores",
"Digite um valor para 'a'", "");
StrToInt(a);
if (a == 0)
{
MessageBox(0,"Zero não é um valor válido para 'a' !"
"\nDigite outro valor... ",
"Erro no valor escolhido", 16);
goto retornoA;
}
else
Label1 -> Caption = " a = " + a;
Label1 -> Font -> Color = clRed;
Label1 -> Font -> Style = TFontStyles()<< fsBold;
}
catch(...)
{
MessageBox(0, "Erro ... Valor não suportado pelo programa ...",
"Erro de digitação", 16);
}
try
{
b = InputBox("Caixa de Entrada de Valores",
"Digite um Valor para 'b'", "");
Apostila de C++ Builder
Pagina -76-
StrToInt(b);
Label1 -> Caption = Label1 -> Caption + "\n b = " + b;
}
catch(...)
{
MessageBox(0, "Erro ... Valor não suportado pelo programa ...",
" Erro de digitação", 16);
}
try
{
c = InputBox("Caixa de Entrada de Valores",
"Digite um Valor para 'c'", "");
StrToInt(c);
Label1 -> Caption = Label1 -> Caption + "\n c = " + c;
}
catch(...)
{
MessageBox(0, "Erro ... Valor não suportado pelo programa ...",
"Erro de digitação", 16);
}
try
{
Label1 -> Font -> Color = clBlue;
Label1 -> Font -> Style = TFontStyles()<< fsBold;
double x1, x2, delta;
delta = (b*b) - ((a * 4) * c);
if(delta < 0)
MessageBox(0,"delta negativo... não existem raízes reais...\n"
"\ntente novamente com outros valores...",
"Erro nos valores", 16);
else
{
Label1 -> Caption =
Label1 -> Caption +
"\n\ndelta = " + FloatToStr(delta);
x1 = ((b * (-1)) + (sqrt (delta))) / (a * 2);
Label1 -> Caption =
Label1 -> Caption + "\nx1 = " + FloatToStr(x1);
x2 = ((b * (-1)) - (sqrt (delta))) / (a * 2);
Label1 -> Caption =
Label1 -> Caption + "\nx2 = " + FloatToStr(x2);
}
}
catch(...)
{
MessageBox(0, "Por favor, digite os valores corretamente",
"Erro de execução...", 16);
}
}
Apostila de C++ Builder
Pagina -77-
//--------------------------------------------------------------------------void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key)
{
if (Key == 13) // se for pressionada a tecla Enter
Close();
// encerra o programa
}
//---------------------------------------------------------------------------
Entendendo melhor o C++Builder
Quando você inicia o C++Builder, ele escreve automaticamente algumas linhas de código para a sua
aplicação. Tentaremos entender o que significa cada uma dessas linhas:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
Inicialmente, na parte superior da janela, podemos contemplar uma linha de comentários, constituída
basicamente de vários hífens seguindo //. Como sabemos, em C++ tudo o que segue a dupla de caracteres
// será ignorado pelo compilador até o final da linha. O compilador insere essas linhas, basicamente, para
separar seções de códigos ou funções. Eles estão ali, apenas, para melhorar a visualização.
Perceba que, existem outras linhas iguais a essa no código e que sempre que codificarmos um um método
de algum evento, através da chamada no Object Inspector, essas linhas de comentário estarão lá, criadas
automaticamente para melhorar a visualização dos códigos.
#include <vcl.h>
Já sabemos que quando o pré-processador encontra a diretiva #include em nosso código fonte, ele
substitui a mesma pelo conteúdo do arquivo indicado, sendo que o compilador encontrará e processará o
arquivo implementado, e não a diretiva de compilação. Avançado no estudo do código inicial inserido
automaticamente pelo C++Builder, encontramos essa diretiva de determina a inclusão do arquivo
chamado vcl.h:
Apostila de C++ Builder
Pagina -78-
#include <vcl.h>
Se você instalou o C++Builder no diretório padrão, poderá abrir e visualizar esse arquivo em
C:\Arquivos de programas\Borland\CBuilder6\Include\Vcl\vcl.h
Vejamos o seu conteúdo:
/////////////////////////////////////////////////////////////////////////////////
// VCL.H - Borland C++ Builder pre-compiled header file
// $Revision:
1.12.1.0.1.1 $
// Copyright (c) 1997, 2002 Borland Software Corporation
/////////////////////////////////////////////////////////////////////////////////
#ifndef VCL_H
#define VCL_H
#define INC_VCL
#include <basepch0.h>
#endif
// VCL_H
A vcl.h é parte da Visual Component Library usada para linkar a definição de componentes como botões,
menus etc. Perceba que no arquivo em questão aparece novamente um include. Vejamos o conteúdo do
arquivo basepch0.h:
/////////////////////////////////////////////////////////////////////////////////
// BASEPCH0.H - Borland C++ Builder pre-compiled header file
// $Revision:
1.0.1.0.1.0 $
// Copyright (c) 1997, 2002 Borland Software Corporation
//
// BASEPCH0.H is the core header that includes VCL/CLX headers. The headers
// included by BASEPCH0.H are governed by the following macros:
//
// MACRO
DESCRIPTION
DEFAULT
// =======
=============
=======
//
// NO_WIN32_LEAN_AND_MEAN When this macro is defined, BASEPCH.H does
OFF
//
not define WIN32_LEAN_AND_MEAN.
//
// INC_VCL
This macro is defined by VCL.H to
OFF
//
include the base set of VCL headers
//
// VCL_FULL
Same as NO_WIN32_LEAN_AND_MEAN
OFF
//
(NOTE: This macro is for BCB v1.0 backward
//
compatibility)
//
// INC_VCLDB_HEADERS
When this macro is defined, VCL.H includes
//
requires INC_VCL
the core Database headers of VCL.
OFF
//
(Defining this macro is functionally
//
equivalent to including VCLDB.H)
//
// INC_VCLEXT_HEADERS
When this macro is defined, VCL.H includes
//
requires INC_VCL
all VCL headers.
OFF
//
(Defining this macro is functionally
Apostila de C++ Builder
Pagina -79-
//
equivalent to including VCLMAX.H)
//
// INC_CLX
This macro is defined by CLX.H to include
OFF
//
the base set of CLX headers
//
// INC_CLXDB_HEADERS
When this macro is defined, CLX.H includes
//
requires INC_CLX
the core Database headers of CLX.
OFF
//
// INC_CLXEXT_HEADERS
When this macro is defined, CLX.H includes
//
requires INC_CLX
all CLX headers.
OFF
//
/////////////////////////////////////////////////////////////////////////////////
#ifndef __BASEPCH0_H__
#define __BASEPCH0_H__
// v1.0 of BCB checked for VCL_FULL to avoid LEAN_AND_MEAN support
//
#if defined(VCL_FULL)
// BCB v1.0 compatible
#define NO_WIN32_LEAN_AND_MEAN
#endif
#if !defined(_WINDOWS_) // Don't optimize
#if !defined(NO_WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
//
#define _VCL_LEAN_AND_MEAN
//
#endif
//
#endif
//
if WINDOWS.H has already been included
Enable LEAN_AND_MEAN support
BCB v1.0 compatible
NO_WIN32_LEAN_AND_MEAN
_WINDOWS_
#if !defined(COM_NO_WINDOWS_H) //Make sure COM Headers don't include WINDOWS.H/OLE2.H
#define COM_NO_WINDOWS_H
#define UNDEF_COM_NO_WINDOWS_H
#endif
#if !defined(RPC_NO_WINDOWS_H) // Make sure RPC Headers don't include WINDOWS.H
#define RPC_NO_WINDOWS_H
#define UNDEF_RPC_NO_WINDOWS_H
#endif
// Core (minimal) Delphi RTL headers
//
#include <System.hpp>
#include <Types.hpp>
#include <Windows.hpp>
#include <Messages.hpp>
#include <SysUtils.hpp>
#include <Classes.hpp>
// Core (minimal) VCL headers
//
#if defined(INC_VCL)
#include <Controls.hpp>
#include <Graphics.hpp>
#include <Forms.hpp>
#include <Dialogs.hpp>
#include <StdCtrls.hpp>
#include <ExtCtrls.hpp>
Apostila de C++ Builder
Pagina -80-
// Full set of VCL headers
//
#if defined(INC_VCLEXT_HEADERS)
#include <Buttons.hpp>
#include <ChartFX.hpp>
#include <ComCtrls.hpp>
#include <DdeMan.hpp>
#include <FileCtrl.hpp>
#include <GraphSvr.hpp>
#include <Grids.hpp>
#include <MPlayer.hpp>
#include <Mask.hpp>
#include <Menus.hpp>
#include <OleCtnrs.hpp>
#include <OleCtrls.hpp>
#include <Outline.hpp>
#include <Tabnotbk.hpp>
#include <Tabs.hpp>
#include <VCFImprs.hpp>
#include <VCFrmla1.hpp>
#include <VCSpell3.hpp>
#endif // INC_VCLEXT_HEADERS
#endif
//
INC_VCL
// Core (minimal) CLX headers
//
#if defined(INC_CLX)
#include <Qt.hpp>
#include <BindHelp.hpp>
#include <QConsts.hpp>
#include <QStdActns.hpp>
#include <QStyle.hpp>
#include <QControls.hpp>
#include <QTypes.hpp>
#include <QGraphics.hpp>
#include <QForms.hpp>
#include <QDialogs.hpp>
#include <QStdCtrls.hpp>
#include <QExtCtrls.hpp>
#include <QMenus.hpp>
#include <QClipbrd.hpp>
#include <QMask.hpp>
#include <QButtons.hpp>
#include <QComCtrls.hpp>
// CLX Database related headers
//
#if defined(INC_CLXDB_HEADERS)
#include <QDBCtrls.hpp>
#include <DB.hpp>
#include <DBTables.hpp>
#endif // INC_CLXDB_HEADERS
// Full set of CLX headers
//
Apostila de C++ Builder
Pagina -81-
#if defined(INC_CLXEXT_HEADERS)
#include <QDBGrids.hpp>
#include <QDBCtrls.hpp>
#include <QDBActns.hpp>
#include <QGrids.hpp>
#include <QImgList.hpp>
#include <QSearch.hpp>
#include <QActnList.hpp>
#include <QCheckLst.hpp>
#include <QPrinters.hpp>
#endif // INC_CLXEXT_HEADERS
#endif
//
INC_CLX
#if defined(INC_OLE_HEADERS)
#include <cguid.h>
#include <dir.h>
#include <malloc.h>
#include <objbase.h>
#include <ole2.h>
#include <shellapi.h>
#include <stddef.h>
#include <tchar.h>
#include <urlmon.h>
#include <AxCtrls.hpp>
#include <OleCtnrs.hpp>
#include <OleCtrls.hpp>
#endif
// Using ATLVCL.H
//
#if defined(INC_ATL_HEADERS)
#include <atl\atlvcl.h>
#endif
#if defined(UNDEF_COM_NO_WINDOWS_H) //Clean up MACRO to prevent inclusion of WINDOWS.H/OLE2.H
#undef COM_NO_WINDOWS_H
#undef UNDEF_COM_NO_WINDOWS_H
#endif
#if defined(UNDEF_RPC_NO_WINDOWS_H) // Clean up MACRO to prevent inclusion of WINDOWS.H
#undef RPC_NO_WINDOWS_H
#undef UNDEF_RPC_NO_WINDOWS_H
#endif
#endif
// __BASEPCH0_H__
Veja quantos include. Por exemplo, #include <Buttons.hpp>, #include <Menus.hpp> etc. Um bom
conhecimento dessas bibliotecas, pode nos facilitar bastante a compreensão. Todavia, por enquanto, não
se preocupe com isso, mesmo porque o compilador cuida muito bem da tarefa, à margem de nosso
profundo, ou escasso, conhecimento de suas particularidades. Quanto ao estudo das diretivas, no avançar
do curso procuraremos conhecer pelo menos um pouco de cada uma delas.
Apostila de C++ Builder
Pagina -82-
#pragma hdrstop
A seguir, encontramos a seguinte linha de códigos:
#pragma hdrstop
Através da diretiva #pragma, o C++Builder pode definir diretivas que podem ser passadas ao compilador.
Se o compilador não identificar o nome da diretiva, ele simplesmente ignora #pragma sem emitir qualquer
aviso ou mensagem de ERRO. A sintaxe de pragma é #pragma nome_da_diretiva.
O compilador C++Builder suporta as seguintes diretivas #pragma:
#pragma alignment
#pragma anon_struct
#pragma argsused
#pragma checkoption
#pragma codeseg
#pragma comment
#pragma defineonoption
#pragma exit
#pragma hdrfile
#pragma hdrstop
#pragma inline
#pragma intrinsic
#pragma link
#pragma message
#pragma nopushoptwarn
#pragma obsolete
#pragma option
Apostila de C++ Builder
Pagina -83-
#pragma pack
#pragma package
#pragma resource
#pragma startup
#pragma undefineonoption
#pragma warn
Sintaxe: #pragma hdrstop
Descrição: Esta diretiva termina a lista de hearde files escolhidos para pré-compilação. Você pode usá-lo
para reduzir a quantia de espaço em disco usados por headers de pré-compilação.
Hearder files pré-compilados podem ser compartilhados entre os arquivos fonte de seu projeto sendo que
somente as diretivas #include antes de #pragma hdrstop são idênticas (comuns a todos os arquivos do
projeto). Então, você adquire o melhor desempenho do seu compilador se você incluir os header files
comuns de seu projeto antes de #pragma hdrstop, e os específicos depois disto. Certifique-se de ter
inserido as diretivas #include antes de #pragma hdrstop de forma indêntica em todo o código fonte, ou
que haja apenas pequenas variações.
O ambiente de desenvolvimento integrado gera o código para aumentar desempenho de header précompilados. Por exemplo, depois de uma Aplicação Nova, o arquivo fonte " Unit1.cpp" terá a seguinte
aparência (comentários adicionados):
#include <vcl.h> // arquivos de header comuns
#pragma hdrstop // fim da lista comum
#include "Unit1.h" // header files específicos
// ....
Só se usa essa diretiva pragma em arquivos fonte. Ela não tem nenhum efeito quando usada em um
arquivo header (.h, .hpp etc).
“Unit1.h”
A seguir, percebemos mais um enunciado include inserido automaticamente pelo BCB:
Apostila de C++ Builder
Pagina -84-
#include “Unit1.h”
O arquivo de cabeçalho “Unit1.h” é criado simultaneamente com o arquivo “Unit1.cpp” que é o arquivo
que contém o código principal de um aplicativo (aquele onde temos efetuado a maioria das inclusões de
códigos até agora). Já sabemos que os cabeçalhos colocados entre aspas “...” determinam que o préprocessador procure o cabeçalho primeiro no diretório atual, e depois na biblioteca padrão (Include).
Entende-se por diretório atual, aquele onde salvamos o projeto, ou seja, onde se encontram os arquivos do
projeto gerados pelo sistema no desenvolvimento do programa. Eventualmente, se o arquivo não se
encontrar em nenhum desses dois diretórios, podemos indicar o seu caminho completo entre as aspas:
“C:\\Meu_aplicativo\\Unit1.h”
Evidentemente que Unit1.h é o nome padrão dado pelo C++Builder ao arquivo. Se, num projeto,
salvarmos os arquivos com nomes diferentes, esses nomes serão aqueles que encontraremos
acompanhados do “.h” e do “.cpp”. Por exemplo, “Edit_Texto.h” e “Edit_Text.cpp”. Outra questão
importante diz respeito ao número 1 entre Unit e o .h ou o .cpp. Esse número refere-se ao número do
componente na aplicação. Se colocarmos um segundo Form no aplicativo, o arquivo principal dele será
nomeado de Unit2.cpp; um terceiro, de Unit3.cpp , e assim por diante. Evidentemente, conforme exposto,
não precisamos concordar com nomes padrão.
Em outra seção, abordaremos técnicas de como dar nomes a nossas aplicações e aos componentes nelas
inseridos. Porém, no nosso curso, raramente trocaremos o nome padrão fornecido pelo compilador. Por
um motivo muito simples. O nome padrão facilita a compreensão para estudantes de línguas diferentes,
que não compreendem nosso idioma. Quando eu fazia pesquisas na internet, várias vezes pude aproveitar
códigos, cujos autores eu nem poderia, de mim mesmo, imaginar a nacionalidade, de tão complexa a
forma de escrever (tipo chinês, russo, grego etc). Mesmo assim, quando os códigos eram mantidos no
default fornecido pelo C++Builder, eram-me bastante claros (exceto strings e comentários,
evidentemente).
Penso que é o mínimo que posso fazer, uma vez que a maior parte do que consegui aprender sobre
programação foi através da Internet, através de uma infinidades de generosos professores anônimos.
Porém, quando eu aproveitar exemplos que já fiz anteriormente, alterando os nomes, manterei estes
nomes.
“#pragma package(smart_init)”
Sintaxe:
#pragma package(smart_init)
#pragma package(smart_init, weak)
Descrição: smart_init argument
Apostila de C++ Builder
Pagina -85-
A diretiva #pragma package(smart_init) é utilizada durante a inicialização do aplicativo. Ela assegura
que qualquer unidade de códigos requisitada pelo programa será inicializada na segqüência correta, ou
seja, serão inicializadas na ordem determinada por suas dependências (Incluída por default no pacote
arquivo fonte). Tipicamente, você pode usar o #pragma package para arquivos .CPP que são criados
como packages.
Esta diretiva pragma afeta a ordem de inicialização desta unidade de compilação. Para units, a
inicialização ocorre na seguinte ordem:
1. Pelas suas dependências de "usos", quer dizer, se unitA depende de unitB, unitB deve ser inicializa
antes de unitA.
2. A ordem de vínculo.
3. Ordem de prioridade dentro da unit.
para arquivos .OBJ regulares (aqueles não construídos como units), a inicialização acontece primeiro de
acordo com a ordem de prioridade e, então, de acordo com a ordem de vínculo. Mudando a ordem de
vínculo dos arquivos .OBJ muda-se a ordem na qual os objetos construtores globais são chamados.
Os exemplos seguintes mostram como a inicialização difere entre units e arquivos .OBJ regulares.
Tome como um exemplo três arquivos unit, A, B e C que são “inteligentemente inicializados” com
#pragma package(smart_init) e possuem valor de prioridade (definidas no parâmetro prioridades do
#pragma startup) setados de 10, 20 e 30. As funções são nomeadas de acordo com os seus valores de
prioridades e o parent .OBJ. assim os nomes são a10, a20, a30, b10, e assim por diante.
Desde que todas as três são units, e se A usa B e C e a ordem de vínculo é A, B e então C, a ordem de
inicialização é:
B10 B20 B30 C10 C20 C30 A10 A20 A30
Se, ao invés de units, o arquivo anterior fosse .OBJ a ordem seria:
A10 B10 C10 A20 B20 C20 A30 B30 C30
Os arquivos .CPP que usam #pragma package(smart_init) também requerem que algum #pragma link
referencie outro arquivo .OBJ desde um arquivo .CPP que declara #pragma package(smart_init), e deve
estar resolvido na unit. Se #pragma link fizer referências para arquivos não .OBJ pode ainda ser
resolvido por bibliotecas etc.
Descrição: weak packages
A diretiva #pragma package(smart_init, weak) afeta o modo que um arquivo .OBJ é armazenado num
package’s .BPI e arquivos .BPL. Se #pragma package(smart_init, weak) aparece em um arquivo de unit,
Apostila de C++ Builder
Pagina -86-
o compilador omite a unit de BPLs quando possível, e cria uma cópia local non-packaged da unit quando
ela é requerida por outra aplicação ou package. Uma unit compilada com esta diretiva é dita como sendo
“weakly packaged”.
#pragma package(smart_init, weak) é usado para eliminar conflitos entre packages que possivelmente
dependem da mesma biblioteca externa.
Arquivos Unit contendo a diretiva #pragma package(smart_init, weak) não devem ter variáveis globais.
para maiores informações sobre uso weak packages, veja Weak packaging.
#pragma resource
Sintaxe:
#pragma resource “*.dfm”
Descrição: Esta diretiva pragma faz o arquivo ser marcado como uma unidade de Form e requer
combinação .dfm. Ou seja, ela determina que o compilador utilize tal arquivo (.dfm) de definição do
Form, requisitado por essa Unit. Tais arquivos são administrados pelo IDE.
Se seu Form requer qualquer variável, elas devem ser declaradas depois da inscrição do pragma resource.
As declarações devem ser pertencentes ao Form.
TFormName *Formname;
TForm
TForm representa uma janela padrão da aplicação (Form).
Unit: Forms
Descrição:
Quando você cria formulários no Form designer em tempo de projeto, eles são implementados como
descendentes de TForm. Forms podem representar a janela principal da aplicação, caixas de diálogo, ou
janelas filhas (MDI children). Um form pode conter outros objetos como TButton, TCheckBox,
TcomboBox etc.
Exemplos de forms incluem objetos TLoginDialog e TPasswordDialog.
Apostila de C++ Builder
Pagina -87-
Nota: Quando trabalhamos com objetos form, não chamamos explicitamente os métodos Free ou Destroy
para liberá-los. Em vez disso, usamos o método liberar (Release method), o qual espera até que todos
event handlers (parte do programa que trata determinadas situações) tenham terminados sua execução.
_fastcall, __fastcall
Categoria: modificadores C++Builder, extensão de palavra-chave
Sintaxe:
valor_de_retorno _fastcall nome_da_função(parm-lista)
valor_de_retorno __fastcall nome_da_função(parm-lista)
Descrição:
use o modificador __fastcall para declarar funções que aguardam parâmetros para serem passados em
registros. os três primeiros parâmetros são passados (da esquerda para a direita) em EAX, EDX, e ECX,
se eles se ajustarem no registro. Os registros não são usados se o parâmetro é um ponto flutuante ou uma
estrutura (struct type).
Todas as funções-membro da classe Form devem usar a convenção __fastcall.
O compilador trata essa convenção chamada como uma nova language, ao longo das linhas of _cdecl e
_pascal
Funções declaradas usando _cdecl ou _pascal não podem ter os modificadores _fastcall porque eles usam
a pilha para passar os parâmetros. Igualmente, o modificador __fastcall não pode ser usado junto com
_export.
O compilador antepõe o nome da função __fastcall com um a-sinal (at-sign) ("@"). Este prefixo aplica-se
a desqualificação de nomes de função C e para C++.
Para implementação estilo Microsoft VC++ __fastcall, veja __msfastcall e __msreturn.
Nota: o modificador __fastcall está sujeito desqualificação do nome da função. Veja a descrição do -VC
option.
TComponent
TComponent é o ancestral comum de todos os componentes que podem aparecer no form designer.
Apostila de C++ Builder
Pagina -88-
Unit: Classes
Descrição:
Components são objetos persistentes que possuem as seguintes capacidades:
A capacidade de aparecer no Componente palette e ser transferido para o form designer.
A capacidade de administrar-se, a si, e a outros componentes.
Incrementar o fluxo e armazenar capacidades.
A capacidade de ser convertido em um controle ActiveX ou outro objeto COM por wizards na página
ActiveX do diálogo New Objects.
1.
2.
3.
4.
Não crie instâncias de TComponent. Use TComponent como uma classe base quando declarar um
componente não-visual que pode mostrar-se no component palette e ser usado no form designer.
Propriedades e métodos de TComponent fornecem comportamentos básicos que classes dependentes
herdam e também comportamentos que componentes podem ativar para ajustar seu comportamento.
Para criar componentes os quais são visíveis em tempo de execução para os usuários, use TControl ou
seus descendentes como base. Para criar controles baseados em objetos janelas, use TWinControl ou seus
descendentes como base.
TComponent::Owner
Owner = Proprietário
Indica o componente que é responsável por carregar e liberar seus componentes.
__property TComponent* Owner = {read=FOwner};
Descrição:
Use Owner para encontrar o proprietário de um componente. O Owner de um componente é responsável
por duas coisas:
A memória referente ao componente é liberada assim que a memória de seu proprietáro é liberada.
O Owner é responsável por carregar e salvar as propriedades published de seu controle proprietário.
Por default, um form possui todos os componentes que estão nele. Por sua vez, o form é pertencente à
aplicação. Assim, quando a aplicação é encerrada e a memória correspondente é liberada, a memória para
todos os forms (e todos os seus componentes) também é liberada. Quando um form é carregado na
memória, automaticamente são carregados todos os componentes que nele estão instalados.
Apostila de C++ Builder
Pagina -89-
O proprietário de um componente é determinado pelo parâmetro passado ao construtor quando o
componente é criado. Pelo fato de os componentes serem criados no form designer, o form é
automaticamente nomeado como o proprietário.
Aviso: Se um componente tem um proprietário diferente de um form ou módulo de dados, ele não será
salvo ou carregado com seu proprietário, a menos que você o identifique como um subcomponente. Para
identificar um componente como um subcomponente, chame o método SetSubComponent.
Operadores de incremento e decremento
Você já deve ter percebido que mencionamos expressões do tipo incremento e decremento. Num sentido
mais técnico, trata-se de dois operadores de extrema utilidade em C++.
O operador de incremento é formado por dois sinais de adição ++ e adiciona 1 ao valor da variável à qual
é aplicado. Num exemplo anterior (Operações matemáticas com unsigned), a expressão:
uiAcresce = uiAcresce + 1;
produziria o mesmo resultado se fosse substituída por:
uiAcresce++;
Já o operador de decremento é formado por dois sinais de subração -- e subtrai 1 do valor da variável à
qual é aplicado:
uiDecresce--;
é equivalente a:
uiDecresce = uiDecresce - 1;
É relevante a posição dos operadores de incremento (++) ou decremento (--) em relação à variável:
++uiAcresce; // operador antecedendo do nome da variável - prefixo
uiAcresce++; // operador após o nome da variável - sufixo
--uiDecresce; // prefixo
uiDecresce--; // sufixo
Apostila de C++ Builder
Pagina -90-
Na posição de prefixo, primeiro o operador é aplicado e depois a variável tem seu valor acessado. Na
posição de sufixo, o valor da variável é acessado antes de o operador ser aplicado.
O exemplo abaixo usa um Button e um Memo no Form. Quando o usuário dá cliques no botão, as
variáveis são, conforme o caso, incrementadas ou decrementadas, com operadores pós e pré-fixados. A
diferença dos resultados que tais operadores operam nas variáveis nessas diversas situações é demonstrada
no Memo:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------int Acresce = 100, ACRESCE = 100,
Decresce = 100, DECRESCE = 100;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Lines->Add("incrementa Acresce com sufixo " + String(Acresce++) +
"
" "incrementa ACRESCE com prefixo " + String(++ACRESCE));
Memo1->Lines->Add("decrementa Decresce com sufixo " + String(Decresce--) +
"
" "decrementa DECRESCE com prefixo " + String(--DECRESCE));
}
//---------------------------------------------------------------------------
Operadores relacionais
Existem alguns operadores que fazem comparações e, por isso, são chamados de relacionais:
Apostila de C++ Builder
>
maior
>=
maior ou igual
<
menor
<=
menor ou igual
==
igual a
Pagina -91-
>
maior
!=
diferente
Os operadores relacionais são bastante usados pelas partes dos programas que trabalham com laços e
comandos de decisão. O exemplo a seguir usa um desses operadores num programa que coloca um efeito
degradê no form:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormPaint(TObject *Sender)
{
/* inicia o loop (laço) for com variável ponto flutuante. Aqui está
sendo usado um operador relacional menor ou igual */
for ( float fVar = 0; fVar <= 200; fVar = fVar + 0.1)
{
TRect Val; // tipo que define um retângulo
// função que agrupa as coordenadas do retângulo especificado
SetRect((RECT *)&Val,0,(Height*fVar) / 200, Width,(Height*(fVar+1)) / 200);
//determina a cor para preenchimento do background.
Canvas->Brush->Color=RGB(0,0,255-fVar*1.1);
// preenche o retângulo especificado no canvas usando o Brush atual
Canvas->FillRect(Val);
}
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormResize(TObject *Sender)
{
/* determina a repintura do controle depois de outra importante mensagem Windows ser
tratada*/
Invalidate();
}
//---------------------------------------------------------------------------
O comando if
No início deste curso informamos que usaríamos alguns nomes, comandos ou tipos de dados com os quais
você poderia não estar habituado a trabalhar. Em algumas ocasiões, já tivemos oportunidade de usar o
Apostila de C++ Builder
Pagina -92-
comando if ... else sem, contudo, entrar em detalhes sobre o funcionamento do mesmo, exceto por alguma
rápida explicação, até mesmo em comentários, que já devem ter lhe dado uma pequena noção acerca do
mesmo. Essa é justamente nossa intenção na utilização antecipada dessas instruções. Você vai tendo
contato com os comandos e, aos poucos, assimilando sua utilidade e forma de funcionamento. Dessa
forma, o estudo deve ser facilitado no momento que tratamos diretamente do assunto, pois, ainda que não
se lembre das minúcias, o comando não será algo totalmente estranho para você.
Dentro de cada bloco de códigos, o fluxo de execução de um programa faz com que as linhas sejam
executadas na ordem em que aparecem. Por exemplo:
Edit1 -> Text == "";
Edit1 -> SetFocus();
Nestas duas linhas acima, primeiro será esvaziado o Text de Edit1 que depois receberá o foco.
Todavia, é bastante comum um programa precisar quebrar esse fluxo na execução, seguindo por um
caminho diferente, deixando de executar determinadas instruções em resposta a certas condições. O
comando if permite testar uma condição (por exemplo, se o valor de uma variável booleana é verdadeiro true) e seguir para uma ou outra parte do código em virtude do resultado desse teste.
Podemos resumir o uso do if da seguinte forma:
if(expressão)
comando;
É mais comum que a expressão avaliada pelo comando if seja do tipo relacional, mas, em princípio, pode
ser de qualquer tipo. Feita a avaliação pelo comando, se o resultado da expressão for zero, ela é
considerada falsa, e o comando não será executado. Se o valor da expressão for diferente de zero, ela é
considerada verdadeira, e o comando será executado.
No exemplo:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(Edit1 -> Text == "")
Edit1 -> SetFocus();
}
Quando o usuário der um clique no botão, o foco ira para Edit1 somente se Edit1 estiver vazio.
Também podemos determinar um bloco de comandos entre chaves { }:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(Edit1 -> Text == "")
{
Edit1 -> Text = "Primeiro Comando deste bloco";
Label1 -> Caption = "Segundo Comando deste bloco";
Apostila de C++ Builder
Pagina -93-
Memo1
->
Lines
->
Add("Terceiro
Comando
e
último
comando
deste
bloco");
}
}
O exemplo a seguir usa um ComboBox, um Label e um Button no Form. Quando o usuário dá um clique
no botão, IndexOf() faz uma busca pelo item que contém a string Teste no ComboBox. Se o item é
encontrado, o valor numérico do item é atribuído à variável que passará a ter um valor igual ou maior do
que zero (zero corresponde ao primeiro item da relação no ComboBox, 1 ao segundo e assim por diante).
Feito isso, o programa chama Delete() para apagar o item correspondente à busca. Consecutivamente, o
programa informa o valor da variável no Label.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
/* Adiciona cabeçalho (texto) e items no
ComboBox na inicialização do programa*/
ComboBox1 -> Text = "Fatos e Testes";
ComboBox1 -> Items -> Add("Hoje é 22 de julho de 2002");
ComboBox1 -> Items -> Add("Teste");
ComboBox1 -> Items -> Add("Está fazendo um pouco de frio");
ComboBox1 -> Items -> Add("A Marta é professora");
ComboBox1 -> Items -> Add("Ela está trabalhando no Jd. Álamo");
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
/* declara e inicializa variável iBusc com o número devolvido
por IndexOf() para o item que contém Teste*/
int iBusc = ComboBox1->Items->IndexOf("Teste");
// se a a busca tiver êxito, a variável possui valor diferente de -1
if (iBusc > -1)
// chama Delete() para apagar o item buscado
ComboBox1->Items->Delete(iBusc);
}
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -94-
O comando else
Pode ocorrer de um aplicativo ter de executar uma instrução se determinada condição for verdadeira, e
outra instrução se tal condição for falsa. Um recurso oferecido por C++ para essas situação é a
combinação if ... else.
A forma do uso de if ... else pode ser resumido da seguinte forma:
if(expressão)
comando;
else
outro_comando;
O Exemplo a seguir usa um Edit, um Label e um Button no Form. Quando o usuário dá um clique no
botão, o programa fará uma busca pelo arquivo, cujo caminho estiver especificado no Edit. O resultado da
busca será informado no Label.
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
/* Declara e inicializa variável BuscArquiv com o resultado da busca
por um arquivo, cujo caminho completo é especificado em Edit1 */
AnsiString BuscArquiv = FileSearch(Edit1->Text, GetCurrentDir());
// Se BuscArquiv estivar vazia (pelo fato de a busca fracassar)
if (BuscArquiv == "")
// O Label informará que não encontrou o arquivo
Label1 -> Caption = "Não foi possível encontrar " + Edit1->Text + ".";
// caso contrário (Se houver algum dado em BuscArquiv)
else
// Label informará que encontrou o arquivo
Label1 -> Caption = "Encontrado " + BuscArquiv + ".";
}
//---------------------------------------------------------------------------
if ...else - Continuação
O comando if ... else comporta em seu interior quaisquer outros comandos, incluindo combinações else if,
if ... else:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(StrToInt(Edit1 -> Text) >= StrToInt(Edit2 -> Text))
{
if((StrToInt(Edit1 -> Text) % StrToInt(Edit2 -> Text)) == 0)
{
if(StrToInt(Edit1 -> Text) == StrToInt(Edit2 -> Text))
Apostila de C++ Builder
Pagina -95-
Label1
->
Caption
=
"O
número
de
Edit1
é
igual
ao
de
Edit2!!!";
else
Label1 -> Caption = "O número de Edit1 é multiplo do de
Edit2";
}
else
Label1 -> Caption = "O número de Edit1 não é múltiplo do de Edit2";
}
else
Label1 -> Caption = "O número de Edit2 é maior que o de Edit1";
}
//---------------------------------------------------------------------------
Nota: O comando else nem sempre estará associado ao comando if mais recente. Um comando if contido
com todas as suas instruções num bloco de chaves {} deverá remeter o else (fora desse bloco) para o if
antecedente. Um bloco de código, em virtude de "pequenas" alterações, poderá apresentar resultados
totalmente diferentes. Por exemplo:
//----------------------------------------------------------------------------int x = 45, y = 200, z = 10;
if (x == 1)
{
// início do bloco
if (y == 200)
z = y - x;
}
//fim do bloco sem else
else // a instrução desse comando será aplicada se x for diferente de 1
z = y + x;
Label1->Caption = z; /*Resultado: z = 245, pois o comando else, em virtude das chaves {},
está relacionado com o primeiro if, cuja condição é falsa, porque x é diferente de 1 */
//-----------------------------------------------------------------------------int x = 45, y = 200, z = 10;
if (x == 1); //o ponto e vírgula faz a diferença. Não existe instrução para essa condição
if (y == 200)
z = y - x; // Ok. y é igual a 200
else
z = y + x; // essa instrução seria aplicada se y fosse diferente de 200
Label1->Caption = z; /*Resultado: z = 155, porque o primeiro if não é levado em
consideração em virtude do ponto e vírgula que deixa a instrução deste comando vazia */
//-----------------------------------------------------------------------------int x = 45, y = 200, z = 10;
if (x == 1)
if (y == 200)
z = y - x;
else
z = y + x;
Label1->Caption = z; /*Resultado: z = 10, pois o programa avalia as duas condições e não
encontra uma opção (um segundo else) para o fato de a primeira condição ser falsa */
//------------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -96-
int x = 45, y = 200, z = 10;
if (x == 1)
if (y == 200)
z = y - x;
else
z = y + x;
else
z = 300;
Label1 -> Caption = z; /* Resultado: z = 300, pois esse segundo else vai de encontro
com o fato de a primeira condição ser falsa */
//-----------------------------------------------------------------------------int x = 45, y = 200, z = 10;
if (x == 45)
if (y == 200)
z = y - x;
else
z = y + x;
Label1 -> Caption = z; //
verdadeiras.
Resultado:
z
=
155,
porque
as
duas
condições
são
//------------------------------------------------------------------------------
Comandos aninhados e Indentação
Já tivemos oportunidade de mostrar a grande importância do correto uso das chaves {} nos comandos if ...
else. Dito isso, é bom realçarmos que esses comandos podem ser aninhados tantas vezes quantas forem
necessárias, ou seja, um if ... else pode conter outro if ... else, que pode conter outro if ... else etc, etc, etc.
Esse tipo de codificação pode se tornar difícil de escrever e interpretar, induzindo a erros. Daí a
necessidade do adequado uso das chaves {}, bem como de recursos que facilitem a escrita como a
indentação do código.
Promover à indentação do código significa indicar os níveis de aninhamento de suas diversas partes,
afastando-se gradualmente (através de espaços ou tabulações) as linhas (ou blocos) de código da margem
da página:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(Edit1->Text == "Olá")
Label1->Caption = "Olá";
else if(Edit1->Text == "Pessoal")
Label1->Caption = "Pessoal";
else if(Edit1->Text == "da cidade")
Label1->Caption = "da cidade";
else if(Edit1->Text == "de Assis")
Label1->Caption = "de Assis";
else
Label1->Caption = "Olá pessoal de Assis!!!";
Apostila de C++ Builder
Pagina -97-
}
//--------------------------------------------------------------------------Conforme o caso, podemos tornar mais legíveis as construções aninhadas if ... else,
sem o recurso da identação:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if(Edit1->Text == "Olá")
Label1->Caption = "Olá";
else if(Edit1->Text == "Pessoal")
Label1->Caption = "Pessoal";
else if(Edit1->Text == "da cidade")
Label1->Caption = "da cidade";
else if(Edit1->Text == "de Assis")
Label1->Caption = "de Assis";
else
Label1->Caption = "Olá pessoal de Assis!!!";
}//---------------------------------------------------------------------------
Operadores lógicos
Pode ocorrer de um comando precisar receber mais de uma resposta para tomar uma decisão.
A linguagem C++ oferece três operadores lógicos que são:
&&
significa: E
||
significa: OU
!
significa: NÃO
A sintaxe é:
expressão_1 && expressão_2
resulta verdadeiro somente se ambas as expressões forem verdadeiras;
expressão_1 || expressão_2
se pelo menos uma das expressões for verdadeira, o resultado será verdadeiro;
! expressão
o resultado será verdadeiro somente se a expressão for falsa.
Como os dois primeiros operadores avaliam duas expressões, são classificados como binários; já o último,
unário.
Nota: O tipo TShiftState é usado para eventos de teclado e eventos do mouse para determinar o estado das teclas
Alt, Ctrl e Shift, bem como dos botões do mouse, quando ocorrer um evento. Trata-se de um grupo de flags que
indica o seguinte:
Apostila de C++ Builder
Pagina -98-
ssShift
A tecla Shift está segura em baixo.
ssAlt
A tecla Alt está segura em baixo.
ssCtrl
A tecla Ctrl está segura em baixo.
ssLeft
O botão esquerdo do mouse está seguro em baixo.
ssMiddle
O botão do meio do mouse está seguro em baixo..
ssDouble
O mouse recebeu um duplo clique.
O exemplo a seguir leva um label no Form. Conforme o usuário produzir um dos eventos acima, uma
resposta diferente será dada pelo programa:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if(Shift.Contains(ssCtrl) && Shift.Contains(ssShift))
Label1->Caption = "Tecla Ctrl \"e\" tecla Shift pressionadas";
if(!Shift.Contains(ssAlt) && !Shift.Contains(ssCtrl) && !Shift.Contains(ssShift))
ShowMessage("A tecla pressionada não é a tecla Alt, Shift ou Ctrl.");
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if(Shift.Contains(ssDouble) || Shift.Contains(ssLeft))
Label1->Caption = "Evento do mouse detectado: Duplo Clique \"ou\" botão esquerdo.";
}
//---------------------------------------------------------------------------
O operador condicional ternário
C++ possui o operador condicional ternário ? : que nos formece uma maneira concisa de expressar,
simplificadamente, uma instrução if ... else:
Apostila de C++ Builder
Pagina -99-
condição? expressão_1 : expressão_2;
É feita uma avaliação da condição. Se ela for verdadeira, o programa retornará a expressão_1; se falsa,
a expressão_2. A nomenclatura ternário decorre do fato de o operador manipular três termos. Embora o
operador condicional ternário seja constituído de dois caracteres (? :), trata-se de um único operador.
O exemplo a seguir leva um Edit no Form e é usado para permitir a edição de valores numéricos inteiros:
//--------------------------------------------------------------------------void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
isdigit(Key)? Key<48 || Key>57 : Key = 0;
}
//---------------------------------------------------------------------------
Funções
Todos os aplicativos escritos em linguagem C ou C++ iniciam a execução do programa através de uma
função principal. Nas aplicações tipo Console, essa função denomina-se main. Aplicações Win32 GUI
chamam WinMain para iniciar a execução:
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow
);
//
//
//
//
manipulador da instancia atual
manipulador da instancia prévia
ponteiro à línha de comando
estado de visualização da janela
A função WinMain tem quatro parâmetros de entrada:
hInstance
é um manipulador para a instância do programa que estamos executando.
Cada vez que se executa uma aplicação, o Windows cria uma Instância
para ela, e lhe passa um manipulador de dita instância para a aplicação
hPrevInstance
é um manipulador a instâncias prévias da mesma aplicação. Como o
Windows é multitarefa, podem existir várias versões da mesma aplicação
executando-se varias instâncias. Em Windows 3.1, este parâmetro nos
servia para saber se nossa aplicação já estava sendo executada, e desse
modo podiam compartilhar os dados comuns a todas as instâncias. Mas
isso era antes, já que em Win32 usa um segmento distinto para cada
instância e este parâmetro é sempre NULL, sendo conservado apenas por
motivo de compatibilidade
lpszCmdLine
ponteiro a uma cadeia (terminada em zero) que especifica os argumentos
Apostila de C++ Builder
Pagina -100-
hInstance
é um manipulador para a instância do programa que estamos executando.
Cada vez que se executa uma aplicação, o Windows cria uma Instância
para ela, e lhe passa um manipulador de dita instância para a aplicação
de entrada da linha de comando da aplicação
nCmdShow
este parâmetro especifica como se mostrará a janela
Não entraremos em detalhes sobre essa função, visto que tal assunto está reservado para outro curso, sobre
WinAPI, que em breve esperamos estar disponibilizando neste Sítio:
Eis um arquivo básico de um Project.cpp do C++Builder, onde podemos visualizar a função principal:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
USERES("Project2.res");
USEFORM("Unit1.cpp", Form1);
//--------------------------------------------------------------------------WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
//---------------------------------------------------------------------------
A função acima encontra-se num arquivo em separado daqueles que estamos acostumados a trabalhar.
Via de regra, em C++, é irrelevante se a função inicial está colocada no início ou no fim do arquivo, pois o
programa ignorará o fato, executando do mesmo jeito, tanto numa quanto noutra posição. Evidentemente,
no BCB não devemos alterar, sem prévio conhecimento, o conteúdo ou disposição da função WinAPI.
As instruções em C++ aparecem dentro de alguma função, ou seja, de um grupo de comandos que executa
alguma tarefa, sendo que as funções podem conter instruções que chamam outras funções, bem como
retornar algum valor no seu encerramento para a instrução chamadora.
Criamos a função AloCy() no exemplo abaixo. Quando o usuário der um clique no botão colocado no
Form, AloCy() será chamada para imprimir uma mensagem no Label1.
Apostila de C++ Builder
Pagina -101-
//-------------------ilustra o uso de uma função ---------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------void AloCy(); // Protótipo da função
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AloCy(); // Chama a função AloCy()
}
//--------------------------------------------------------------------------// Definição da função AloCy()
void AloCy()
{
Form1 -> Label1 -> Caption = "Alô, Cy!!!! \n"
"Como estão todos aí em Araçatuba???"
"\nEstou com saudades !!!!!";
}
NOTA: Esclarecemos que neste e nos próximos tópicos estaremos trabalhando com funções de uma
maneira que, embora não seja incorreta, não é a convencional para o C++Builder. Basicamente
estaremos preocupados em conhecer os fundamentos das funções em C++, sem nos preocupar se essa é a
forma ideal de se trabalhar com o BCB.
Chamada de Funções
Já tivemos oportunidade de, em tópicos anteriores, verificar que todo programa C++ deve ter uma função
principal denominada main() ou, conforme o caso, WinMain(), não podendo haver outra função com
esse mesmo nome, sendo que a execução do programa sempre será iniciado por essa função. As funções
main() e WinMain() são diferentes das outras funções, visto que estas são chamadas após iniciada e ao
longo da execução do programa.
Evidente que uma função, quando é chamada, pode também chamar a uma outra função:
//-------------------ilustra chamada de funções----------------------------#include <vcl.h>
#pragma hdrstop
Apostila de C++ Builder
Pagina -102-
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------void AloCy(); // Protótipo da função AloCy()
void AloDau(); // Protótipo da função AloDau()
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AloCy(); // Chama a função AloCy()
}
//--------------------------------------------------------------------------// Definição da função AloCy()
void AloCy()
{
Form1 -> Label1 -> Caption = "Alô, Cy!!!! \n"
"Como estão todos aí em Araçatuba???"
"\nEstou com saudades !!!!!";
AloDau(); // Chama AloDau()
return;
}
//-------------------------------------------------------------------------// Definição de AloDau()
void AloDau()
{
// Acrescenta dados no caption de Label1, sem apagar o que já está nele
Form1 -> Label1 -> Caption =
Form1 -> Label1 -> Caption + "\nAlo, Daury!!!!";
return;
}
//-------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
Label1->Caption = ""; // Limpa o caption de Label1
}
//---------------------------------------------------------------------------
Definição de uma função
A definição de uma função se dá por meio de um cabeçalho e de um corpo. O cabeçalho é constituído
pelo tipo retornado, pelo nome da função, bem como pelos parâmetros que a mesma recebe. Os
parâmetros são declarações dos tipos dos valores ou os dados que serão passados para a função, e sobre
os quais a função poderá operar. Os dados ou valores passados são denominados de argumentos. Por
exemplo, se desejássemos que uma função efetue uma operação matemática de divisão de dois números
Apostila de C++ Builder
Pagina -103-
inteiros e nos devolva o resultado que poderia ser valor um ponto flutuante, poderíamos ter o cabeçalho da
definição assim:
float Opera_Divisao( int, int)
Já o corpo da função consiste de uma chave de abertura
{
seguida pelos comandos que executam a tarefa da função, seguidos pela chave de fechamento
}
Por exemplo:
float Opera_Divisao( int Divsr, int Divdnd)
{
float Result = Divsr / Divdnd;
Return Result;
}
O Exemplo abaixo possui um Button, um Edit e um Label. Preste bastante atenção nos comentários, pois
eles explicam os códigos com detalhes:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------/* Protótipo da função com o tipo retornado,
o nome da função e o parâmetro entre parênteses*/
AnsiString Recb_Pass(AnsiString);
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
/* Cria um texto para Edit1 e um caption para Label1 em tempo de execução*/
Edit1 -> Text = "Essa string será passada para"
" a função Recb_Pass() que a imprimirá em Label1";
Label1 -> Caption = "Esta string será devolvida para Edit1";
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Valor_Devolvido; // declara variável para receber valor de Recb_Pass()
Apostila de C++ Builder
Pagina -104-
/* recebe valor devolvido pelo comando return de Recb_Pass()*/
Valor_Devolvido =
/*Texto de Edit1 será passado como parâmetro para Recb_Pass()*/
Recb_Pass (Edit1 -> Text);
/*O valor devolvido por Recb_Pass() será o novo texto de Edit1*/
Edit1 -> Text = Valor_Devolvido;
/*Desabilita Button1 para evitar que valores sejam trocados a cada clique do mouse*/
Button1 -> Enabled = false;
}
//--------------------------------------------------------------------------//Definição de Recb_Pass()
//Recb_Pass() devolverá um valor tipo AnsiString
//Recb_Pass() receberá AnsiString de Edit1->Text
AnsiString Recb_Pass(AnsiString Valor_Recebido)
{
/*Antes de alterar o caption de Label1, acumula o valor atual na variável
Valor_a_Devolver para retorná-lo via comando return*/
AnsiString Valor_a_Devolver = Form1 -> Label1 -> Caption;
//Coloca o valor recebido no Caption de Label
Form1 -> Label1 -> Caption = Valor_Recebido;
//Devolve o valor acumulado do caption de Label1
return Valor_a_Devolver;
}
Protótipos de funções
Não podemos usar uma função sem declará-la previamente. Trata-se duma instrução geralmente colocada
no início do programa ou do arquivo, obrigatoriamente antecedendo a definição e a chamada da função.
O protótipo informa ao compilador o tipo que a função retorna, o nome da função, bem como os
parâmetros que ela recebe. Eis um exemplo:
double diametro (int raio);
Esse protótipo está declarando uma função chamada diametro, que recebe um valor inteiro (a declaração
int dentro dos parênteses: diametro(int raio)) e devolve um valor ponto flutuante (o double que
antecede o nome da função: double diâmetro()). Poderiam ser quaisquer outros tipos.
O protótipo e a definição da função devem conter o mesmo nome, o mesmo tipo retornado e a mesma lista
de parâmetros:
double diametro(int raio); // Protótipo da função: com ponto e vírgula após os
parênteses
double diametro(int raio) // Definição da função: sem ponto e vírgula
{ // chave de abertura do corpa da função
return raio * 3.14; // única instrução dessa função
} // chave de encerramento da função
Apostila de C++ Builder
Pagina -105-
O nome da variável passada como parâmetro (em nosso exemplo, a inteira raio) é facultativo, mas o tipo é
obrigatório. Quanto ao valor de retorno será um tipo, ou será void (caso não haja valor de retorno):
void diametro(int raio); // Protótipo da função
Se nenhum tipo for especificado como valor de retorno, o compilador retornará o tipo int:
O exemplo:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------Experimenta_Tipos(char raio); // Protótipo da função
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
// o caption de Label1 chama diametro e passa o caracter 'a' como parâmetro
/* IMPORTANTE: em qualquer hipótese, na chamada à função não podemos colocar
o tipo de retorno, pois o compilador retornará mensagem de ERRO*/
Label1 -> Caption = /*sem tipo de retorno*/Experimenta_Tipos('a');
}
//--------------------------------------------------------------------------Experimenta_Tipos(char raio) /* Definição da função sem tipo especificado
para retorno. A função retornará como sendo int */
{
// 'a' equivale a 97 na tabela ASCII.
return raio * 1000.01;
}
terá como valor de retorno: 97000.
Experimente, nesse mesmo exemplo, colocar como valor de retorno o tipo char. Depois faça o mesmo
como o tipo double. Com essas pequenas pequenas alterações, você poderá visualizar os diferentes
comportamentos do compilador.
Além da forma de declaração apresentada (protótipo externo, pois é escrito fora e antes das funções),
existe a possibilidade de declararmos o protótipo dentro do corpo de cada função chamadora (protótipo
interno).
Apostila de C++ Builder
Pagina -106-
Para esses casos, há duas restrições:
1ª. A função é visível apenas dentro do bloco onde estiver o seu protótipo;
2ª. devemos declarar o tipo retornado no protótipo (ou void), para que o compilador não interprete o
protótipo como uma chamada à função e, conseqüentemente, retorne uma mensagem de erro:
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
char Experimenta_Tipos(char raio); // Protótipo da função
Label1 -> Caption = /*sem tipo de retorno*/Experimenta_Tipos('a');
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
char Experimenta_Tipos(char raio); // Protótipo da função
Label2 -> Caption = /*sem tipo de retorno*/Experimenta_Tipos('a');
}
//---------------------------------------------------------------------------
Variáveis locais e globais
Já tivemos oportunidade de trabalhar com variáveis locais e globais sem, contudo, entrar em detalhes
acerca de suas particularidades.
Comecemos pelas variáveis locais. Imaginemos o seguinte bloco de códigos com as respectivas
mensagens de erro na forma de comentários:
void __fastcall TForm1::Button1Click(TObject *Sender)
{ // início do primeiro bloco (bloco externo)
// [C++ Error] Unit1.cpp(20): E2451 Undefined symbol 'a'.
Label1->Caption = a;
{ // início do segundo bloco (bloco interno)
int a = 10, b = 20;
Label2->Caption = a + b; // Ok. Label2 enxerga as variáveis "a" e "b"
// [C++ Error] Unit1.cpp(24): E2451 Undefined symbol 'c'.
Label3->Caption = a + b + c; // Erro. Label3 não enxerga "c"
// [C++ Error] Unit1.cpp(25): E2238 Multiple declaration for 'c'.
int c = 30;
} // fim do bloco interno
//[C++ Error] Unit1.cpp(27): E2451 Undefined symbol 'c'.
Apostila de C++ Builder
Pagina -107-
Label4->Caption = c;
} // fim do bloco externo
Analisemos o código. No exemplo acima visualizamos dois blocos de código: um externo e outro interno.
1 - O componente Label1 não enxerga a variável porque sua chamada encontra-se antes e fora do bloco da
declaração de a;
2 - não temos qualquer problema com o componente Label2 porque sua chamada se encontra no mesmo
bloco e após as declarações das variáveis a e b;
3 - Embora a chamada a Label3 se encontre no mesmo bloco, é anterior à declaração da variável c.
4 - Label4 não enxerga a variável c pelo fato de estar fora do bloco onde tal variável é declarada.
Conclusão: podemos entender como locais as variáveis que se encontram declaradas dentro de
determinado bloco, com a ressalva de que essas variáveis são visíveis apenas dentro desse bloco e a partir
do local de sua declaração:
Já as variáveis globais devem ser declaradas antes e fora de qualquer função, uma vez que elas serão
visíveis somente pelas instruções posteriores à sua declaração. Logo se você quiser que uma variável seja
"vista" por todas as funções de uma determinada Unit, pode declará-la na região dos include. A seguir
ilustramos um exemplo que demonstra a o relacionamento existente entre as variáveis globais e locais:
//--------------------------------------------------------------------------// Unit1.cpp
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------int global_1 = 50; // declara e inicializa variável global
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
/*o código abaixo apresentará um erro, pois
Button1Click não enxerga global_2 */
Apostila de C++ Builder
Pagina -108-
Label1->Caption = global_1 + global_2;
}
//--------------------------------------------------------------------------int global_2; //variável que será visível somente pelas funções abaixo dela
void __fastcall TForm1::Button2Click(TObject *Sender)
{
global_2 = 45; // aqui essa variável adquire característica de global
int local_de_Button2Click = 12; // variável local
// não haverá erro
Label1->Caption = global_1 + global_2 + local_de_Button2Click;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button3Click(TObject *Sender)
{
// ERRO: local_de_Button2Click não pode ser visualizada
// pelo evento OnClick de Button3. ELA É LOCAL A Button2Click
Label1->Caption = global_1 + global_2 + local_de_Button2Click;
}
//---------------------------------------------------------------------------
Muito importante: as variáveis locais são criadas pelo aplicativo cada vez que sua declaração é
encontrada pela execução do programa e são destruídas cada vez que a execução sai do bloco em que elas
se encontram; já as variáveis globais são criadas uma única vez no início do programa e destruídas uma
única vez no encerramento do programa.
O resultado da observação acima é que, cada vez que uma variável local for destruída, se ela for criada
novamente, não terá conservado o valor que possuía no momento em que foi destruída. Será inicializada
novamente com o valor que lhe for atribuído no momento apropriado.
Quanto às variáveis globais, seus valores vão sendo alterados durante a execução do programa, sendo que
cada vez que tal variável for chamada trará em si o valor resultante da última operação em que foi tratada.
A palavra-chave extern
As palavras-chaves extern e static são recursos oferecidos pela linguagem C++ para alterar o
comportamento dos conceitos local e global, abordados no tópico anterior.
A palavra-chave extern pode ser usada para que um arquivo fonte possa usar uma variável global que se
encontra definida em outro arquivo fonte.
Apostila de C++ Builder
Pagina -109-
O exemplo a seguir trabalha com dois Forms. O primeiro leva um Label e três Buttons. O segundo leva
um Label e um Button. Declaramos duas variáveis inteiras globais no primeiro formulário (Form1), as
inicializamos e alteramos seus valores conforme o componente que recebe um clique, exibindo o resultado
em Label1. No segundo formulário (Form2), através da palavra-chave extern, manipulamos e exibimos o
resultado, conforme o componente que recebe o clique do mouse:
Unit1.h
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------int a, b;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
a = 1;
b = 2;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
a = 100;
b = 200;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
Label1->Caption = a + b;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button3Click(TObject *Sender)
{
Form2->ShowModal();
}
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -110-
Unit2.h
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
//--------------------------------------------------------------------------extern int a;
extern int b;
//--------------------------------------------------------------------------__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm2::Label1Click(TObject *Sender)
{
Label1->Caption = a + b;
}
//--------------------------------------------------------------------------void __fastcall TForm2::Button1Click(TObject *Sender)
{
a = 1000;
b = 2000;
}
//---------------------------------------------------------------------------
O recurso extern é um modo de trabalhar uma mesma variável em mais de um arquivo, guardando sempre
o último valor atribuído à variável, não importando em qual arquivo se tenha dado tal atribuição.
Em C++ , extern não é mais tão necessário por causa do conceito de namespace, o qual é mais facilmente
gerenciado em sistemas grandes. Futuramente abordaremos esse conceito.
A palavra-chave static
As palavra-chave static, usada junto a variáveis, possui uma amplitude maior, visto que possui três
significados distintos. Vejamos cada um deles:
1 - Dentro de um bloco:
Apostila de C++ Builder
Pagina -111-
Como sabemos, as variáveis declaradas dentro de um bloco, só existem durante a execução do bloco.
Quando o bloco acaba, elas são destruídas. Exemplo:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
int numero = 16;
numero+= numero;
Label1->Caption = numero;
}
O exemplo acima sempre exibirá o número 32 no label. Isto acontece porque cada vez que a função é
chamada, a variável numero é criada com o valor dezesseis, e quando a função acaba, ela é destruída. Se
alterarmos a forma de declarar a variável, inserindo a palavra-chave static:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
static int numero = 16;
numero+= numero;
Label1->Caption = numero;
}
tudo muda de figura. Declarada deste modo, a variável numero é criada uma única vez, e não é destruída
até o encerramento do programa. Note que ela não poderá ser usada fora do bloco onde foi declarada, mas,
exceto por esse fato, essa variável comporta-se exatamente como uma variável global. Deste modo, na
primeira vez que chamamos Label1Click(), o label exibe 32; na segunda, 64; na terceira, 128; e assim
por diante... a cada nova chamada, a variável numero já está lá, com o valor deixado na última chamada.
2 - Em variável global.
Em uma variável global, a palavra static esconde a variável de acessos através de declarações extern.
Assim se definimos a variável global numero como:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------static int numero = 10;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
Apostila de C++ Builder
Pagina -112-
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
numero+= numero;
Label1->Caption = numero;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form2->ShowModal();
}
//---------------------------------------------------------------------------
esta variável global numero só poderá ser usada dentro do fonte em que foi definida.
Uma outra unidade como essa a seguir:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
//--------------------------------------------------------------------------extern int numero;
//--------------------------------------------------------------------------__fastcall TForm2::TForm2(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm2::Label1Click(TObject *Sender)
{
numero+= numero;
Label1->Caption = numero;
}
//---------------------------------------------------------------------------
não apresentará o resultado esperado para extern int numero, provavelmente apresentando alguma
mensagem
de
erro:
[Linker Error] Unresolved external '_numero' referenced from C:\ARQUIVOS DE PROGRAMAS\BORLAND\CBUILDER4\PROJECTS\UNIT2.OBJ.
Esse resultado pode ser desejado para reduzir a interferências entre equipes de programação.
3) Em uma definição de classe
Apostila de C++ Builder
Pagina -113-
Em uma definição de classe, a palavra static indica que um dado membro é compartilhado por todas as
instâncias da classe. Esse uso para static em C++ será abordado no momento oportuno, quando
estivermos estudando as classes.
Parâmetros das funções
passando argumentos por valor
Podemos, de modo errado, supor que toda vez que passamos valores na forma de argumentos para outras
funções, estas, ao manipular esses dados, estariam alterando o valor original das mesmas. Observe o
exemplo, onde criamos uma função para manipular os dados recebidos de ButtonClick():
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------void Impr_Label_1(int a, int b); // Protótipo da função
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
int a = 10, b = 40; // declara e inicializa duas inteiras
/* imprime os valores de a e b em Label1*/
Label1 -> Caption = "Valor de a = " + String(a) + "\n" +
"Valor de b = " + String(b) + "\n";
Impr_Label_1(a, b); // chama a função Impr_Label_1 pasando argumentos por valor
/* após o comando retornar para Button1Click(), imprime novamente o valor local
de a e b no Label, adicionando esta impressão aos valores que já estavem no Label*/
Label1 -> Caption = Label1 -> Caption + "\n" +
"Valor de a após a chamada a Impr_Label_1() = "
+ String(a) + "\n" +
"Valor de b após a chamada a Impr_Label_1() = "
+ String(b) + "\n";
Apostila de C++ Builder
Pagina -114-
}
//--------------------------------------------------------------------------void Impr_Label_1(int a, int b)
{
/* A função Impr_Label_1() recebe dois valores e
cria cópias dessas variáveis para operar */
a = 100; // altera o valor da variável a (cópia) para 100
b = 400; // altera o valor da variável b (cópia) para 400
/*Imprime o valor das variáveis originais e das cópias no label*/
Form1 -> Label1 -> Caption = Form1 -> Label1 -> Caption + "\n" +
"Valor de a em Impr_Label_1() = " + String(a) +
"Valor de b em Impr_Label_1() = " + String(b) +
"\n" +
"\n";
return; // não há valores a retornar
}
Na execução deste programa. ocorre o seguinte: Quando o usuário dá um clique no botão, o valor das
variáveis a e b são imprimidos no Label e é chamada a função Impr_Label_1(a, b) que recebe os
valores de a e b como argumentos. Impr_Label_1() cria uma cópia para cada argumento que lhe foi
passado e opera sobre essas cópias, alterando e imprimindo os novos valores no Label. Quando a função
retorna o controle para a função chamadora, todas as cópias criadas em Impr_Label_1(a, b) são
destruídas e, então, ButtonClick() imprime novamente no Label os valores das variáveis a e b que, nessa
função, permanecem inalterados.
Passar argumentos dessa forma, onde a função cria cópias dos valores transmitidos, dá-se o nome de:
passar argumentos por valor.
Podemos, então, concluir que os valores passados dessa forma a uma função funcionam como variáveis
locais a essa função.
O comando return
Quando tivermos que retornar um valor de alguma função, devemos fazê-lo através do comando return,
seguido do valor a ser retornado:
return altura * largura;
return 412;
Podemos retornar uma expressão, cujo valor deve ser compatível com o valor retornado por return:
return(ImprLabel_1(int, int));
Apostila de C++ Builder
Pagina -115-
return (i <= 100);
Se não houver valores a retornar, usamos a palavra void indicando a circunstância:
void Impr_Diam(int raio); // protótipo
O comando return funciona como uma porta de saída da função. Assim que é executado, a expressão que
o segue é retornada, sendo desconsideradas todas as instruções posteriores. Contudo, o fato de um
comando return ser encontrado, não significa necessariamente que o mesmo será executado, pois poderá
haver um desvio na linha de execução do código. Daí, podemos ter um ou mais comandos return numa
função:
if(a > b) return a;
else return b;
Exemplo:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------int Form_Capt(int, int); // protótipo - recebe dois inteiros e devolve um
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
int a = 10, b = 40; //declara e inicializa duas int que serão passadas como
argumentos da função
Form1->Caption = Form_Capt(a, b); // Chama Form_Capt(), passando a e b como
argumentos
}
//--------------------------------------------------------------------------int Form_Capt(int c, int d)
{
if(c > d)
return c * d;
// retorna c vezes d se c for maior que d
else if(c == d)
return c + d;
// retorna c mais d se c for igual a d
else
return d - c;
// retorna c menos d se c for menor que d
}
Apostila de C++ Builder
Pagina -116-
Valores Default
Conforme vimos, na chamada de uma função existe a necessidade de fornecermos um argumento que
corresponda a cada parâmetro declarado no protótipo e na definição da função, respeitando-se os tipos que
aparecem no protótipo:
/*double*/ conta (/*int*/ a, /*double*/ b);
Podemos, contudo, estabelecer um valor defaut (padrão) para um ou mais parâmetros:
/*protótipo*/ double conta ( int raio, double Pi = 3.1416);
Em tais situações, se na chamada à função não fornecermos um valor, o default será usado:
conta ( raio); // chamada à função
Se, porém, fornecermos um valor, o default não será usado:
double Pi = 3.14;
conta (raio, Pi); // chamada à função
Não há limites para a quantia de parâmetros com valores default. A única exigência é que os parâmetros
com valores default devem ser, sempre, os últimos na lista de parâmetros de uma função.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------/*protótipo*/ double conta ( int raio, double Pi = 3.1416);
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
double diametro = conta(6);
ShowMessage((String)"O diâmetro com o default é: " + diametro);
}
Apostila de C++ Builder
Pagina -117-
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
double diametro = conta(6, 3.14);
ShowMessage((String)"O diâmetro sem o default é: " + diametro);
}
//--------------------------------------------------------------------------/*definição*/ double conta ( int i, double Pi)
{
return 2*Pi*i;
}
Funções inline
Quando uma função é chamada, a execução do programa salta para o grupo de instruções constantes dessa
função na memória. Encerrada a função, a execução do programa retorna para a linha de código seguinte
àquela que chamou a função.
Esse trabalho de saltar para o conjunto de instruções onde se encontra definida a função e depois retornar
para a linha de código seguinte à instrução chamadora à função denota um trabalho que ocupa certo
espaço em memória, acarretando determinada quantidade de tempo para ser executada.
Para ganhar um pouco de tempo, podemos colocar a palavra-chave inline como primeira palavra do
cabeçalho da definição de uma função, para que seja inserida uma cópia da função em todo lugar onde a
mesma é chamada. Contudo esse procedimento só se justifica se a função chamada for muito pequena
(uma ou duas linhas de instruções), pois nesses casos as instruções necessárias à chamada da função
podem ocupar mais espaço na memória do que as instruções do seu próprio código.
Outro cuidado a ser tomado é que a definição (e não o protótipo apenas) da função deve anteceder à sua
primeira chamada.
Não devemos esquecer que se uma função inline for chamada muitas vezes, a mesma quantia de vezes
será realizada uma cópia de seu código no programa, o que poderá significar um aumento considerável no
tamanho do executável.
O exemplo a seguir leva um Label no Form e é usado para devolver o valor da área de um retângulo:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------inline int Area(int a, int b)
Apostila de C++ Builder
Pagina -118-
{
return(a * b);
}
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
int x, y;
x = StrToInt(InputBox("Cálculo da área", "entre com o valor de um lado", ""));
y = StrToInt(InputBox("Cálculo da área", "entre com o valor do outro lado", ""));
Label1->Caption = (String)"Área = " + Area(x, y);
}
//---------------------------------------------------------------------------
O comando goto
A instrução goto é formada por duas partes: o comando goto e um rótulo:
goto Erro;
A instrução goto causa um desvio na execução normal do programa para a instrução seguinte ao rótulo.
Um rótulo é um nome (que respeita a convenção para a nomenclatura de variáveis) que deve ser colocada
em dois locais: imediatamente após a instrução goto e, completada por dois pontos, antecedendo a
instrução para a qual o goto desviará a execução do programa:
Erro:
ShowMessage("Erro... Não é possível efetuar divisão por zero...");
// ...
if(Divid / 0)
goto Erro;
// ...
No exemplo, se o usuário tentar efetuar uma divisão por zero, o comando goto remeterá a execução do
programa para " Erro: " que chamará uma caixa de mensagem.
Existem códigos que possibilitam usar o goto como um laço (loop), mas é desaconselhável, uma vez que
os laços for , while e do... while fazem com muito mais segurança a tarefa.
O loop while
LAÇOS - Algumas vezes um aplicativo precisa executar operações repetitivas. A fim de evitar que um
programador tenha de repetir uma diversidade de vezes um mesmo código, criou-se o conceito de loop
(ou laço).
Apostila de C++ Builder
Pagina -119-
Laços são comandos que fazem com que uma ou mais instruções sejam repetidas enquanto determinada
condição não estiver satisfeita.
A linguagem C++ possui três formas de laços:
for
while
do ... while
Podemos imaginar os laços como uma roda gigante que fica girando até que determinada condição se
verifique. E a cada giro da roda, determinada tarefa é realizada. A ocorrência da última tarefa dar-se-ia
com a satisfação da condição.
Quando não possuímos prévio conhecimento acerca do número de vezes que o corpo de um loop deverá
ser executado, o while se mostra como opção mais correta, que, basicamente, fará o seguinte: testará uma
condição definida inicialmente; enquanto o comando verificar que a condição continua verdadeira, o
conjunto de instruções contido no corpo do laço continuará sendo executado. No instante em que o
comando verificar um valor de retorno falso para a condição, o processamento será desviado para fora do
laço.
Voltando ao exemplo da roda gigante, no while não haveria a necessidade de uma catraca contando o
número de giros, mas um funcionário aguardando a ocorrência de determinado fato para desligar o
aparelho, como, por exemplo, uma pessoa pedir para descer do aparelho.
Se a condição avaliada for falsa logo no início do loop, o conjunto de instruções será ignorado e o laço
não será executado.
Eis a forma do laço while:
while(condição)
corpo_do_loop;
O exemplo a seguir usa um Label no Form. Quando o usuário dá um clique no label, inicia-se um loop
onde o comando while irá procurar nove caracteres z, ou Z, em quantas tabelas ASCII forem necessárias,
sendo que essas tabelas serão imprimidas no label. Depois informará o resultado da busca no próprio
label.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
Apostila de C++ Builder
Pagina -120-
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Label1->Caption =
"Dê um clique em mim para iniciar um loop while "
"\nonde serão montadas sucessivas tabelas ASCII "
"\naté encontrar as letras 'z' ou 'Z' nove vezes!";
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
Label1 -> Caption = ""; // Apaga o Caption de Label1
// Declara e inicializa três variáveis para ser incrementadas no loop
int contador = 0;
// variável que contará o número de loops
int paraZ = 0;
// variável que contará ocorrências de z e Z
unsigned char caract = 0;
// variável usada para montar as tabelas ASCII
while( paraZ < 9) // enquanto a variável paraZ form menor do que 9
{
contador++;
// incrementa contador a cada giro do loop
caract++;
// incrementa caract a cada giro do loop
/* A tabela ASCII vai sendo construída no Caption de Label1 */
Label1->Caption = Label1->Caption + char(caract);
/* Um comando de decisão switch ... case dentro do corpo do loop*/
switch(caract) /* o comando switch ... case avaliará os valores de caract*/
{
/*caso o valor de caract seja 100, 200 ou 256, a tabela ASCII continuará
sendo imprimida na linha de baixo do Label1*/
case 100:
Label1->Caption = Label1->Caption + '\n';
break;
/* o break remete o processamento para fora do comando switch*/
case 200:
Label1->Caption = Label1->Caption + '\n';
break;
case 256:
Label1->Caption = Label1->Caption + '\n';
break;
/*caso o valor de caract seja Z ou z, a variável paraZ sofre
um incremento e a execução do programa sai do switch*/
case 'Z':
paraZ++;
break;
case 'z':
paraZ++;
break;
}
// fim do comando switch ... case
}
// fim do loop while
/*o label usará as variáveis paraZ e contador para informar os resultados da busca*/
Label1->Caption =
Label1->Caption + "\n\nEncontrei os caracteres "
"\"Z\" e \"z\" " + String(paraZ) + " vezes!";
Label1->Caption =
Label1->Caption + "\n\nPara encontrar esses caracteres, "
"o loop foi executado " + String(contador) + " vezes";
}
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -121-
O comando goto pode fazer com que a execução do programa pule para qualquer ponto visível do código,
e seu uso incorreto ou indiscriminado facilita a introdução de erros. Via de regra, os programadores
evitam usar esse comando, mas existem ocasiões em que o mesmo poderá ser útil.
break e continue
Existem dois comandos que podemos usar no loop while. O comando continue causa uma interrupção na
execução das instruções, remetendo a execução para o topo do laço; já o comando break determina a
imediata saída do laço, independentemente de a condição ter sido satisfeita. Ao encontrar a instrução
break, a execução do programa é enviada para primeira instrução após a chave de fechamento } do corpo
do laço.
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
int i = 1;
while(i <= 1000)
{
ShowMessage((String)"i é igual a " + i++);
if(i <= 5)
continue;
ShowMessage("Agora o corpo todo do laço será executado, até i ser igual a
10");
if(i == 10)
{
ShowMessage("Agora i é igual a 10 e, embora i ainda não seja 1000, "
"vou embora... Adeus...");
break;
}
}
ShowMessage("Se você está vendo esta mensagem, "
"significa que estamos fora do while");
Close();
}
//---------------------------------------------------------------------------
O loop while sem fim
Podemos inserir uma condição permanentemente verdadeira (igual a 1) para ser testada por um laço
while. Nessas condições, o while será executado indeterminadamente, num loop sem fim. Essas situações
carecem de um comando break satisfazendo uma condição de encerramento em algum ponto do laço,
para evitar o travamento da máquina.
Apostila de C++ Builder
Pagina -122-
É preciso tomar um cuidado especial sempre que se utilizar laços nos programas, pois qualquer erro de
lógica ou de condição de encerramento poderá fazer com que o programa entre em um loop sem fim.
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
int i = 0;
while(1) // também poderia ser: while(true)
{
ShowMessage((String)"Mensagem número: "+ ++i);
if(i == 10)
break;
}
}
//--------------------------------------------------------------------------
loop while - continuação
A condição avaliada num loop pode ser qualquer expressão válida em C++, incluindo
expressões construídas com o uso de operadores lógicos &&, ||, ! :
Uma das instruções que compõem um bloco de códigos do corpo de um loop while pode ser
outro laço while. A essa situação denominamos laços while aninhados.
O exemplo a seguir leva um Button e dois Labels no Form. Quando o usuário der um clique no
Button, uma sequência de loops será executada, mostrando o resultado final nos Labels:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1->Caption = "";
Label2->Caption = "";
bool bl = true; // variável que nos dará uma das formas de encerrar o loop
Apostila de C++ Builder
Pagina -123-
int i = 0; // variável para avaliação no primeiro loop
int j2 = 0; /* variável que contará o total de
vezes que o segundo loop executará */
/*enquanto i for menor ou igual a 10 e bl for verdadeira*/
while(i <= 10 && bl == true)
{
i++; // incrementa i (i = i + 1)
/*Label1 exibirá o número de vezes que o primeiro loop rodará*/
Label1->Caption = Label1-> Caption + " " + i;
int j = 10; // variável para avaliação no segundo loop
while(j >= 0) /*enquanto j for maior ou igual a zero*/
{
j--; // decrementa j (j = j - 1)
j2++; // incrementa j2
/*Label1 exibirá o número de vezes que o segundo loop rodará*/
Label2->Caption = Label2->Caption + " " + j2;
}
/*condição para forçar o encerramento do loop
antes de a variável i ser igual a 10*/
if (i >= 4 && j <= 6)
{
bl = false;
}
}
}
//---------------------------------------------------------------------------
O loop do... while
A estrutura do laço do... while é muito parecida com a estrutura do laço while. A diferença básica entre
elas consiste no fato de, enquanto o teste condicional no comando while é realizado no início do loop
(antes das instruções), no laço do... while o teste é realizado no fim do loop (após as instruções).
Dessa forma, o programador garante que pelo menos uma vez as instruções contidas no corpo do laço
serão processadas, mesmo que a condição seja falsa.
eis a sintaxe:
do
{
...
}
Apostila de C++ Builder
Pagina -124-
while(condição)
O exemplo a seguir leva um Button e um Label no Form. Quando o usuário dá um clique no Button, uma
mensagem é exibida no Label e, após sete segundos (7000 milisegundos), o programa é encerrado.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
void __fastcall Esperar(int Miliseg);
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1->Caption = "Aguarde... Após sete segundos o programa será fechado";
Esperar(7000); // tempo que o programa aguardará até ser encerrado
Close(); // encerra o programa
}
//--------------------------------------------------------------------------void __fastcall Esperar (int Miliseg)
{
TDateTime Hora; // declara variável da classe TDateTime
unsigned short Hor, Min, Seg, Miles; // declara vairiáveis
int Tempo;
Hora = Time(); // atribui a hora do sistema à variável Hora
/*todas as instruções abaixo serão executadas ao menos uma vez,
(independente de a condição avaliada ser verdadeira) */
do // início do loop
{
/*DecodeTime() suspende um objeto TdateTime
em horas, minutos, segundos e milisegundos*/
(Time() - Hora).DecodeTime (&Hor, &Min, &Seg, &Miles);
/*incrementa a variável Tempo*/
Tempo = (Min * 60000) + (Seg * 1000) + Miles;
/*interrompe a execução da aplicação possibilitando
ao Windows processar determinadas mensagens*/
Application->ProcessMessages();
}
/* o loop será executado enquanto Tempo for menor que Miliseg */
Apostila de C++ Builder
Pagina -125-
while (Tempo < Miliseg);
}
//---------------------------------------------------------------------------
O loop for
Retornando à idéia de que poderíamos comparar os laços com uma roda-gigante, o laço for funcionaria do
seguinte modo: O aparelho começaria a girar e cada volta seria registrada num contador (ou catraca).
Quando o aparelho atingisse o número de voltas determinado no controlador da catraca (na condição), o
aparelho automaticamente se desligaria.
Isso significa que quando possuímos prévio conhecimento acerca do número de vezes que um loop deverá
ser executado (ver while), a melhor opção será o laço for.
A sintaxe do for é um cabeçalho onde encontramos o valor inicial, a condição e a atualização separados
por ponto e vírgula dentro de um par de parênteses, antecedendo o corpo do laço que contém os
comandos da instrução:
for( valor inicial; condição; atualização)
comando;
Geralmente o valor inicial, também conhecido como inicialização, é uma instrução de atribuição a uma
variável (geralmente inteira), que será executado apenas uma vez no início do loop.
A condição é uma instrução que é avaliada sempre que o loop inicia ou reinicia. Se o valor retornado pela
condição for verdadeiro (ver while), as instruções do corpo do laço são executadas; caso contrário, se
falsa, a execução do programa sai do loop e vai para a instrução seguinte.
A atualização contém a instrução de acordo com que a variável será alterada cada vez que o loop repetir.
Geralmente a variável será incrementada. exemplo:
for(int i = 0; i < 100; i++);
O corpo do loop contém as instruções que serão processadas a cada giro do laço.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include <math.h> // biblioteca para floor
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------/*função para arredondar casas decimais*/
double Arredonda(double valor, int casas_dec);
Apostila de C++ Builder
Pagina -126-
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
/*variável para receber os números inteiros*/
AnsiString a = InputBox("Caixa de Valores", "Digite um Valor", "");
/*variável para guardar o número de casas decimais*/
AnsiString b = InputBox("Caixa de casas decimais", "Digite as casas decimais", "");
/*chama a função que arredonda casas decimais, atribuindo o valor retornado a b*/
b = Arredonda(StrToFloat(a), StrToInt(b));
/*exibe o valor retornado, já com as casas decimais arredondadas*/
ShowMessage(b);
}
//--------------------------------------------------------------------------double Arredonda(double valor, int casas_dec)
{
int i = 1;
/* j será incrementada até o número de casas decimais*/
for(int j = 1; j <= casas_dec; j++)
i*= 10; // o mesmo que: i = i * 10;
// floor é uma função matemática usada para arredondar cifras
return floor(valor * i + 0.5) / i;
}
//---------------------------------------------------------------------------
loop for - continuação
O loop for aceita diversas construções como, por exemplo, conter mais de um valor inicial:
for(int i = 0, j = 100; ...
múltiplas condições:
...; i <= j, j >= i; ...
e processar múltiplas instruções de atualização:
...; i = i + 3, j = j - 7)
Esses múltiplos comandos são separados entre si por vírgulas e das demais instruções por ponto e vírgula.
Apostila de C++ Builder
Pagina -127-
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
Label1->Caption = "";
int i, j, k;
for(i = 0, j
i <=
i++,
// variáveis para o laço for
= 50, k = 100; // inicializa as variáveis
10, j <= 100, k >= 0; // estabelece os limites
j=j*2, k--) // alterações para as variáveis
/******************************
NOTA: O EXEMPLO NOS DEIXA CLARO QUE O LIMITE A SER OBEDECIDO PELO LOOP
SERÁ O LIMITE MAIOR. OU SEJA, EMBORA A VARIÁVEL "i" DEVA SER INCREMENTADA
SOMENTE DE "0" ATÉ "10", ELA CONTINUARÁ SENDO INCREMENTADA ATÉ A VARIÁVEL "k"
SER SATISFEITA PLENAMENTE O SEU DECREMENTO (DE 100 A 0).
QUANTO À VARIÁVEL "k", NÓS PROCURAMOS DEMONSTRAR UMA FORMA DE INTERFERIR
EM SUA VARIAÇÃO DENTRO DO LAÇO, ATRAVÉS DO COMANDO if
******************************/
{
if(j > 900000000) // um novo limite para "j"
j = 53; // "j" agora é igual a 53
// as variações serão demonstradas no Label
Label1-> Caption = (String) Label1->Caption +
" i = " + i + " " + " j = " + j + " " + " k = " + k + '\n';
}
}
//---------------------------------------------------------------------------
Omissão e aninhamento no loop for
Podemos deixar de incluir uma ou mais instruções no cabeçalho do loop for. Caso isso ocorra, não
podemos nos esquecer de colocar o ponto e vírgula respectivo:
for( ; ; )
Apostila de C++ Builder
Pagina -128-
Dependendo da forma que construímos o laço for, o mesmo poderá funcionar como se fosse um loop
while:
//--------------------------------------------------------------------------void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
int i = 1;
for(;i < 10;)
{
ShowMessage((String)"laço for com alguns comandos nulos.
i + "°" + " loop");
i++;
}
}
" +
//---------------------------------------------------------------------------
É possível construir um loop for com todos os comandos nulos:
//--------------------------------------------------------------------------void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
int i = 1;
for(;;)
{
if(i<10)
{
ShowMessage((String)"laço for com todos os comandos nulos. " +
i + "°" + " loop");
i++;
}
else
break;
}
}
//---------------------------------------------------------------------------
Por incrível que pareça, às vezes podemos colocar instruções que estariam no corpo do loop dentro do
cabeçalho. Nessas situações, se o corpo do loop não for usado pelo comando, será necessário colocar-se a
instrução de nulo (um ponto e vírgula) no corpo do laço.
//--------------------------------------------------------------------------void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
for(int i = 1;
i<10;
ShowMessage((String)"laço for com instruções no corpo do laço.
i++ + "°" + " loop"));
Apostila de C++ Builder
Pagina -129-
" +
}
//---------------------------------------------------------------------------
loops for aninhados
Os laços for também podem ser aninhados (um dentro do outro):
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
StringGrid1->ColCount = 10;
StringGrid1->RowCount = 10;
int coluna, linha, celula;
celula = 0;
for (coluna = 0; coluna < StringGrid1->ColCount; coluna++)
{
for (linha = 0; linha < StringGrid1->RowCount; linha++)
StringGrid1->Cells[coluna][linha] = IntToStr(++celula);
}
}
//---------------------------------------------------------------------------
O comando switch
Existem situações onde a execução do programa deverá avaliar uma opção entre várias alternativas. Uma
forma de resolvermos essas situações, seria através de várias estruturas if ... else; outra forma, através
do comando switch() ... case.
Em tais situações, o uso do comando switch() ... case é, de longe, o mais apropriado pois o uso de
vários if ... else pode tornar o código confuso, sujeito a erros e cansativo de depurar.
O comando switch consiste de um cabeçalho e de um corpo. O cabeçalho é montado com a palavrachave switch seguida de uma expressão (que pode ser uma constante ou uma variável) entre parênteses:
Apostila de C++ Builder
Pagina -130-
switch (expressão)
após o parênteses de fechamento, não há ponto e vírgula, seguindo-se imediatamente a abertura do corpo
do comando com a chave {.
O corpo do comando consiste de várias opções rotulando a palavra-chave case, bem como os comandos
para os quais será desviado o controle do programa. Via de regra, essas ações conterão o comando break,
remetendo o processamento do programa para fora do switch (primeira linha de código após o comando).
Sempre que o comando não encontrar uma opção para processar nos case, a opção default, caso exista,
será processada; se, porém, nesses casos, a opção default não existir, a execução do programa
atravessará o switch sem executar instrução alguma:
case valor_x:
faz_isso;
break;
case valor_y:
faz_isto;
break;
case valor_z:
faz_aquilo;
break;
default:
faz_outra_coisa;
faz_isso_também;
O exemplo a seguir leva um Edit no Form. O código está voltado a impedir a entrada de caracteres não
numéricos no Text do Edit.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------bool virgula = true;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if (virgula == false)
DecimalSeparator = '\0';
Apostila de C++ Builder
Pagina -131-
//if(Edit1->Text.Length() != DecimalSeparator)
//virgula = true;
switch(Key)
{
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
case '.':
case ',':
case 13:
case 8:
default:
}
Key
Key
Key
Key
Key
Key
Key
Key
Key
Key
Key
Key
Key
Key
Key
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
'1';
break;
'2';
break;
'3';
break;
'4';
break;
'5';
break;
'6';
break;
'7';
break;
'8';
break;
'9';
break;
'0';
break;
DecimalSeparator;
DecimalSeparator;
13;
break;
8;
break;
'\0';
virgula = false;
virgula = false;
break;
break;
}
//---------------------------------------------------------------------------
Entendendo o evento OnMouseMove
Um evento OnMouseMove ocorre periodicamente quando o usuário move o mouse. O evento inicia-se
no objeto que está sob o ponteiro do mouse quando o usuário pressiona o botão. Isto permite ao usuário
algumas opções intermediárias enquanto o mouse move. Observe, porém, que podemos chamar esse
evento mesmo sem que qualquer botão esteja pressionado.
Para responder aos movimentos do mouse, defina um evento handler para o evento OnMouseMove.
Num Form vazio, selecione a guia Events do Object Inspector. No setor esquerdo das opções dessa
guia, procure pelo evento OnMouseMove. Dê um duplo clique no campo situado ao lado direito desse
evento para abrir a janela de edição de códigos. Imediatamente você entrará em contato com as seguintes
linhas de código:
//--------------------------------------------------------------------------void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
}
//---------------------------------------------------------------------------
Já temos conhecimento prévio do significado de várias partes do código acima. Mas há algumas
novidades: TShiftState Shift, int X, int Y)
Apostila de C++ Builder
Pagina -132-
Primeiramente, vamos entender o significado de do tipo TShiftState Shift.
O tipo TShiftState indica o estado das teclas Alt, Ctrl e Shift, bem como dos botões do mouse.
typedef Set<Classes__1, ssShift, ssDouble>
TshiftState;
Descrição: O tipo TShiftState é usado para eventos de teclado e eventos do mouse para determinar o
estado das teclas Alt, Ctrl e Shift, bem como dos botões do mouse, quando ocorrer um evento. Trata-se
de um grupo de flags que indica o seguinte:
Valor
Significa
ssShift
A tecla Shift está segura em baixo.
ssAlt
A tecla Alt está segura em baixo.
ssCtrl
A tecla Ctrl está segura em baixo.
ssLeft
O botão esquerdo do mouse está seguro em baixo.
ssRight
O botão direito do mouse está seguro em baixo.
ssMiddle
O botão do meio do mouse está seguro em baixo..
ssDouble
O mouse recebeu um duplo clique.
Podemos entender TShiftState como uma espécie de variável que, automaticamente, é nomeada de
Shift pelo C++Builder no evento estudado.
Quanto às duas variáveis inteiras nomeadas respectivamente de X e Y ( int X, int Y ) pelo
C++Builder, nada mais são do que os possíveis valores para as coordenadas X e Y do Form.
Então se, por exemplo, inserirmos as seguintes linhas de código no evento acima:
//--------------------------------------------------------------------------void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
if(Shift.Contains(ssShift) && X == 0 && Y == 0)
Close();
}
//---------------------------------------------------------------------------
Não ficará difícil entender que: se a tecla Shift (Shift.Contains(ssShift)) estiver pressionada
enquanto o mouse mover-se sobre as coordenadas 0 e 0 ( X == 0 && Y == 0 ), ou seja, no extremo
esquerdo do topo de Form1, o programa será encerrado.
Apostila de C++ Builder
Pagina -133-
TCanvas
A classe TCanvas encapsula um dispositivo de contexto Windows na VCL e um dispositivo de pintura
(Qt painter) em CLX, que lida com todo desenho para forms, containers visuais (como panels) e o objeto
printer, em ambas as formas. Usando o objeto canvas, você não tem dificuldades para alocar pens,
brushes, palettes, e assim por diante — todas as alocações e desalocações são tratadas por você.
TCanvas inclui um grande número de gráficos primitivos, rotinas para desenhar linhas, shapes (figuras),
polygons (polígonos), fonts (fontes), etc para qualquer controle que contém um canvas.
O exemplo abaixo trata um evento através do clique de um botão, determinando o desenho de uma linha
desde as coordenadas X = 100 e Y = 100 até as coordenadas X = 200 e Y = 200, bem como a colocação de
um texto, cujo início dar-se-á nessas últimas coordenadas:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Canvas->Pen->Color = clBlue;
Canvas->MoveTo( 100, 100 );
Canvas->LineTo( 200, 200 );
Canvas->Brush->Color = clBtnFace;
Canvas->Font->Name = "Arial";
Canvas->TextOut( Canvas->PenPos.x, Canvas->PenPos.y,"Este é o fim da linha"
);
}
O objeto TCanvas também protege você contra erros gráficos comuns Windows, como por exemplo
restaurar dispositivos contexto, pens, brushes, e assim por diante de acordo com os valores que eles
possuiam antes da operação desenho. TCanvas é usado em qualquer lugar no C++Builder onde um
desenho seja requerido ou possível, e faz gráficos facilmente.
TCanvas::CopyMode
Especifica como uma imagem gráfica é copiada sobre a tela.
__property int CopyMode = {read=FCopyMode, write=FCopyMode, default=13369376};
Descrição:
Use CopyMode para afetar o modo como imagens gráficas são desenhadas sobre a tela. CopyMode é
usado quando copiamos uma imagem de outra tela usando o método CopyRect. O CopyMode também é
usado por objetos TBitmap para desenhar sobre a tela.
Apostila de C++ Builder
Pagina -134-
Use CopyMode para executar uma variedade de afeitos quando desenhar uma imagem. Consiga efeitos
especiais como fundir imagens e criar partes de um bitmap transparente combinando imagens múltiplas
com diferentes CopyModes.
A tabela seguinte mostra os possíveis valores de CopyMode. (trata-se de constantes definidas em
Windows.hpp.)
cmBlackness
Preenche o retângulo de destino na tela com negro.
cmDstInvert
Inverte a imagem na tela e ignora a fonte.
cmMergeCopy
Combina a imagem na tela e a fonte bitmap usando o operador
booleano AND.
cmMergePaint
Combina a fonte invertida bitmap com a imagem na tela usando o
operador booleano OR.
cmNotSrcCopy
Copia a fonte bitmap invertida para a tela.
cmNotSrcErase
Combina a imagem na tela e a fonte bitmap usando o operador
booleano OR, e inverte o resultado.
cmPatCopy
Copia a fonte moldando na tela.
cmPatInvert
Combina a fonte moldando com a imagem na tela usando o
operador booleano XOR.
cmPatPaint
Combina a fonte invertida bitmap com a fonte moldada usando o
operador booleano OR. Combina o resultado dessa operação com a
imagem na tela usando o operador booleano OR.
cmSrcAnd
Combina a imagem na tela e a fonte bitmap usando o operador
booleano AND.
cmSrcCopy
Copia a fonte bitmap na tela.
cmSrcErase
Inverte a imagem na tela e combina o resultado com a fonte bitmap
usando o operador booleano AND.
cmSrcInvert
Combina a imagem na tela e a fonte bitmap usando o operador
booleano XOR.
cmSrcPaint
Combina a imagem na tela e a fonte bitmap usando o operador
booleano OR.
cmWhiteness
Preenche o retângulo de destino na tela com branco.
Apostila de C++ Builder
Pagina -135-
O seguinte exemplo usa CopyMode para deixar branco fora da imagem quando o usuário escolher
“Cortar” no menu .
void __fastcall TEdtDsnh::Cortar1Click(TObject *Sender)
{
TRect ARect;
// copia a figura para o Clipboard, através do método copiar.
Copiar1Click(Sender);
// preenche a seção cortada com branco. CmBlackness determinaria preto
Image1->Canvas->CopyMode = cmWhiteness;
// dimensiona a seção da tela copiada que será preenchida com branco
ARect = Rect(0, 0, Image1->Width, Image1->Height);
// complementa as duas linhas acima, retirando a figura (cortando)
Image1->Canvas->CopyRect(ARect, Image1->Canvas, ARect);
// restaura o modo default
Image1->Canvas->CopyMode = cmSrcCopy;
}
TRect, Rect, TextRect, Brush e FrameRect
Rect cria uma estrutura TRect para colocar as coordenadas.
Use Rect para criar uma TRect que representa o retângulo segundo as coordenadas especificadas. Use Rect
para construir parâmetros para funções que requerem TRect, especialmente para colocar variáveis locais
para cada parâmetro.
O exemplo a seguir usa um Button no Form que, ao ser clicado, exibe um texto em um retângulo definido
pelas coordenadas (10 e 10) e (300 e 300). Após exibir o texto por meio do método TextRect, o
código desenha uma linha definida pelo método FrameRect em volta do retângulo.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// TRect,
Rect,
TextRect,
Brush
e
FrameRect
TRect ARect;
// coordenadas do retângulo.
//O mesmo que: ARect = Rect(10,10,300,300);
ARect.Top = 10;
ARect.Left = 10;
ARect.Bottom = 300;
ARect.Right = 300;
Apostila de C++ Builder
Pagina -136-
// TCanvas::TextRect - escreve uma string
// dentro de um recorte retangular
// coordenadas do texto: X = 85 e Y = 100
Canvas->TextRect(ARect, 85, 100, "Texto no recorte retangular");
// clicar uma vez deixará a borda será vermelha;
// duas vezes, toda a área dentro do retângulo
Canvas->Brush->Color = clRed;
// A função FrameRect desenha uma borda em torno do retângulo
// especificado, usando o brush especificado. A largura e altura
// da borda será sempre uma unidade lógica.
Canvas->FrameRect(ARect);
}
CopyRect
A função CopyRect copia as coordenadas de um retângulo em outro.
BOOL CopyRect(
LPRECT lprcDst, // aponta para a estrutura do retângulo de destino
CONST RECT *lprcSrc // aponta para a estrutura com o retângulo inicial
);
O parâmetro lprcDst aponta para a estrutura RECT que recebe as coordenada lógicas do retângulo inicial
e o parâmetro lprcSrc aponta para a estrutura RECT à qual pertencem as coordenadas que estão sendo
copiadas.
Se a função lograr êxito, produzirá um valor de retorno diferente de zero; se fracassar, o valor de retorno
será zero. Para obter informações de erro estendidas, use GetLastError.
TCanvas::CopyRect
Copia partes de uma imagem de um a outro canvas.
void __fastcall CopyRect(const Windows::TRect &Dest, TCanvas* Canvas,
const Windows::TRect &Source);
Use CopyRect para transferir parte da imagem de outro canvas para a imagem do objeto TCanvas. Dest
especifica o retângulo no canvas onde a imagem inicial será copiada. O parâmetro Canvas especifica a
tela com a imagem fonte. Source especifica um retângulo que limita a porção de tela fonte que será
copiada.
A porção da tela inicial é copiada usando o modo especificado por CopyMode.
Apostila de C++ Builder
Pagina -137-
O exemplo a seguir ilustra como usufruir a diferença entre CopyRect e BrushCopy. O bitmap gráfico
“meu_desenho.bmp” é carregado dentro de Bitmap e exibido no Canvas de Form1. BrushCopy substitui
a cor negra no gráfico com o brush de canvas, enquanto CopyRect mantém a cor intacta.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form1->WindowState = wsMaximized; // amplia a janela
Graphics::TBitmap *Bitmap; // declara Bitmap
TRect ARect, A2Rect; // declara
ARect = Rect(30, 30, 480, 300); // inicializa ARect determinado as coordenadas
A2Rect = Rect(30, 310, 480, 570); // inicializa o retângulo A2Rect
Bitmap = new Graphics::TBitmap; // alocação dinâmica de memória para Bitmap
Bitmap->LoadFromFile("c:\\meu_desenho.bmp"); // carrega o arquivo especificado
// BrushCopy carregará o desenho substituindo as cores negras do mesmo
Form1->Canvas->BrushCopy(ARect, Bitmap, ARect, clBlack);
// CopyRect carregará o arquivo sem alterá-lo
Form1->Canvas->CopyRect(A2Rect, Bitmap->Canvas, ARect);
// desaloca a memória dinâmica
delete Bitmap;
}
métodos Canvas para desenhar objetos gráficos.
Esta seção mostra a forma de usar alguns métodos comuns para desenhar objetos gráficos. Eles envolvem:
desenhar linhas e múltiplas-linhas;
desenhar figuras;
desenhar retângulos arredondados;
desenhar polígonos.
desenhar linhas e múltiplas-linhas
Canvas pode ser usado para desenhar linhas e múltiplas-linhas. uma linha reta pode ser entendido como
uma linha de pixels conectando dois pontos. Múltiplas-linhas pode ser interpretado como uma série de
linhas retas, conectando cada ponto inicial com o seu ponto final. O canvas desenha todas as linhas
usando pen (caneta).
desenhar figuras
Apostila de C++ Builder
Pagina -138-
Canvas possui métodos diferentes para desenhar diferentes espécies de figuras. Você pode usar pen para
desenhar uma figura e brush (pincel) para preencher o interior da figura. A linha que firma a borda da
figuras é controlada pelo corrente objetos Pen.
Esta parte envolve:
desenhar retângulos e elipses;
desenhar retângulos arredondados;
desenhar polígonos.
desenhar retângulos e elipses
Para desenhar retângulo ou elipse na tela, chame o método Rectangle ou o método Ellipse, passando as
coordenadas dos limites do retângulo.
O método Rectangle desenha um retângulo; Ellipse desenha uma elipse que toca todos os lados das
coordenadas de um retângulo que lhe fornece os limites.
O seguinte método desenha um retângulo no canto superior esquerdo do form, colocando, depois, uma
elipse no interior do mesmo:
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// ClientWidth especifica a largura da área do controle em pixels
Canvas->Rectangle(0, 0, ClientWidth/2, ClientHeight/2);
// ClientHeight especifica a altura da área do controle em pixels
Canvas->Ellipse(0, 0, ClientWidth/2, ClientHeight/2);
}
desenhando retângulos arredondados
Para desenhar um retângulo arredondado na tela, chame o método RoundRect.
Os quatro primeiros parâmetros passados para RoundRect referem-se aos limites do retângulo
(coordenadas), da mesma forma que nos métodos Rectangle ou Ellipse. RoundRect recebe mais dois
parâmetros que indicam como desenhar cantos arredondados.
O seguinte método desenha um retângulo arredondado no quadrante superior esquerdo do form,
arredondando os cantos com seções de um círculo de 30 pixels de diâmetro:
Apostila de C++ Builder
Pagina -139-
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->RoundRect(0, 0, ClientWidth/2, ClientHeight/2, 30, 30);
}
desenhando polígonos
Para desenhar polígonos com qualquer número de linhas na tela, use o método Polygon de canvas, que
desenha uma série de linhas na tela conectando os pontos passados, providenciando, por fim, o
fechamento da figura por unir o último ponto com o primeiro.
Polygon recebe um array de pontos como seu único parâmetro e conecta os pontos com pen, até
conectar o último ponto com o primeiro para fechar o polígono. Depois de estipular os limites, Polygon
usa o brush para preencher a parte interior do polígono.
O exemplo a seguir leva um PaintBox no Form. Quando o programa é executado, desenha um polígono
irregular no form, cujas coordenadas são especificadas pelo array. Observe que, uma das maneiras pela
qual você pode alterar o resultado da execução, é variando as possibilidades de acordo com o exposto nos
comentários.
void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
{
TPoint ponto[6];
ponto[0] = Point(0,0);
ponto[1] = Point(30,80);
ponto[2] = Point(280,130);
ponto[3] = Point(280,220);
ponto[4] = Point(340,320);
ponto[5] = Point(440,380);
// ((TPaintBox *)Sender)->Canvas->Brush->Color = clBlue; /*opcional*/
((TPaintBox *)Sender)->Canvas->
Polygon(ponto, 5 /*número de lados: ou 4, ou 3, ou 2 ...*/);
}
propriedades Canvas
Com o objeto Canvas, você pode colocar as propriedades de uma pen para desenhar linhas, um brush
para preencher figuras, uma font para escrever um texto, e um array de pixels para representar a imagem.
As próximas seções descrevem:
Usando pens (canetas);
Usando brushes (pincéis);
Lendo e inserindo pixels.
Apostila de C++ Builder
Pagina -140-
Usando pens
A propriedade Pen de um controle canvas é o meio de fazer linhas aparecerem, incluindo linhas
trabalhadas como linhas de figuras. Desenhar uma linha linha reta é alterar um grupo de pixels que estão
entre dois pontos.
De si mesmo, pen possui quatro propriedades que você pode alterar: Color, Width, Style, e Mode.
Color muda a cor de pen.
Width muda a largura de pen.
Style muda o estilo de pen.
Mode muda a forma de pen.
O valor dessas propriedades determinam como pen pode alterar os pixels numa linha. Por default, cada
pen inicia com cor negra, com largura (width) de 1 pixel, um estilo sólido e um modo chamado copy
que escreve qualquer coisa elaboradamente na tela.
Você pode usar TPenRecall para rapidamente salvar e restaurar as propriedades de pens.
TPen::Color
Color determina a cor usada para desenhar linhas na tela.
__property TColor Color = {read=GetColor, write=SetColor, default=0};
Você pode usar Color para mudar a cor usada para desenhar linhas ou figuras. O jeito que Color é usado
depende das propriedades Mode e Style.
O exemplo a seguir desenha uma infinidade de elipses de vários estilos, cores e tamanhos (pen e brush)
no form para preencher inteiramente a tela. Para executar o código, coloque um componente TTimer no
form e use o Object Inspector para criar os eventos handlers OnTimer e OnActivate.
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -141-
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include <stdlib.h> // para random() e randomize(): valores aleatórios
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
int x, y;
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormActivate(TObject *Sender)
{
WindowState = wsMaximized; // janela maximizada
Timer1->Interval = 50; // intervalo de tempo para Timer
randomize();
}
//--------------------------------------------------------------------------void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
// inicializa variáveis com valores aleatórios
x = random(Screen->Width - 10);
y = random(Screen->Height - 10);
// Cor aleatória para pen
Canvas->Pen->Color = (Graphics::TColor) random(65535);
// comando de decisão
switch (random(5))
{
case 0: Canvas->Pen->Style = psSolid; // estilo da linha
Canvas->Brush->Color = clLime; break; // cor da figura: clLime
case 1: Canvas->Pen->Style = psDash;
Canvas->Brush->Color = random(65535); break; // cor aleatória
case 2: Canvas->Pen->Style = psDot;
Canvas->Brush->Color = random(65535); break;
case 3: Canvas->Pen->Style = psDashDot;
Canvas->Brush->Color = random(65535); break;
case 4: Canvas->Pen->Style = psDashDotDot;
Canvas->Brush->Color = clAqua; break;
}
// desenha elípses com tamanhos diversos escolhidos aleatoriamente
Canvas->Ellipse(x, y, x + random(400), y + random(400));
}
Apostila de C++ Builder
Pagina -142-
//---------------------------------------------------------------------------
TPen::Width
Especifica a largura máxima de um pen em pixels.
__property int Width = {read=GetWidth, write=SetWidth, default=1};
Você pode usar a propriedade Width para dar à linha maior ou menor espessura. Se Width for colocado
para um valor menor do que 1, o valor de pen automaticamente será remetido para 1.
Nota: O valor de Width influencia os possíveis valores válidos de Style.
O exemplo a seguir desenha muitos grupos, geralmente sobrepostos, de retângulos, retângulos
arredondados e elipses de vários tamanhos e cores em um form maximizado, preenchendo totalmente o
screen:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include <stdlib.h> // para random() e randomize(): valores aleatórios
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
int x, y;
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormActivate(TObject *Sender)
{
WindowState = wsMaximized; // janela maximizada
Timer1->Interval = 600; // intervalo de tempo para Timer
randomize();
}
//--------------------------------------------------------------------------void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
x = random(Screen->Width - 10);
y = random(Screen->Height - 10);
Canvas->Pen->Color = (Graphics::TColor) random(65535);
Canvas->Pen->Width = random(50); // Pen terá largura variável
Apostila de C++ Builder
Pagina -143-
int Dx = random(400); // variáveis para os pixels
int Dy = random(400); // do arredondamento do retângulo
Canvas->Brush->Color = random(65535); // cor aleatória p retângulo arredondado
Canvas->RoundRect(x, y, x + Dx, y + Dy, Dx/2, Dy/2);
Canvas->Brush->Color = random(65535); // cor aleatória para o retângulo
Canvas->Ellipse(x, y, x + random(400), y + random(400));
Canvas->Brush->Color = random(65535); // cor aleatória para a elípse
Canvas->Rectangle(x, y, x + random(400), y + random(400));
}
//---------------------------------------------------------------------------
TPen::Style
Determina o estilo que pen desenha linhas.
enum TPenStyle
{psSolid, psDash, psDot, psDashDot, psDashDotDot, psClear, psInsideFrame};
__property TPenStyle Style = {read=GetStyle, write=SetStyle, default=0};
Você pode usar Style para escolher entre várias opções de como desenhará linhas, como por exemplo,
desenhar uma linha pontilhada ou tracejada, ou para omitir a linha que aparece como uma moldura em
torno de uma figura. Eis os possíveis valores para Style:
psSolid
Uma linha sólida.
psDash
Uma linha composta de uma série de traços.
psDot
Uma linha feita com uma série de pontos.
psDashDot
Uma linha construída alternando traços e pontos.
psDashDotDot
Uma linha feita com uma série de combinações traço-ponto-ponto.
psClear
Linhas não são desenhadas (usado para omitir a linha em volta de figuras
que traça um desenho usando a atual pen).
psInsideFrame
Uma linha sólida, mas que pode usar uma cor diferente se Width é maior
do que 1.
Nota: Só o estilo psInsideFrame produz cor diferente para adaptar à propriedade Color que não está na
tabela de cores. Todos os outros escolhem cores na tabela de cores do Windows.
Nota: estilos pontilhados ou tracejados não estão disponíveis quando a propriedade Width é diferente de
1.
Apostila de C++ Builder
Pagina -144-
Veja o exemplo do tópico Tpen::Color.
TPen::Mode
Determina como pen desenha linhas na tela.
enum TPenMode
{pmBlack, pmWhite, pmNop, pmNot, pmCopy, pmNotCopy, pmMergePenNot,
pmMaskPenNot, pmMergeNotPen, pmMaskNotPen, pmMerge, pmNotMerge, pmMask,
pmNotMask, pmXor, pmNotXor};
__property TPenMode Mode = {read=FMode, write=SetMode, default=4};
Use Mode para determinar como a cor de pen atua sobre a cor sobre o canvas. Os efeitos de Mode estão
descritos na seguinte tabela:
Mode
Cores Pixels
pmBlack
Sempre negro.
pmWhite
Sempre branco.
pmNop
Imutável.
pmNot
Inverso à cor de canvas background.
pmCopy
Cor de Pen especificada na propriedade Color.
pmNotCopy
Inverso da cor de pen.
pmMergePenNot
Combinação da cor de pen e o inverso de canvas background.
pmMaskPenNot
Combinação de cores comuns para ambos, pen e inverso de canvas
background.
pmMergeNotPen
Combinação da cor de canvas background e o inverso da cor de pen.
pmMaskNotPen
Combinação de cores comuns para ambos, canvas background e o inverso
de pen.
pmMerge
Combinação da cor de pen e da cor de canvas background.
Apostila de C++ Builder
Pagina -145-
Mode
Cores Pixels
pmNotMerge
Inverso de pmMerge: combinação da cor de pen e cor de canvas
background.
pmMask
Combinação de cores comuns em ambos, pen e canvas background
pmNotMask
Inverso de pmMask: combinação de cores comuns em ambos, pen e
canvas background
pmXor
Combinação de cores em um ou em outro (pen ou canvas background),
mas não em ambos.
pmNotXor
Inverso de pmXor: combinação de cores em um ou outro (pen ou canvas
background), mas não juntos.
TPenRecall
TPenRecall salva e restaura as propriedades de um objeto TPen.
Use TPenRecall para armazenar o estado atual de um objeto TPen. Então você estará livre para mudar
esse estado atual armazenado do objeto pen. Para restaurar o objeto pen para o seu estado original
armazenado, basta destruir o objeto TPenRecall o que fará com que o objeto pen seja automaticamente
restaurado para as propriedades e valores salvos.
Você pode atualizar a instância TPenRecall para refletir as propriedades atuais do objeto TPen chamando
o método Store. Você pode prevenir a destruição de TPenRecall da atualização do objeto TPen pela
chamada do método Forget.
Usando brushes
A propriedade Brush de um controle canvas é o meio pelo qual podemos preencher o interior de áreas,
incluindo figuras. Preencher uma área com um brush é, nada mais, do que um meio de alterarmos um
grande número de pixels na área especificada, ou seja, um pincel.
Brush possui três propriedades que podemos manipular:
Color Muda a cor de preenchimento.
Style muda o estilo de brush.
Bitmap usa um bitmap como um modelo para brush.
Apostila de C++ Builder
Pagina -146-
O valor dessas propriedades determina o jeito que o canvas preenche figuras ou outras áreas. Por default,
qualquer brush inicia branco, com um estilo sólido e modelo não bitmap.
Você pode usar TBrushRecall para rapidamente salvar e restaurar as propriedades de brushes.
Tcanvas::Brush
Determina a cor e o padrão de preenchimento para figuras gráficas e backgrounds.
__property TBrush* Brush = {read=FBrush, write=SetBrush};
Use a propriedade Brush para especificar a cor e o padrão quando desenhar o background ou preencher
em figuras gráficas. O valor de Brush está num objeto TBrush. Use as propriedades do objeto TBrush
para especificar a cor e o modelo ou bitmap quando preencher espaços no canvas.
Nota: Fixando a propriedade Brush nomeie o objeto TBrush especificado, no lugar de substituir o objeto
TBrush atual.
O código a seguir carrega um bitmap desde um arquivo, destinando-o ao Brush do Canvas de Form1:
Graphics::TBitmap *BrushBmp = new Graphics::TBitmap;
try
{
BrushBmp->LoadFromFile("C:\meu_desenho.bmp");
Form1->Canvas->Brush->Bitmap = BrushBmp;
Form1->Canvas->FillRect(Rect(0,0,100,100));
}
__finally
{
Form1->Canvas->Brush->Bitmap = NULL;
delete BrushBmp;
}
TCanvas::FillRect
Preenche o retângulo especificado no canvas usando o brush atual.
void __fastcall FillRect(const Windows::TRect &Rect);
Use FillRect para preencher uma região retangular usando o pincel atual. A região preenchida inicia-se
com as coordenadas do topo esquerdo do retângulo até as coordenadas da base direita do mesmo.
Apostila de C++ Builder
Pagina -147-
O exemplo a seguir cria um retângulo no form, preenchendo-o de branco (cor default para Brush), ou
vermelho (se você remover o símbolo: // ).
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRect NewRect = Rect(20, 30, 350, 290);
//
Canvas->Brush->Color = clRed;
Canvas->FillRect(NewRect);
}
TBrush::Color
Indica a cor do brush.
__property TColor Color = {read=GetColor, write=SetColor, default=16777215};
A propriedade Color determina a cor de brush. Esta cor é usada para traçar a forma representada pela
propriedade Style, e não a cor background do brush (a menos que Style seja sólido).
Nota: Se o valor da propriedade Style for bsClear, a propriedade Color é ignorada. Mais ainda,
qualquer valor marcado para a propriedade Color é perdido quando Style é marcado para bsClear.
O exemplo a seguir leva um Button e um Image no Form. Quando o usuário clicar o botão, uma elipse é
desenhada em Image1 (TImage sobre o form), adotando o estilo bsDiagCross para Brush, ou seja,
linhas cruzadas, as quais serão vermelhas:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TCanvas *pCanvas = Image1->Canvas;
pCanvas->Brush->Color = clRed;
pCanvas->Brush->Style = bsDiagCross;
pCanvas->Ellipse(0, 0, Image1->Width, Image1->Height);
}
TBrush::Style
Especifica a forma para brush.
enum TBrushStyle {bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal,
Apostila de C++ Builder
Pagina -148-
bsBDiagonal, bsCross, bsDiagCross
__property TBrushStyle Style = {read=GetStyle, write=SetStyle, default=0};
A propriedade Style determina a forma pintada por brush, a menos que um valor seja determinado por
Bitmap. Os possíveis valores de Style são:
bsSolid
bsCross
bsClear
bsDiagCross
bsBDiagonal
bsHorizontal
bsFDiagonal
bsVertical
O exemplo a seguir demonstra o uso dos possíveis valores da propriedade Style de Brush, pintando os
exemplos no Form que é redimensionado quando o mesmo recebe um clique do mouse:
//--------------------------------------------------------------------------void __fastcall TForm1::FormClick(TObject *Sender)
{
Form1->Height = 430;
Form1->Width = 410;
TCanvas *pCanvas = Form1->Canvas;
pCanvas->Brush->Color = clBlue;
Apostila de C++ Builder
Pagina -149-
pCanvas->Brush->Style = bsDiagCross;
pCanvas->Ellipse(0, 0, 100, 200);
pCanvas->Brush->Color = clRed;
pCanvas->Brush->Style = bsSolid;
pCanvas->Rectangle(101, 0, 200, 200);
pCanvas->Brush->Color = clYellow;
pCanvas->Brush->Style = bsDiagCross;
pCanvas->RoundRect(201, 0, 300, 200, 50, 90);
pCanvas->Brush->Color = clLime;
pCanvas->Brush->Style = bsBDiagonal;
pCanvas->Ellipse(301, 0, 400, 200);
pCanvas->Brush->Color = clFuchsia;
pCanvas->Brush->Style = bsHorizontal;
pCanvas->Rectangle(0, 200, 100, 400);
pCanvas->Brush->Color = clWhite;
pCanvas->Brush->Style = bsFDiagonal;
pCanvas->RoundRect(101, 200, 200, 400, 90, 50);
pCanvas->Brush->Color = clGreen;
pCanvas->Brush->Style = bsVertical;
pCanvas->Ellipse(201, 200, 300, 400);
pCanvas->Brush->Color = clGreen;
pCanvas->Brush->Style = bsClear;
pCanvas->Rectangle(301, 200, 400, 400);
}
//---------------------------------------------------------------------------
Dica: Coloque a propriedade Style para bsClear para eliminar instabilidade na tela (pisca-pisca) quando
o objeto é repintado.
TBrush::Bitmap
Especifica uma imagem bitmap externa que define uma composição para brush.
__property TBitmap* Bitmap = {read=GetBitmap, write=SetBitmap};
Bitmap aponta para um objeto TBitmap que guarda uma imagem bitmap. Se Bitmap não estiver vazio, a
imagem bitmap define a composição para brush’s. Se a imagem é maior do que oito pixels por oito
pixels, somente a região do topo esquerdo oito-por-oito é usado.
Apostila de C++ Builder
Pagina -150-
Mudar a imagem não afeta brush até que TBitmap seja redefinido para a propriedade Bitmap. Deve-se
liberar o TBitmap depois de finalizado com brush, visto que TBrush não pode fazê-lo.
O exemplo a seguir leva um Button no Form. Quando o usuário clicar o botão, um bitmap é carregado do
disco e destinado para o Brush do Canvas de Form1. O form é totalmente preenchido, mesmo que a
figura tenha um dimensionamento menor. Nesse caso as imagens serão colocadas lado a lado.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Graphics::TBitmap *BrushBmp = new Graphics::TBitmap;
try
{
TRect ARect = Rect(0, 0, Width, Height);
BrushBmp->LoadFromFile("C:\\Meu_desenho.bmp");
Canvas->Brush->Bitmap = BrushBmp;
Canvas->FillRect(ARect);
}
__finally
{
Canvas->Brush->Bitmap = NULL;
delete BrushBmp;
}
}
TBrushRecall
TBrushRecall salva e restaura um objeto TBrush.
Use TBrushRecall para guardar o atual estado de um objeto TBrush. Você então está livre para mudar o
estado atual do objeto brush. Para restaurar o objeto brush ao seu estado original, basta destruir o objeto
TBrushRecall e o objeto brush é automaticamente restaurado para os valores da propriedade salvos.
Você pode atualizar a instância TBrushRecall para refletir as propriedades atuais do objeto TBrush pela
chamada do método Store. Você pode impedir o destruidor de TBrushRecall pela atualização do objeto
TBrush pela chamada do método Forget.
Ler e inserir pixels
Você pode reparar que cada tela possui uma propriedade Pixels que representa pontos individuais
coloridos que compõem a imagem na tela. Raramente você precisará acessar Pixels direta ou
individualmente, sendo que eles estão disponíveis apenas, por conveniência, para executar pequenas ações
como, por exemplo, buscar ou inserir uma cor.
Apostila de C++ Builder
Pagina -151-
Nota: Manipular pixels individuais é muito mais lento do que executar operações gráficas por regiões.
Não use a propriedade array Pixel para ter acesso a pixels de imagem de um array geral. Para acesso de
alto-desempenho a pixels de imagem, veja a propriedade TBitmap::ScanLine.
TCanvas::Pixels
Especifica as cores dos pixels dentro do atual ClipRect.
__property TColor Pixels[int X][int Y] = {read=GetPixel, write=SetPixel};
Use Pixels para descobrir a cor na superfície do desenho em uma posição específica dentro da região
selecionada. Se a posição estiver fora do retângulo selecionado, a leitura do valor de Pixels retornará -1;
use Pixels para mudar a cor individual de pixels na superfície do desenho; e use Pixels para detalhar
efeitos em uma imagem. Pixels também pode ser usado para determinar a cor que deveria ser usada pelo
método FillRect.
Nem todo contexto de dispositivo suporta a propriedade Pixels. Ler a propriedade Pixels para cada
contexto de dispositivo retorna um valor de -1. Colocar a propriedade Pixels para cada contexto de
dispositivo não realiza ação alguma.
O exemplo a seguir desenha várias linhas coloridas formando uma moldura, quando o usuário pressiona o
botão.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
for (int i = 10; i <= 300; i++)
{
Canvas->Pixels[i][10] = clRed;
Canvas->Pixels[i][14] = clBlue;
Canvas->Pixels[i][18] = clYellow;
Canvas->Pixels[i][22] = clWhite;
Canvas->Pixels[i][26] = clFuchsia;
Canvas->Pixels[i][30] = clGreen;
Canvas->Pixels[10][i] = clRed;
Canvas->Pixels[14][i] = clBlue;
Canvas->Pixels[18][i] = clYellow;
Canvas->Pixels[22][i] = clWhite;
Canvas->Pixels[26][i] = clFuchsia;
Canvas->Pixels[30][i] = clGreen;
Canvas->Pixels[i][280] = clRed;
Canvas->Pixels[i][284] = clBlue;
Canvas->Pixels[i][288] = clYellow;
Apostila de C++ Builder
Pagina -152-
Canvas->Pixels[i][292] = clWhite;
Canvas->Pixels[i][296] = clFuchsia;
Canvas->Pixels[i][300] = clGreen;
Canvas->Pixels[280][i] = clRed;
Canvas->Pixels[284][i] = clBlue;
Canvas->Pixels[288][i] = clYellow;
Canvas->Pixels[292][i] = clWhite;
Canvas->Pixels[296][i] = clFuchsia;
Canvas->Pixels[300][i] = clGreen;
}
}
Para entender o que cada bloco faz, você pode tirá-lo da execução, colocando-o entre os símbolos de
comentário C: /* */
TBitmap::ScanLine
Provê acesso indexado a cada linha de pixels.
__property void * ScanLine[int Row] = {read=GetScanline};
ScanLine é usado somente com DIBs (Device Independent Bitmaps) para imagens que são editadas em
ferramentas que fazem trabalhos de pixels de baixo nível.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Graphics::TBitmap *pBitmap = new Graphics::TBitmap();
// Este exemplo mostra edição diretamente no Bitmap
Byte *ptr;
try
{
pBitmap->LoadFromFile
("C:\\meu_desenho.bmp");
for (int y = 0; y < pBitmap->Height; y++)
{
ptr = (Byte *)pBitmap->ScanLine[y];
for (int x = 0; x < pBitmap->Width; x++)
ptr[x] = (Byte)y;
}
Canvas->Draw(0,0,pBitmap);
}
catch (...)
{
ShowMessage("Não foi possível carregar ou alterar bitmap");
}
Apostila de C++ Builder
Pagina -153-
delete pBitmap;
}
Propriedades e métodos comuns de Canvas
A tabela a seguir exibe as propriedades do objeto Canvas geralmente usadas.
Propriedades
descrições
Font
Especifica a fonte a ser usada quando escrever textos na imagem. Use as propriedades
do objeto TFont para especificar a aparência da fonte, como cor, tamanho e estilo da
fonte.
Brush
Determina a cor e a forma que Canvas usa para preencher figuras gráficas e
backgrounds. Use as propriedades do objeto TBrush para especificar a cor e a forma
ou bitmap em uso quando preencher espaços no canvas.
Pen
Especifica a espécie de caneta que o canvas usa para desenhar linhas e esboçar
figuras. Use as propriedade do objeto TPen para especificar a cor, estilo largura e
modo da caneta.
PenPos
Especifica a posição atual de pen.
Pixels
Especifica a cor de pixels da área dentro do atual ClipRect.
Estas propriedades são descrevidas em maiores detalhes em Using the properties of the Canvas object.
Abaixo a lista de vários métodos que você pode usar:
Métodos
Descrições
Arc
void __fastcall Arc(int X1, int Y1, int X2, int Y2,
int X3, int Y3, int X4, int Y4);
Desenha um arco na imagem ao longo do perímetro da elipse limitada pelo
retângulo especificado.
Use Arc para desenhar uma linha encurvada elipticamente com a Pen (caneta)
atual. O arco atravessa o perímetro de uma elipse que é limitada pelos pontos
(X1,Y1) e (X2,Y2). O arco é desenhado seguindo o perímetro da elipse, no sentido
anti-horário (à esquerda), do ponto de partida para o ponto final. O ponto inicial é
definido pela intersecção da elipse e uma linha definida pelo centro da elipse e
(X3,Y3). O ponto final é definido pela intersecção da elipse e uma linha definida
pelo centro da elipse e (X4, Y4). Exemplo:
void __fastcall TForm1::FormPaint(TObject *Sender)
{
TRect R = GetClientRect(); // Coordenadas retangulares
// da janela atual
Apostila de C++ Builder
Pagina -154-
Métodos
Descrições
Canvas->Arc(R.Left, R.Top, R.Right, R.Bottom,
R.Right, R.Top, R.Left, R.Top);
}
Chord
void __fastcall Chord(int X1, int Y1, int X2, int Y2,
int X3, int Y3, int X4, int Y4);
Desenha figura fechada representada pela intersecção de uma linha e uma elipse.
Use Chord para criar uma forma que é definida por um arco e uma linha que une
os pontos finais do arco. A corda (chord) consiste numa porção da elipse que é
limitada pelos pontos (X1,Y1) e (X2,Y2). A elipse é dividida por uma linha que liga
os pontos (X3,Y3) e (X4,Y4).
O perímetro de chord vai da direita (X3, Y3), para a esquerda, ao longo da elipse
para (X4,Y4), e para trás (X3,Y3). Se (X3,Y3) e (X4,Y4) não estiver na superfície da
elipse, os cantos correspondentes em chord serão os pontos mais íntimos do
perímetro que cruza a linha. O desenho da corda (chord) é feito usando valores
tirados de Pen, e a figura é preenchida usando valores de Brush.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRect R = Rect(20, 5, 350, 350);
Canvas->Chord(20, 5, 350, 350, 10, 10, 200, 200);
}
CopyRect
void __fastcall CopyRect(const Windows::TRect &Dest,
TCanvas* Canvas,const Windows::TRect &Source);
Copia parte de uma imagem de um canvas diferente para dentro do Canvas.
Use CopyRect para transferir parte da imagem de outra tela para a imagem do
objeto TCanvas. Dest especifica o retângulo na tela onde a imagem de fonte será
copiada. O parâmetro de Canvas especifica a tela com a imagem fonte. Source
especifica um retângulo que limita a porção da tela fonte que será copiada.
A porção da tela fonte é copiada usando o modo especificado por CopyMode.
Exemplo:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Graphics::TBitmap *Bitmap;
TRect Figura_Copiada;
Figura_Copiada = Rect(0,0,300,300);
Bitmap = new Graphics::TBitmap;
Bitmap->LoadFromFile("c:\\indio2.bmp");
Form1->Canvas->CopyRect(Figura_Copiada,
Bitmap->Canvas,
Figura_Copiada);
Apostila de C++ Builder
Pagina -155-
Métodos
Descrições
delete Bitmap;
}
Draw
void __fastcall Draw(int X, int Y, TGraphic* Graphic);
Desenha o objeto gráfico especificado pelo parâmetro Graphic na tela na
localização determinada pelas coordenadas (X, Y).
Chame Draw para colocar um gráfico na tela. Draw chama o método Draw da
imagem. A imagem é desenhada em um retângulo determinado pelo tamanho de
Graphic, com o canto esquerdo superior nos pontos X e Y.
Gráficos podem ser bitmaps, ícones, ou metafiles. Se o gráfico é um objeto
TBitmap, o bitmap é desenhado usando o valor de CopyMode. Exemplo:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Graphics::TBitmap* figura = new Graphics::TBitmap;
figura->LoadFromFile("c:\\meu_desenho.bmp");
Canvas->Draw(10, 10, figura);
delete figura;
}
Ellipse
void __fastcall Ellipse(int X1, int Y1, int X2, int Y2);
Desenha a elipse definida pelos limites de um retângulo na tela.
Use Ellipse para desenhar um círculo ou elipse na tela. O ponto do topo superior
direito do retângulo é o pixel representado pelas coordenadas X1 e Y1 e o ponto
inferior direito pelas coordenadas X2 e Y2. Se os pontos do retângulo formam um
quadrado, um círculo é desenhado.
A elipse é desenhada usando o valor de Pen, e preenchida usando o valor de Brush.
Exemplo:
TCanvas *pCanvas = Canvas;
pCanvas->Brush->Color = clRed;
pCanvas->Brush->Style = bsSolid;
pCanvas->Ellipse(0, 0, Width*2, Height/2);
pCanvas->Brush->Color = clYellow;
pCanvas->Brush->Style = bsVertical;
pCanvas->Ellipse(0, 0, Width, Height);
pCanvas->Brush->Color = clBlue;
pCanvas->Brush->Style = bsHorizontal;
pCanvas->Ellipse(200, 0, Width*2, Height);
FillRect
void __fastcall FillRect(const Windows::TRect &Rect);
Apostila de C++ Builder
Pagina -156-
Métodos
Descrições
Preenche o retângulo especificado na tela usando o brush (pincel) atual.
Use FillRect para preencher uma região retangular usando o pincel atual. A região
preenchida inicia-se com as coordenadas do topo esquerdo do retângulo até as
coordenadas da base direita do mesmo. O exemplo a seguir cria um retângulo no
form, preenchendo-o de azul.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRect NewRect = Rect(20, 30, 350, 290);
Canvas->FBrush->Color = ClBlue;
Canvas->FillRect(NewRect);
}
FloodFill
enum TFillStyle {fsSurface, fsBorder};
void __fastcall FloodFill(int X, int Y,
TColor Color, TFillStyle FillStyle);
Preenche a área da tela usando o brush atual.
Use FloodFill para encher uma região possivelmente não retangular da imagem
com o valor de Brush. Os limites da região para ser preenchidos são determinados
pelo passar ao lado externo do ponto (X,Y) quando o limite de cor que envolve o
parâmetro Color é encontrado.
FillStyle determina qual tipo de mudança de cor define os limites, como indicado
na seguinte tabela.
fsSurface Enche toda a área que tem a cor indicada pelo parâmetro Color. Para
quando outra cor é encontrada.
fsBorder Enche toda a área que não tem a cor indicada pelo parâmetro Color. Para
quando Color é encontrado.
Use a propriedade Pixels para adquirir o valor exato da cor no ponto (X,Y) quando
usar fsSurface de FillStyle. Semelhantemente, quando FillStyle é fsBorder, use
Pixels para adquirir o valor exato da cor limite se um ponto no limite é conhecido.
void __fastcall TForm1::FormMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
TCanvas *pCanvas = Canvas;
pCanvas->Ellipse(0, 0, 100, 400);
}
//---------------------------------------------------------------void __fastcall TForm1::FormMouseUp(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
Apostila de C++ Builder
Pagina -157-
Métodos
Descrições
{
Canvas->FloodFill(ClientWidth/2,
fsBorder);
}
FrameRect
ClientHeight/2,
clBlack,
void __fastcall FrameRect(const Windows::TRect &Rect);
Desenha uma borda de retângulo usando o Brush do canvas.
Use FrameRect para desenhar um 1 pixel de borda ao redor de uma região
retangular. FrameRect não enche o interior do retângulo com o Brush padrão.
Para desenhar um limite usando Pen, use o método Polygon.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRect ARect;
Canvas->Brush->Color = clFuchsia;
ARect.Top = 10;
ARect.Left = 10;
ARect.Bottom = 200;
ARect.Right = 200;
Canvas->FrameRect(ARect);
}
LineTo
void __fastcall LineTo(int X, int Y);
Desenha uma linha no canvas desde PenPos para o ponto especificado por X e Y,
colocando a posição atual de Pen para (X, Y).
Use LineTo para desenhar uma linha desde PenPos até o ponto X e Y, mas não
incluindo o mesmo. LineTo altera o valor de PenPos para X e Y. A linha é
desenhada usando Pen.
void __fastcall TForm1::FormMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
Canvas->LineTo(X, Y);
}
MoveTo
void __fastcall MoveTo(int X, int Y);
Altera, determinado a posição atual do desenho de acordo com as coordenadas X e
Y.
Muda a posição atual do desenho para o ponto X e Y. Use MoveTo para colocar o
valor de PenPos antes de LineTo. Chamar MoveTo é equivalente a setar a
propriedade PenPos.
Apostila de C++ Builder
Pagina -158-
Métodos
Descrições
int p_X, p_Y; // Variáveis para trabalhar com o ponto (X,Y)
void __fastcall TForm1::FormMouseMove(TObject *Sender,
TShiftState Shift, int X, int Y)
{
// Canvas->FillRect(ClientRect);
Canvas->MoveTo(p_X, p_Y);
Canvas->LineTo(X, Y);
}
//-------------------------------------------------------------void __fastcall TForm1::FormMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
p_X = X;
p_Y = Y;
}
Pie
void __fastcall Pie(int X1, int Y1, int X2, int Y2,
int X3, int Y3, int X4, int Y4);
Desenha a figura seccionada em direção da elipse como se dela houvera sido
retirada uma fatia (como num bolo redondo). O seccionamento (ou cunha) é
determinado por duas linhas que radiam do centro da elipse pelos pontos (X3, Y3) e
(X4, Y4). A cunha é desenhada com Pen, e preenchida com Brush. Exemplo:
Canvas->Pie(50, 50, 300, 200, 50, 30, 100, 50);
Polygon
void __fastcall Polygon(const Windows::TPoint * Points,
const int Points_Size);
Desenha uma série de linhas na tela, ligando os pontos passados e fechando a
figura ao ligar o último ponto ao primeiro ponto.
Use Polygon para desenhar uma figura com vários lados fechados na tela, usando o
valor de Pen. Depois de completar o desenho da figura, Polygon preenche a figura
usando o valor de Brush. Para desenhar um polígono na tela, sem preenchê-lo, use
o método Polyline, especificando o primeiro ponto uma segunda vez no fim (após
o último ponto).
O exemplo a seguir desenha uma estrela de cinco pontas no Form, preenchendo as
pontas da mesma com amarelo:
void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint points[5];
points[0] = Point(40,10);
points[1] = Point(20,60);
points[2] = Point(70,30);
points[3] = Point(10,30);
points[4] = Point(60,60);
Apostila de C++ Builder
Pagina -159-
Métodos
Descrições
Canvas->Brush->Color = 0xffff; // Color = clYellow
Canvas->Polygon(points, 4);
}
PolyLine
void __fastcall Polyline(const Windows::TPoint * Points,
const int Points_Size);
Desenha uma série de linhas na tela com a atual caneta (pen), conectando cada um
dos pontos passados de acordo com Points. Use Polyline para ligar um grupo de
pontos na tela. Se existem apenas dois pontos, Polyline desenha uma linha linha
simples.
Chamando a função MoveTo com o valor do primeiro ponto, e então chame LineTo
repetidamente com todos os pontos subseqüentes para desenhar uma imagem na
tela. De qualquer forma, diferente de LineTo, Polyline não altera o valor de
PenPos.
O exemplo a seguir, que procura demonstrar sutis diferenças, desenha duas estrelas
com linhas coloridas. Uma estrela no Form e outra num PaintBox:
void __fastcall TForm1::FormPaint(TObject *Sender)
{
TPoint points[6];
Canvas->Pen->Color = 0xff0000; // clBlue
points[0].x = 40;
points[0].y = 10;
points[1].x = 20;
points[1].y = 60;
points[2].x = 70;
points[2].y = 30;
points[3].x = 10;
points[3].y = 30;
points[4].x = 60;
points[4].y = 60;
points[5].x = 40;
points[5].y = 10;
Canvas->Polyline(points,5);
}
//----------------------------------------------------------void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
{
TPaintBox *pPB = (TPaintBox *)Sender;
TPoint points[6];
pPB->Canvas->Pen->Color = clFuchsia; // Color = 0xff00ff;
points[0].x = 40;
points[0].y = 10;
points[1].x = 20;
points[1].y = 60;
points[2].x = 70;
Apostila de C++ Builder
Pagina -160-
Métodos
Descrições
points[2].y = 30;
points[3].x = 10;
points[3].y = 30;
points[4].x = 60;
points[4].y = 60;
points[5].x = 40;
points[5].y = 10;
((TPaintBox *)Sender)->Canvas->Polyline(points,5);
// A linha acima é equivalente a:
// pPB->Canvas->Polyline(points,5);
}
Rectangle
void __fastcall Rectangle(int X1, int Y1, int X2, int Y2);
Desenha um retângulo na tela com o canto superior esquerdo no ponto (X1, Y1) e o
canto inferior direito no ponto (X2, Y2). Use Rectangle para desenhar um retângulo
com Pen e preenchendo-o com Brush. Para preencher a região retangular externa
desenhando a borda no corrente pen, use FillRect. Para desenhar uma área
retangular externa adicional, use FrameRect ou Polygon. Para desenhar um
retângulo com cantos arredondados, use RoundRect.
O exemplo a seguir desenha muitos retângulos de vários tamanhos e cores no form
maximizado preenchendo janela. Para executar o exemplo, inclua o cabeçalho
stdlib.h, coloque um componente TTimer no Form e use o object inspector para
criar o evento OnTimer.
int x, y;
//--------------------------------------------------------void __fastcall TForm1::FormActivate(TObject *Sender)
{
WindowState = wsMaximized;
Canvas->Pen->Width = 20;
Canvas->Pen->Style = psDot;
Timer1->Interval = 50;
randomize();
}
//---------------------------------------------------------void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
x+= 4;
y+=4;
Canvas->Pen->Color = random(65535);
Canvas->Rectangle(x, y, x + random(400), y + random(400));
if(x > 700)
Timer1->Enabled = false;
}
Apostila de C++ Builder
Pagina -161-
Métodos
Descrições
RoundRect
void __fastcall RoundRect(int X1, int Y1, int X2,
int Y2, int X3, int Y3);
Desenha um retângulo na tela com o canto superior esquerdo no ponto (X1, Y1) e o
canto inferior direito no ponto (X2, Y2). Use Rectangle para desenhar um retângulo
com Pen e preenchendo-o com Brush. Para preencher a região retangular externa
desenhando a borda no corrente pen, use FillRect. Para desenhar uma área
retangular externa adicional, use FrameRect ou Polygon. Para desenhar um
retângulo com cantos arredondados, use RoundRect.
Este exemplo desenha retângulos de cantos arredondados de vários tamanhos e
cores no form maximizado preenchendo a janela. Para executar o exemplo,
coloque um componente TTimer no form e use o object inspector para criar o
evento OnTimer.
int x, y;
void __fastcall TForm1::FormActivate(TObject *Sender)
{
WindowState = wsMaximized;
Canvas->Pen->Width = 20;
Canvas->Pen->Style = psInsideFrame;
Timer1->Interval = 100;
randomize();
}
//---------------------------------------------------------------void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
x+=4;
y+=4;
Canvas->Pen->Color = random(65535);
Canvas->RoundRect(x, y, x + random(400), y + random(400),
40, 40);
if(x == 600)
{
x = 400;
y = 4;
}
}
StretchDraw
void __fastcall StretchDraw(const Windows::TRect &Rect,
TGraphic* Graphic);
Desenha o gráfico especificado no parâmetro Graphic no retângulo especificado
pelo parâmetro Rect. Chame StretchDraw para desenhar um gráfico na tela de
forma que a imagem encaixe-se no retângulo especificado. StretchDraw chama o
método Draw do gráfico. O objeto gráfico determina a forma de preenchimento do
Apostila de C++ Builder
Pagina -162-
Métodos
Descrições
retângulo. Isto talvez envolva mudança de tamanho e/ou relação entre altura e
largura.
Para desenhar o gráfico em seu tamanho natural, use o método Draw.
Graphics podem ser bitmaps, ícones ou metafiles. se o gráfico é um objeto
TBitmap, o bitmap desenhado usando o valor de CopyMode.
O código a seguir estica o bitmap para preencher a área cliente de Form1.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Graphics::TBitmap* figura = new Graphics::TBitmap;
figura->LoadFromFile("c:\\Meu_Desenho.bmp");
// Canvas->Draw(10, 10, figura);
Canvas->StretchDraw(ClientRect, figura);
delete figura;
}
TextHeight, int __fastcall TextHeight(const AnsiString Text);
TextWidth
Retorna a altura (height), em pixels, de uma string desenhada na atual font. Use
TextHeight para determinar o height que uma string ocupa na imagem. Outros
elementos na imagem como linha, boxes, ou linhas adicionais de textos podem ser
posicionadas para acomodar o height do texto. TextHeight retorna o mesmo valor
de TextExtent(Text).cy.
Este exemplo exibe o height de um texto string na atual font do canvas num edit
box no form:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
int x = Canvas->TextHeight(" Curso de C++Builder");
Edit1->Text = String(x) + String(" pixels em height");
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int __fastcall TextWidth(const AnsiString Text);
Retorna a largura (width), em pixels, de uma string desenhada na atual font. Use
TextWidth para determinar o comprimento (length) que uma string ocupa numa
Apostila de C++ Builder
Pagina -163-
Métodos
Descrições
imagem. TextWidth indica se determinada string encaixa-se no espaço disponível.
Outros elementos gráficos na imagem como linhas, ou strings adicionais podem ser
posicionadas para acomodar a largura (width) do texto.
TextWidth retorna o mesmo valor que TextExtent(Text).cx.
Esse exemplo determina a largura (width) de uma string especificada, e se a string
é demasiadamente grande para ser exibida no Edit, o Edit é redimensionado para
acomodar a string. A string é exibida no edit box.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
long int t;
String str("Tutorial de C++Builder");
t = Canvas->TextWidth(str);
if(t > Edit1->Width)
Edit1->Width = t + 10;
Edit1->Text = str;
}
TextOut
void __fastcall TextOut(int X, int Y, const AnsiString Text);
Escreve uma string na tela, iniciando nos pontos X e Y, e envia o PenPos para o
fim da string. Use TextOut para escrever uma string na tela. A string pode ser
escrita usando a font atual, ou não. Use o método TextExtent para determinar o
espaço ocupado pelo texto na imagem. Para escrever somente o texto que ajusta
dentro do retângulo, use TextRect.
Este exemplo exibe uma string na posição especificada no form quando o usuário
clica o botão no form:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Canvas->Brush->Color = clBtnFace;
Canvas->Font->Name = "Courier New";
Canvas->TextOut(20, 100,
"Este curso C++Builder destina-se a facilitar o aprendizado de
C++ ");
}
TextRect
void __fastcall TextRect(const Windows::TRect &Rect,
int X, int Y, const
Text);
Apostila de C++ Builder
Pagina -164-
AnsiString
Métodos
Descrições
Escreve uma string dentro de um retângulo. Use TextRect para escrever uma string
dentro de uma limitada região retangular. Qualquer porção da string que ultrapasse
o retângulo passado no parâmetro é cortado e não aparece. O canto superior
esquerdo do texto é colocado no ponto (X, Y).
O código a seguir insere u texto num retângulo definido pelas coordenadas (10, 10)
e (100, 100).
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TRect TheRect;
TheRect.Top = 10;
TheRect.Left = 10;
TheRect.Bottom = 40;
TheRect.Right = 100;
Canvas->TextRect
(TheRect, 20, 20, String("Wilson e Milton... bons amigos!!!"));
}
Conhecendo arrays
As palavras matriz, array ou vetor são sinônimos utilizados para um mesmo tipo de dados utilizado na
linguagem C++ para representar uma coleção de variáveis do mesmo tipo, referenciadas por um mesmo
nome e identificadas através de um índice numérico.
Essas variáveis ficam alocadas seqüencialmente na memória, seguindo o sentido da primeira variável
(índice 0 - zero, o endereço mais baixo) para a última variável (índice n - endereço mais alto).
Para declarar uma matriz, devemos escrever um tipo, seguido do nome da matriz, seguidos pelo índice,
que nada mais é do que um número, indicando a quantidade de elementos de array, contido entre
colchetes.
A instrução:
char Abcdario[26];
Apostila de C++ Builder
Pagina -165-
declara uma matriz de vinte e seis ([26]) caracteres (char), cujo nome é Abcdario. Quando o compilador
se depara com essa declaração, ele reserva vinte e seis bytes (1 byte para cada char), enfileirados
seqüencialmente na memória do computador.
O exemplo a seguir usa matrizes para imprimir o abecedário num Label:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1->Caption = "";
char Abcdario[26];
for(int i = 0; i < 26; i++)
{
Abcdario[i] = 97 + i;
Label1 -> Caption = (String)Label1 -> Caption +
"Abcdario[" + i + "] = " + Abcdario[i] + '\n';
}
}
//---------------------------------------------------------------------------
As variáveis de uma matriz são numeradas a partir do zero (índice). No exemplo anterior,
char Abcdario[0]
é a primeira variável;
char Abcdario[1] é a segunda variável;
char Abcdario[2]
é a terceira variável;
...
char Abcdario[25]
é a última variável;
Para acessar uma variável determinada de um array, indicamos o nome do array e o índice; ou seja, o
número que está entre colchetes [ ] e indica a posição da mesma na memória.
No exemplo anterior (Abcdario[]), temos que:
Abcdario[0]
= a (1ª posição)
Abcdario[1]
= b (2ª posição)
Abcdario[2]
= c (3ª posição)
Abcdario[3]
= d (4ª posição)
Apostila de C++ Builder
Pagina -166-
Abcdario[4]
= e (5ª posição)
...
Abcdario[25]
= z (26ª posição)
Vamos reescrever o exemplo para acessar apenas um caracter:
//--------------------------------------------------------------------------// reescrito
void __fastcall TForm1::Button1Click(TObject *Sender)
{
char Abcdario[26];
for(int i = 0; i < 26; i++)
{
Abcdario[i] = 97 + i;
Label1 -> Caption = Abcdario[10];
}
}
//---------------------------------------------------------------------------
Agora o programa acessa apenas a 11ª variável, ou seja, a letra k.
Excedendo o limite de um array
Quando uma matriz é declarada, o espaço necessário em memória é calculado pelo compilador com base
no tipo da variável, multiplicado pelo índice: a declaração float Notas[26] faz que o compilador
reserve 104 bytes seqüenciais em memória para armazenar as 26 variáveis, visto que cada variável float
ocupa 4 bytes. Logo, 4 * 26 = 104.
A utilização de arrays traz uma uma responsabilidade a mais para o programador: a de verificar se o
limite da matriz não foi excedido, uma vez que a linguagem C++ não realiza esse tipo de verificação.
No exemplo anterior, se no código houvesse alguma instrução para que fosse escrito algum dado em
Notas[26], o fato de que não existe espaço reservado em memória para essa variável seria ignorado pelo
compilador que implementaria a instrução (lembre-se de que a primeira variável de vetores inicia-se com
o índice zero).
Uma vez que os valores do array estão armazenados seqüencialmente na memória, o compilador, sem
fazer qualquer verificação, colocará esse elemento imediatamente após Notas[25] (endereço mais alto do
exemplo).
Teoricamente, qualquer dado, instrução ou comando poderá estar ocupando esse endereço, o que poderá
produzir um resultado totalmente desastroso.
Apostila de C++ Builder
Pagina -167-
O exemplo a seguir, que
lógica de programação.
pode causar danos ao sistema, procura demonstrar esse tipo de erro na
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1->Caption = "";
char Abcdario[26];
for(int i = 0; i <= 26; i++)
{
Abcdario[i] = 97 + i;
Label1 -> Caption = (String)Label1 -> Caption +
"Abcdario[" + i + "] = " + Abcdario[i] + '\n';
}
}
//---------------------------------------------------------------------------
Matrizes multidimensionais
Até agora, temos visto vetores unidimensionais (pois apresentam apenas uma dimensão):
char Abcdario[26]; // um par de colchetes
Podemos, também, ter matrizes com duas ou mais dimensões:
bidimensionais: unsigned char Bid_Vetor[3][4]; // dois pares de colchetes
tridimensionais: int Trid_Array[2][3][2]; // três pares de colchetes
e assim por diante, cada par de colchetes representa uma dimensão.
Matrizes com mais de uma dimensão significa: os elementos da matriz são outras matrizes.
Considere uma matriz int bidimensional, cujos índices são [3] e [4]. Vejamos quais seriam as
coordenadas:
| (0 0), (0 1), (0 2), (0 3) |
| (1 0), (1 1), (1 2), (1 3) |
| (2 0), (2 1), (2 2), (2 3) |
Apostila de C++ Builder
Pagina -168-
As coordenadas das linhas localizam-se no lado esquerdo do interior dos parênteses e as coordenadas das
colunas localizam-se no lado direito do interior dos parênteses. Logo, na primeira linha, a coordena
horizontal são os zeros; na segunda, os um; na terceira, os dois. Na primeira coluna, a coordenada vertical
são os zeros; na segundo, os um; na terceira, os dois; e na quarta, os três.
Nós podemos inicializar as matrizes na mesma instrução onde a declaramos:
char
{ a,
{ e,
{ i,
};
Bid_Array[3][4] = {
b, c, d }
f, g, h }
j, l, m }
No exemplo acima, o caracter 'g' ocupa as coordenadas (1, 2).
Então, Bid_Array[1][2] == 'g'.
Nas matrizes de três dimensões, cada elemento é uma matriz de duas dimensões:
char
{
{ //
{ a,
{ e,
} //
Trid_Vetor[3][2][4] =
início do primeiro elemento[0]
b, c, d }, // primeira linha [0]
f, g, h } // segunda linha [1]
fim do primeiro grupo
{ // início do segundo grupo [1]
{ i, j, k, l }, // primeira linha [0]
{ m, n, o, p } // segunda linha [1]
} // fim do segundo grupo
{ // início do terceiro grupo [2]
{ q, r, s, t } // primeira linha [0]
{ u, v, x, z } // segunda linha [1]
} // fim do terceiro grupo
}; // fim da matriz
Na declaração char Trid_Vetor[3][2][4], o índice [3] declara o número de grupos; o índice [2], o
número de linha; e i índice [4], o número de colunas.
Logo,
Trid_Vetor[0][0][0]
Trid_Vetor[0][0][1]
Trid_Vetor[0][0][2]
Trid_Vetor[0][0][3]
Trid_Vetor[0][1][0]
Trid_Vetor[0][1][1]
Trid_Vetor[0][1][2]
Trid_Vetor[0][1][3]
==
==
==
==
==
==
==
==
'a'
'b'
'c'
'd'
'e'
'f'
'g'
'h'
e assim sucessivamente...
Apostila de C++ Builder
Pagina -169-
Outra maneira de dizermos:
'a', 'b', 'c', 'd', 'e', 'f', 'g'
e 'h' estão no primeiro grupo;
'i', 'j', 'k', 'l', 'm', 'n', 'o'
e 'p' estão no segundo grupo; e
'q', 'r', 's', 't', 'u', 'v', 'x'
e 'z' estão no terceiro grupo.
'a', 'b', 'c', 'd', i', 'j', 'k', 'l', 'q', 'r', 's'
e 't' estão na primeira linha; e
e', 'f', 'g', 'h', 'm', 'n', 'o', 'p', 'u', 'v', 'x'
e 'z' estão na segunda linha.
'a', 'e', 'i', 'n', 'q', 'u',
estão na primeira coluna;
'b', 'f', 'j', 'n', 'r', 'v',
estão na segunda coluna;
'c', 'g', 'k', 'o', 's', 'x',
estão na terceira coluna; e
'd', 'h', 'l', 'p', 't', 'z',
estão na quarta e última coluna.
Para projetar imagens na tela, o computador trabalha com as coordenas X e Y, dentro de uma matriz
bidimensional denominada Pixels. No Windows 98, se você der um clique com o botão direito do mouse
sobre a área de trabalho (Desktop) e escolher a opção Propriedades no menu PopUp que aparecer, poderá
acessar a aba Configurações na caixa que se abrir. No campo Área da tela, encontram-se as opções 640
por 480 pixels, 720 por 480 pixels, 800 por 600 pixels, 1024 por 600 pixels e 1024 por 768 pixels.
Basicamente o computador projeta imagens na tela preenchendo-a com pontos coloridos. Por exemplo, a
opção 800 por 600 pixels significa que serão colocados 800 pontos (pixels) na linha horizontal
(coordenada X) por 600 na vertical (coordenada Y). Logo podemos supor que esses pontos estão
localizados dentro da matriz Pixels de tamanho [800] por [600]: Pixels[800][600];
Através do exemplo abaixo podemos conhecer cada coordenada do Form, através do evento
OnMouseDown, e, de quebra, conhecer uma forma de mudar a cor do Hint através de parâmetros
passados para RGB():
//--------------------------------------------------------------------------void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
ShowHint = true;
Application->HintColor = RGB(X%255, Y%255, (X+Y)%255);
Hint = (String)"Coordenada X = " + X + '\n' + "Coordenada Y = " + Y
+"\n" + "RGB(" + X%255 + ", " + Y%255 + ", " + (Y+X)%255 + ")";
}
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -170-
Conforme dissemos, essas coordenadas são preenchidas com pontos coloridos que podem ser visualidas e
acessadas. O exemplo a seguir nos possibilita visualizar a cor do pixels que receber o evento mouse
down:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Form1->Color = clRed;
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
ShowHint = true;
Hint = ColorToString(Form1->Canvas->Pixels[X][Y]);
}
//---------------------------------------------------------------------------
Agora vamos criar uma matriz bi-dimensional que receberá o valor do RGB (cor) do local do form onde
ocorrer o evento mouse down:
//--------------------------------------------------------------------------void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
Form1->Color = clPurple;
short Ponto[800][600];
Ponto[X][Y] = ColorToRGB(Canvas->Pixels[X][Y]);
Label1->Caption = Ponto[X][Y];
}
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -171-
Arrays de caracteres
Podemos declarar e inicializar strings (arrays de caracteres) de mesma forma que fazemos com outros
tipos de matrizes.
Recapitulando um pouco, podemos entender uma constante caracter como sendo uma letra ou símbolo
colocado entre aspas simples: 'A', 'b', 'c', ']'. Também já sabemos que tais dados são armazenados
internamente pelo computador como um número inteiro entre 0 e 255.
Já uma seqüência de caracteres colocados entre aspas duplas constitui uma constante string, ou constante
de caracteres (no plural):
"Assis e Palmital"
"D. Pedro I deu um grito que ecôa até hoje!"
"A"
"b"
Essa "cadeia" ordenada de caracteres contém um caracter a mais do que parece ter, pois é encerrada com
o caracter nulo '\0'. Por exemplo:
char Nome[8] = "Therbio"; // visualmente Therbio possui apenas sete caracteres
Na memória a estrutura de armazenamento que se forma é:
Nome
T
h
e
r
b
i
o
\0
0
1
2
3
4
5
6
7
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
char Nome[ ] = "Therbio"; // não fornecemos um índice
Label1 -> Caption = sizeof(Nome);
}
//---------------------------------------------------------------------------
É diferente a interpretação que o compilador faz quando encontra um caracter entre aspas simples ou entre
aspas duplas:
Apostila de C++ Builder
Pagina -172-
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
Label1 -> Caption = "Tamanho de \'A\' = " + String(sizeof('A')) + " byte"
"\nTamanho de \"A\" = " + sizeof("A") + " bytes";
}
//---------------------------------------------------------------------------
Certamente você já percebeu que a diferença se dá em virtude do caracter NULL (nulo) colocado a mais
automaticamente pelo compilador.
Existem outras formas de declarar e inicializar constantes de caracteres:
int i[ ] = {0, 1, 2, 3, 4, 5, 6, 7}; // inteiros não precisam do caracter nulo
char ch[ ] = {'T', 'h', 'e', 'r', 'b', 'i', 'o', '\0'}; // essa não é a melhor
forma...
char* ch = "Que saudades do Rio Paraná!!!!"; // Ponteiros... logo os entenderemos!!!
Nessas declarações, [ ] significa "array de" e * significa "ponteiro para".
Obs. A expressão
char ch[3] = "Ola";
incorretamente declara uma matriz de índice três e a inicializa com os três caracteres 'O', 'l' e 'a' sem a
terminação usual com o caracter nulo, '\0'. Embora essa notação, algumas vezes, pareça funcionar, deve
ser evitada.
Uma forma sempre preferível é a notação:
char ch[ ] = "Ola";
pois inicializa a matriz com a mesma facilidade e implementa, automaticamente, a terminação usual com
o caracter nulo '\0'.
Se na inicialização fornecermos elementos a menos do que o índice comporta, o valor 0 será atribuído
inicialmente para os elementos restantes da matriz:
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
/*O exemplo imprime a string referente ao mês de março e demonstra o
preenchimento de alguns elementos restantes com 0*/
char nome [12] [10] = {"janeiro", "fevereiro", "março", "abril", "maio",
"junho", "julho", "agosto", "setembro", "outubro",
"novembro", "dezembro"};
Apostila de C++ Builder
Pagina -173-
Label1 -> Caption =
AnsiString(nome[2][0]) +
AnsiString(nome[2][1]) +
AnsiString(nome[2][2]) +
AnsiString(nome[2][3]) +
AnsiString(nome[2][4]) +
// até aqui, imprime março
int(nome[2][5]) +
// imprime zero
int(nome[2][6] + 'a') + // imprime 97
int(nome[2][6] + 1) + // imprime 1
int(nome[2][7] - 1); // imprime -1
}
//---------------------------------------------------------------------------
Estruturas
Conforme estudamos, uma matriz consiste num agrupamento de elementos do mesmo tipo, enquanto uma
struct (estrutura) consiste num agrupamento de tipos de dados arbitrários, denominados membros, sob um
único novo tipo e nome. Por exemplo:
struct dados
{
AnsiString Nome; // conforme a inicialização, aqui haverá um ERRO
char* rua;
int Numero;
String Cidade; // e aqui também.
char Estado[3]; // ou então aqui, se a inicialização for de outro tipo
int Cep;
long Idade;
float Peso;
}; // o ponto-e-vírgula é necessário
define um novo tipo de dados agrupados sob o nome dados, onde podemos anotar alguns dados de alguma
pessoa. Observe que existem itens que requerem diferentes tipos de dados. Enquanto alguns armazenarão
strings (sob suas diversas formas, escolhidas aleatoriamente), outros armazenarão números inteiros e, por
fim, o último, valores numéricos que poderiam conter uma fração.
Feito isso, podemos declarar variáveis do tipo dados como faríamos para declarar outra variável qualquer:
dados DeLima;
Acessamos os membros individuais através do operador . (ponto):
DeLima.Nome = "Therbio";
DeLima.Rua = "Rua Central";
DeLima.Numero = 163;
Podemos inicializar estruturas da mesma forma que inicializamos um array:
struct dados // define uma estrutura
{
char *Nome; // sem problemas
Apostila de C++ Builder
Pagina -174-
char rua[12]; // não esquecer de colocar o índice
int Numero;
char *Cidade;
char Estado[3];
int Cep;
long Idade;
float Peso;
}; // o ponto-e-vírgula é necessário
// inicializa a estrutura igual a uma matriz
dados DeLima =
{
"Therbio", // essa inicialização não trabalha com AnsiString
"Rua Central",
163,
"Ibirarema", // também não trabalha com String
"SP",
7073,
40,
91.6
};
// para imprimir num label, precisamos converter os dados em AnsiString ou String
Label1 -> Caption = AnsiString(DeLima.Nome) + '\n' +
String(DeLima.rua) + '\n' +
AnsiString(DeLima.Numero) + '\n' +
String(DeLima.Cidade) + '\n' +
AnsiString(DeLima.Estado) + '\n' +
AnsiString(DeLima.Cep) + '\n' +
AnsiString(DeLima.Idade) + '\n' +
String(DeLima.Peso);
Porém, esse tipo de inicialização acima tem dificuldade para inicializar dados do tipo AnsiString.
Eis outra forma de inicializar os membros individuais através do operador . (ponto):
struct dados // define uma estrutura
{
AnsiString Nome;
char *rua; // sem problemas
int Numero;
char *Cidade; // não trabalha com dados do tipo ch[ ] ou ch[i]
String Estado;
int Cep;
long Idade;
float Peso;
}; // o ponto-e-vírgula é necessário
dados DeLima; // declara variável do tipo dados
// inicializa as variáveis que trabalha com AnsiString e String
DeLima.Nome = "Therbio";
DeLima.rua = "Rua Central";
DeLima.Numero = 163;
DeLima.Cidade = "Ibirarema";
DeLima.Estado = "SP";
Apostila de C++ Builder
Pagina -175-
DeLima.Cep = 07073;
DeLima.Idade = 40;
DeLima.Peso = 91.6;
// ok. Nesse exemplo não precisamos converter pra String ou AnsiString
Label1 -> Caption = DeLima.Nome + '\n' +
DeLima.rua + '\n' +
DeLima.Numero + '\n' +
DeLima.Cidade + '\n' +
DeLima.Estado + '\n' +
DeLima.Cep + '\n' +
DeLima.Idade + '\n' +
DeLima.Peso;
Observação:
Ainda que duas struct possuam os mesmos membros:
struct aluno
{
char* Nome;
int Nota;
float media;
};
struct __aluno
{
char* Nome;
int Nota;
float media;
};
elas sempre serão diferentes tipos de dados entre si. Portanto a declaração:
aluno Paulo;
__aluno Paulo_Jose = Paulo;
fará com que o compilador acuse um erro, uma vez que se trata de tipos incompatíveis entre si:
[C++ Error] Unit 1.cpp(36): E2034 Cannot convert 'aluno' to '__aluno'.
Ponteiros
Podemos entender um microcomputador como um sistema de cinco unidades de funcionamento: unidade
de entrada (teclado, mouse, drive de CD-ROM, drive de disquetes etc), unidade de saída (impressora,
monitor etc), unidade de memória (memória RAM -escrita e leitura-, memória ROM - leitura), e as
unidades aritmética e lógica que se encontram agrupadas na CPU (Unidade Central de Processamento, o
processador).
O chip responsável pelo controle de todo o computador é o processador. Outro circuito de extrema
importância é a memória RAM, que podemos imaginar como um grupo de células usadas para
Apostila de C++ Builder
Pagina -176-
armazenamento temporário das instruções e dos dados que são acessados e processados pelo
microprocessador em altíssima velocidade. Trata de uma memória volátil pois seus dados perdem-se no
momento em que são desligadas, o que não chega a ser um problema, visto que esses dados, de regra, após
salvos, ficam guardados em algum disco de armazenamento permanente, como os discos rígidos ou os
disquetes, sendo copiados novamente para a memória na ocasião de seu re-processamento.
A memória RAM é constituída por uma imensa seqüência de células de armazenamento (localizações)
com o tamanho de oito bits (um byte) cada, o que permite que cada uma dessas localizações possa assumir
um entre 256 valores diferentes. Ressalte-se, ainda, que cada célula possui um endereço único e
inconfundível, expresso por um valor numérico que define a exata localização desse byte, bem como que,
apesar do limitado tamanho de cada célula, podemos acessar dois bytes consecutivos (word) ou quatro
bytes consecutivos (doubleword) simultaneamente com um único endereçamento.
Conforme exposto, durante a execução de um programa, as instruções e os dados processados ficam
armazenados na memória do computador. Cada informação é representada por certo grupo de bytes (char
- 1 byte, float - 4 bytes, double - 8 bytes etc) e possui um local determinado na memória, um endereço que
pode ser expressado por um valor hexadecimal. Não há necessidade de o programador conhecer o
endereço absoluto de cada dado, pois cabe ao compilador relacionar o nome de cada variável com sua
posição na memória.
Em muitas ocasiões é preferível trabalhar com os endereços das variáveis a acessá-las pela maneira
convencional. A linguagem C++ dispõe do operador unário & que permite que os ponteiros (do inglês
pointer), um tipo especial de variável, armazenem o endereço de outras variáveis.
Considerando-se o poder que os ponteiros representam na linguagem, a compreensão e o uso correto
desses recursos são fundamentais para a criação de muitos programas em C++.
Podemos citar vários motivos para isso: são a única forma de implementar determinadas operações;
produzem código compacto robusto e eficiente; constituem ferramenta bastante poderosa para
manipulação da informação ou de elementos de arrays; constituem um meio para que as funções possam
realmente modificar os argumentos da função chamadora; são usados na alocação e desalocação da
memória do sistema; são usados para passar strings de uma função para outra; podem ser usados no lugar
de arrays, o que proporciona aumento de eficiência.
Mas nem tudo é simples na utilização dos ponteiros. Em primeiro lugar, a sintaxe de ponteiros pode ser
nova para muitos programadores de outras linguagens que estão iniciando seus estudos em C++; e, para
complicar, o uso incorreto ou descuidado dos ponteiros pode causar o travamento imediato do programa,
do sistema ou algo pior, como, por exemplo, a formatação do disco rígido.
Conceitualmente, ponteiro é uma variável que contém o endereço de localização na memória de outro
objeto. Normalmente, esse endereço é a localização de alguma variável declarada no programa. Então
dizemos que o ponteiro aponta para determinada variável. Logo também é correto chamá-lo de
apontador.
Declaração
Apostila de C++ Builder
Pagina -177-
Quando realizamos operações com ponteiros, basicamente desejamos conhecer o endereço de uma
variável e manipular o seu conteúdo. Para tanto, C++ nos fornece dois operadores especiais, o operador
de endereços & (determina o primeiro byte do bloco ocupado pela variável) e o operador indireto de
conteúdos * (retorna o conteúdo, ou seja, o valor do dado armazenado no endereço apontado).
Como qualquer variável, os apontadores precisam ser declarados. Ao declará-los, tomemos o cuidado de
observar o tipo do bloco que será apontado. Por exemplo, se queremos um ponteiro apontando para uma
variável char (bloco de 1 byte), devemos declará-lo como char; se o queremos apontando para uma
variável int (bloco de 2 (ou 4) bytes, conforme a máquina), também devemos declará-lo como int, e assim
por diante.
A declaração de um ponteiro é parecida com a declaração de outra variável qualquer. Eis a sintaxe:
tipo *nome;
onde tipo é o grupo a que pertence a variável e nome é o nome que escolhemos para a variável. Exemplo:
char *ch;
int *pi;
float *flot;
O operador unário *, mais conhecido como operador indireto, nos permite acessar o conteúdo do objeto
apontado; permite, também, que o compilador saiba que a variável guardará um endereço, e não outro
dado qualquer.
Quando declaramos um ponteiro, devemos inicializá-lo com algum "valor" que, de regra, será o
endereço de alguma variável. Conforme vimos, C++ dispõe do operador de endereço & que permite que
um apontador guarde o endereço da variável a qual se refere:
tipo *nome = &nome1;
onde nome1 é o nome da variável apontada:
char c;
int p;
float flt;
Apostila de C++ Builder
Pagina -178-
char *ch = &c;
int *pi = &p;
float *flot = &flt;
Para compreendermos melhor, vamos supor que a variável inteira p seja inicializada com o valor 257 (int
p = 257;) e que esteja localizada no endereço 0065FDF8. Então, após a atribuição do ponteiro (int *pi
= &p;), pi estará apontado para o endereço de memória 0065FDF8 e *pi será igual a p, ou seja, seu
valor será exatamente os mesmos 257.
Veja as instruções:
int *p, p2;
float* flot, *flot2;
A primeira instrução declara um ponteiro p (para int) e uma variável p2 do tipo int. Já a segunda
instrução declara dois ponteiros (flot e flot2) do tipo float. Observe o operador indireto * determinando
os elementos da lista que serão ponteiros. Observe também que os três ponteiros ainda não foram
inicializados, o que significa que eles apontam para um endereço qualquer da memória do micro. Um
ponteiro, nessas circunstâncias, pode acarretar sérios problemas. Se não existir nenhum "valor" para
inicializar o ponteiro, devemos inicializá-lo com 0 (zero), o que indicará que o ponteiro não se refere a
nenhum objeto:
tipo *nome = 0;
Vamos escrever um simples programa Console Wizard abordando o tema:
#include <iostream>
#include <conio>
using namespace std;
main()
{
int i = 97;
//variável int
int *Pi = &i;
//ponteiro para int
int *Pi2 = 0;
//Ponteiro apontando para null
cout << "\n\tConteudo via variavel = " << i;
cout << "\n\tEndereco via variavel = " << &i;
cout << "\n\tConteudo via ponteiro = " << *Pi;
cout << "\n\tEndereco via ponteiro = " << Pi;
cout << "\n\n";
int i2 = 98;
//variável int
Pi2 = & i2;
//inicializa Pi2 com endereço de i2
cout << "\n\tAgora podemos acessar *Pi2 ... "
Apostila de C++ Builder
Pagina -179-
<< *Pi2 << "\n";
getch();
//exibe o conteúdo de i2 via ponteiro
}
Veja um exemplo equivalente no ambiente visual do C++Builder:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
int i = 97;
//variável int
int *Pi = &i;
//ponteiro para int
int *Pi2 = 0;
//Ponteiro apontando para null
Label1 -> Caption = "\tConteúdo via variável = " + String(i) +
"\n\tEndereço via variável = " + IntToHex(int(&i), 8) +
"\n\tConteúdo via ponteiro = " + *Pi +
"\n\tEndereço via ponteiro = " + IntToHex(int(Pi), 8);
int i2 = 98;
//variável int
Pi2 = & i2;
//inicializa Pi2 com endereço de i2
Label2 -> Caption = "\tAgora podemos acessar *Pi2 ... " +
String(*Pi2);
//exibe o conteúdo de i2 via ponteiro
}
//---------------------------------------------------------------------------
O código abaixo exibe o endereço da variável:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
double r = 65;
Label1 -> Caption = IntToHex(int(&r), 8);
}
//---------------------------------------------------------------------------
Os exemplos acima ilustram bem o fato de que toda variável possui um endereço e de que, mesmo sem
saber o endereço específico da variável, podemos armazenar esse endereço em um ponteiro.
A reutilização de um ponteiro
Uma vez declarado e inicializado, podemos redefinir o conteúdo de um ponteiro indefinidamente. Basta
atribuirmos um novo endereço para o ponteiro, descartando o endereço anterior (que será perdido pelo
ponteiro). O exemplo a seguir usa um Label no Form. Conforme o evento do mouse, (down, move ou
up), um ponteiro apontará para variáveis diferentes, exibindo os valores respectivos no Label:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -180-
#pragma package(smart_init)
#pragma resource "*.dfm"
int i_1 = 100; // declara e inicializa variável int global
int *P_i = 0; // declara e inicializa com 0, ponteiro para int
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label1MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
P_i = &i_1; // atribui o endereço0 de i_1 ao ponteiro P_i
Label1 -> Caption = *P_i; // exibe o conteúdo da variável (100) via ponteiro
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label1MouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
int i_2 = 200; // declara e inicializa variável int local
P_i = &i_2; // atribui o endereço da variável local ao ponteiro
Label1 -> Caption = *P_i; // exibe o conteúdo da variável (200) via ponteiro
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label1MouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
int i_3 = 300; // declara e inicializa variável int local
P_i = &i_3; // atribui o endereço da variável local ao ponteiro
Label1 -> Caption = *P_i; // exibe o conteúdo da variável (300)via ponteiro
}
//---------------------------------------------------------------------------
Ponteiros apontando para outros ponteiros
Como sabemos, um ponteiro pode apontar para qualquer tipo de variável.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Alvo = "AnsiString é uma variável ou uma classe?";
AnsiString *Aponta_Alvo = &Alvo;
Edit1 -> Text = *Aponta_Alvo;
}
Ele pode, inclusive apontar para outro ponteiro:
Apostila de C++ Builder
Pagina -181-
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
char Alvo[ ] = "Alvo[ ] é uma matriz ou um ponteiro?"; // logo saberemos
char *Aponta_Alvo = &*Alvo; // igual a: char *Aponta_Alvo = &Alvo[0];
Edit1 -> Text = *Aponta_Alvo; // experimente:
Edit1 -> Text = Aponta_Alvo;
}
//---------------------------------------------------------------------------
A notação acima pode parecer estranha. Ocorre que o nome de um array é um ponteiro que aponta para o
primeiro elemento da matriz (conforme explicado em outra seção mais adiante). Logo, usamos essa
característica para montar o exemplo acima, onde um ponteiro aponta para outro ponteiro.
veja um exemplo, usando três ponteiros:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
char *ch = "testando ponteiros";
char **ch_Ptr = &ch;
char ***Ptr_ch_Ptr = &ch_Ptr;
Label1 -> Caption = "*ch = " + String(*ch) + '\n' + "ch = " + ch + '\n' + '\n' +
"**ch_Ptr = " + **ch_Ptr + '\n' + "*ch_Ptr = " + *ch_Ptr + '\n' + '\n' +
"***Ptr_ch_Ptr = " + ***Ptr_ch_Ptr + '\n' +
"**Ptr_ch_Ptr = " + **Ptr_ch_Ptr;
}
//---------------------------------------------------------------------------
Ponteiros apontando para structs
Diferente do que ocorre com matrizes (das quais podemos acessar seus elementos individuais por meio de
índices), não podemos usar valores numéricos para acessar os membros de uma estrutura com base em sua
ordem. Isso ocorre porque, enquanto todos os elementos de uma matriz são constituídos de um mesmo
tipo de dado, os de uma estrutura podem ser constituídos de dados de tipos diferentes.
Uma vez que cada membro da estrutura possui um nome simbólico, podemos usá-lo no acesso aos dados,
separado do nome do membro através do operador . (ponto - que significa "membro de"):
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
struct Endereco
{
AnsiString Nome;
int idade;
Apostila de C++ Builder
Pagina -182-
}Marta; // outra forma de declarar uma variável struct
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
/*inicializa variáveis na criação do Form*/
Marta.Nome = "Marta C. Plaça";
Marta.idade = 36;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
(*Label1).Caption = /*Breve você entenderá essa notação*/
Marta.Nome + '\n' + Marta.idade;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Label2Click(TObject *Sender)
{
Marta.Nome = "Marta C. Plaça Alves"; // casou!!!
Marta.idade = 432; // se for variável, aceita estes novos valores
Label1 -> Caption = "Será que Marta.Nome e Marta.idade são variáveis?";
(*Label2).Caption = Marta.Nome + '\n' + Marta.idade + " meses";
}
//---------------------------------------------------------------------------
A notação de ponteiros para estruturas segue um procedimento semelhante, com duas diferenças:
1 - Substituição do nome da estrutura pelo nome do ponteiro;
2 - Substituição do operador . "membro de" pelo operador -> ("aponta para"), que é formado por um hífen
“ - ” mais o símbolo "maior que “ > ”:
No exemplo anterior, altere o evento OnClick, apenas, de Label2:
void __fastcall TForm1::Label2Click(TObject *Sender)
{
Endereco *PtrNome = &Marta, *PtrIdade = &Marta;
(*Label2).Caption =
PtrNome -> Nome + '\n' +
(*PtrIdade).idade; /* Ops!! Parece que já vi essa notação*/
}
Você já deve ter entendido. Veja:
Label1 -> Caption é igual a (*Label1).Caption, assim como, obviamente, PtrNome -> Nome será igual a
(*PtrNome).Nome, então essas notações têm muito em comum!
Apostila de C++ Builder
Pagina -183-
Por aí, podemos perceber a grande importância que os ponteiros representam na linguagem C++,
especialmente no C++Builder. Você, mesmo sem consciência, já tem trabalhado com esses conceitos ora
apresentado, desde os nossos primeiros exemplos. Um completo domínio sobre ponteiros, pode fazer a
diferença para qualquer pessoa que se aventure nesse ramo da programação.
O nome do array
Veja o seguinte trecho de código:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
char Abcdario[26];
for(int i = 0; i < 26; i++)
{
Abcdario[i] = 65 + i;
Label1 -> Caption = Abcdario[0];
}
}
Ao ser executado, o programa imprimirá o caracter 'A' no label. Como sabemos, os elementos de um vetor
ficam enfileirados seqüencialmente na memória do computador. Supondo que Abcdario[0], o primeiro
elemento da matriz, ocupe a posição 651256 na mémória, Abcdario[1] ocuparia a posição 651257;
Abcdario[2], a posição 651258; Abcdario[3], a posição 651259; Abcdario[4], a posição 65125A; e
assim sucessivamente.
Veja agora o próximo exemplo, usando o nome da matriz, que também imprime 'A' no Label:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
char Abcdario[26] = {65, 66, 67, 68, 69}; // A, B, C, D, E - na tabela ASCII
Label1 -> Caption = *Abcdario;
}
Pergunta: Se não colocamos o índice, porque essa aplicação imprime o caracter 'A' no Label? E porque
usamos a notação de ponteiros?
Resposta: Simplesmente porque o nome de um array representa o seu endereço de memória, sendo que tal
endereço é o do elemento que ocupa o índice 0 (zero) da matriz. Ou, de um modo mais objetivo: o nome
de um array é um ponteiro que aponta para o primeiro elemento da matriz.
Raciocine. Se
Label1 -> Caption = Abcdario[0]; // imprime 'A'
equivale a
Apostila de C++ Builder
Pagina -184-
Label1 -> Caption = *Abcdario; // imprime 'A'
então,
Label1 -> Caption = Abcdario[1]; // imprime 'B'
equivale a
Label1 -> Caption = *(Abcdario + 1); // imprime 'B'
e
Label1 -> Caption = Abcdario[2]; // imprime 'C'
equivale a
Label1 -> Caption = *(Abcdario + 2); // imprime 'C'
e assim por diante.
Considere a seguinte declaração:
void __fastcall TForm1::Label1Click(TObject *Sender)
{
int i = 6;
char Abcdario[26] = {65, 66, 67, 68, 69, 70, 71};
Label1 -> Caption = Abcdario[i]; // imprime 'G'
Label2 -> Caption = *(Abcdario + i); // imprime 'G'
}
do exposto temos que a expressão *(Abcdario + i) sempre terá o mesmo valor de Abcdario[i].
Vamos, agora, montar dois exemplos que imprimem os caracteres 'C', 'D', 'E', 'F' e 'G':
int i = 2; /* cada valor de i determina inícios de impressão de diferentes*/
{
char Abcdario[26] = {65, 66, 67, 68, 69, 70, 71};
Label1 -> Caption = Abcdario + i; /* se i = 0, Label1 -> Caption = Abcdario;
*/
} // fim do primeiro exemplo
{
char Abcdario[26] = {65, 66, 67, 68, 69, 70, 71};
Label2 -> Caption = &Abcdario[i];
} // fim do segundo exemplo
Agora temos, também, que Abcdario + i é equivalente a &Abcdario[i].
Vejamos, agora, um exemplo onde um ponteiro é declarado e inicializado apontando para um vetor:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1 -> Caption = "";
Apostila de C++ Builder
Pagina -185-
char Ch[] = "O Osmarino é um grande amigo de infância";
char *ApontCh;
ApontCh = &Ch[0];
for(int i = 0; i < 42; i++)
{
Label1 -> Caption = Label1 -> Caption + *ApontCh;
ApontCh++;
}
}
Quando estudamos arrays de caracteres, aprendemos algumas formas de declarar e inicializar constantes
de caracteres, entre elas:
char* ch = "Que saudades do Rio Paraná!!!!"; // Ponteiros... logo os entenderemos!!!
Agora já possuímos conceitos suficientes para entende o porquê de tal declaração. Veja um exemplo:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
char *ch = "Que saudades do Rio Paraná!!!!"; // Ponteiros... logo os entenderemos!!!
Label1 -> Caption = *ch; // imprime: Q
Label2 -> Caption = ch; // imprime: Que saudades do Rio Paraná!!!!
Label3 -> Caption = *ch + 1; // imprime: 82
Label4 -> Caption = ch + 1; // imprime: ue saudades do Rio Paraná!!!!
Label5 -> Caption = *(ch + 1); // imprime: u
}
Conclusão: Na linguagem C++, o relacionamento entre arrays e ponteiros é muito grande, a ponto de, sob
muitos aspectos, poderem ser tratados juntos.
Variáveis dinâmicas
Quando um programa é executado, o sistema operacional reserva um espaço de memória para o código
(ou instruções do programa) e outro espaço para as variáveis usadas durante a execução. Grosso modo,
esses espaços ocupam uma mesma região, que podemos denominar memória local. Também existem
outras zonas de memória, como a pilha, usada, entre outras coisas, para realizar o intercâmbio de dados
entre as funções. O resto, a memória que não estiver em uso por nenhum programa, é o que se conhece
por memória livre (área de alocação dinâmica, heap ou free store). Quando um programa usa a área de
alocação dinâmica, naturalmente estará usando parte desse resto de memória.
O maior poder esperado na utilização de ponteiros decorre, justamente, de seu uso junto a esse conceito de
alocação de memória no free store.
C++ dispõe de dois operadores para acesso à memória dinâmica: new e delete.
Apostila de C++ Builder
Pagina -186-
Operador new:
O operador new oferece um meio para alocação de espaço na memória livre, de uma forma parecida,
porém muito superior àquela alocação oferecida pela função malloc() da livraria padrão da linguagem C.
O operador new precisa, necessariamente, ser suprido com o tipo de dados para o qual está alocando
memória, a fim de que o compilador saiba exatamente quanta memória deverá reservar para colocar os
dados no heap. Por exemplo:
new double
aloca 8 bytes, suficientes para conter um double.
O operador new retorna um endereço de memória a ser atribuído a um ponteiro:
double *dbl = new double;
Feito isso, o que acontece é que dbl passará a apontar para uma variável double na área de alocação
dinâmica. Logo, podemos inserir algum valor nessa variável:
*dbl = 563.9082;
Se a reserva de memória não for bem sucedida, o operador new devolverá um ponteiro nulo (NULL).
Sintaxe (para tipos pré-definidos – baseado no Help do BCB):
<::> new <placement> type-name <(initializer)>
<::> new <placement> (type-name) <(initializer)>
Na sintaxe acima, os símbolos < > significam que os dados inseridos não são obrigatórios. Ex:
placement. Continuemos:
:: new placement tipo ( inicializador) // char *ch = :: new char ('A');
:: new placement (tipo) (inicializador) // char *ch = new (char) (65);
Parênteses envolvendo o tipo é opcional. Os argumentos opcionais podem ser os seguintes:
:: , placement e ( inicializador)
O operador ::, relacionado com a sobrecarga de operadores, executa a versão global de new.
O mesmo se aplica a placement, que pode ser usado para fornecer argumentos adicionais para new. Mas
você poderá usar essa sintaxe somente se você tiver uma versão sobrecarregada de new que seja
compatível com os argumentos opcionais.
Apostila de C++ Builder
Pagina -187-
O inicializador, se aparece, é usada para assinalar valores iniciais para a memória reservada com new,
mas não pode ser usado com arrays.
Um pedido para uma alocação para um dado não-array usa apropriadamente o operador new(). Qualquer
pedido, porém, para alocação array deve chamar o apropriado operador new[]:
:: new tipo[ ]
Alocação de memória para um objeto não-array é feito pelo uso de ::new(). Note que essa alocação é
sempre usada para tipos pré-definidos. Ela também é possível para sobrecarga de operador de função
global. De qualquer forma, isso geralmente não é aconselhável.
Nota: Classes de arrays requerem um construtor default.
Há uma regra de ouro, quando se usa a área de alocação dinâmica:
"toda memória reservada durante a execução do programa deve ser liberada antes do encerramento do
programa".
A memória é liberada através do operador delete.
Vejamos um exemplo:
//--------------------------------------------------------------------------void __fastcall TForm1::Label1Click(TObject *Sender)
{
int *a;
char *b = new char;
float *c = new float (123.4);
struct stPunto {
double e,f;
} *d;
a = new int;
d = new stPunto;
*a = 65;
*b = 65;
d->e = 123.456; d->f = 0.5;
Label1 -> Caption = "a = " + String(*a) + '\n' +
"\nb = " + *b + '\n' +
"\nc = " + *c + '\n' +
"\nd = (" + d->e + "
" + (*d).f + ")";
delete a;
delete b;
delete c;
delete d;
}
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -188-
Não seguir a regra de liberar a memória alocada antes do encerramento do programa é uma atitude
extremamente irresponsável, e na maior parte dos casos acarretará conseqüências desastrosas, tais como:
Tenha muito
cuidado: se um ponteiro
perde uma variável reservada dinamicamente (perda de memória), esta não poderá mais ser liberada.
Exemplo:
void __fastcall
{
int *a;
a = new int; //
*a = 10;
a = new int; //
*a = 20;
delete a; // só
TForm1::Button1Click(TObject *Sender)
variável dinâmica
nova variável dinâmica. Perde-se a anterior
é possível liberar a última reserva
}
Neste exemplo vemos como é impossível liberar a alocação de memória feita em primeiro lugar. Se não
necessitamos mais dela, deveríamos tê-la liberado antes de reservá-la outra vez, e se ainda necessitamos,
devemos guardar sua posição, por exemplo com outro ponteiro.
Operador delete:
Como sabemos, em C++, devemos desalocar a memória usada por qualquer coisa que tenha sido criada
pelo operador new. Existe uma exceção a essa regra que é o filho de um form MDI, criado como filho de
um parent MDI, pois esses objetos serão deletados, automaticamente, pelo sistema quando do
encerramento do programa. Logo, os blocos de memória reservados com o operador new serão válidos até
que sejam liberados com delete ou, em certos casos, até o fim do programa. Mas é sempre aconselhável
liberar-se a memória reservada com new usando delete.
Sintaxe: ::
<::> delete <cast-expression>
<::> delete [ ] <cast-expression>
delete <array-name> [ ];
Acima adotamos a mesma regra usada na sintaxe de new:
Apostila de C++ Builder
Pagina -189-
::delete expressão //expressão normalmente será um ponteiro.
:: delete [ ] expressão // delete[] expressão é usado para arrays dinamicos
como sabemos, em muitos casos, :: é opcional.
Quando se usa o operador delete com um ponteiro nulo, nenhuma ação será realizada. Essa
característica permite usar o operador delete com ponteiros sem a necessidade de se perguntar antes se
o mesmo é nulo.
int *i = new (int) ('\0');
delete i;
Nota: Os operadores new e delete são próprios de C++. Em C, usamos as funções malloc e free para
reservar e liberar memória dinâmica. Liberar um ponteiro nulo com free pode provocar conseqüências
desastrosas.
Em tópicos mais avançados abordaremos arrays dinâmicos (alocados através de new e liberados através
de delete).
Mais problemas ... ponteiros soltos
Outra forma de se pedir problemas, é tentar reutilizar um ponteiro cujo espaço apontado na memória foi
liberado pelo operador delete, sem antes atribuir um novo endereço ou uma nova variável ao ponteiro:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
int * i = new int (100); // cria variável dinâmica
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Label1 -> Caption = *i; // código implementado na criação de Form1
}
Apostila de C++ Builder
Pagina -190-
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
/*************deleta a variável dinâmica.
**************Se você clicar Button1 e ENCERRAR O PROGRAMA
**************sem clicar Button2,
**************ou clicar Button2 antes de clicar Button1,
**************não haverá bug
*******************************************************/
delete i;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
/********* SE VOCÊ JÁ DEU UM CLIQUE EM Button1********
*********E CLICAR Button2, O BICHO VAI PEGAR**********/
*i = 200;
/* coloca um novo valor no endereço apontado pelo ponteiro...
... só que, se Button1 foi já clicado, não existirá uma variável no
endereço para receber o novo valor... então!!!!! */
Label1 -> Caption = *i;
}
//---------------------------------------------------------------------------
O melhor resultado produzido nos meus testes pelo exemplo acima foi o seguinte:
Vamos entender o que aconteceu. A instrução:
int * i = new int (100);
de certa forma, combina várias instruções numa só, pois, na verdade, cria um ponteiro:
Apostila de C++ Builder
Pagina -191-
int * i
uma variável:
new int
e atribui um valor a essa variável:
int (100);
Só que essa variável não possui um nome próprio. Observe que o ponteiro não foi criado por new. O
operador new criou apenas a variável int inominada. Logo o ponteiro não estará sujeito às regras de
criação e destruição new / delete, visto ter sido criado naturalmente pelo programa, como outro ponteiro
qualquer.
Para melhor compreensão, veja a mesma instrução montada de forma diferente:
int * i; // cria naturalmente um ponteiro
i = new int; // cria variável dinâmica e atribui seu endereço ao ponteiro
*i = 100; // inicializa a variável, via ponteiro
Podemos acessar e modificar essa variável inominada através do ponteiro * i. Quando chamamos o
operador delete ele destrói apenas a variável criada pelo operador new, desalocando a memória
respectiva. A partir desse momento, o sistema fica livre para colocar qualquer tipo de dado nesse
endereço. O ponteiro, que não foi destruído pelo operador delete, visto que não foi criado por new,
continua existindo e apontando para o mesmo endereço.
Então, tentar usar esse ponteiro poderá ocasionar o imediato travamento do programa, ou a derrubada de
todo o sistema. Ou pior, o programa poderá continuar funcionando normalmente, e só travar
posteriormente, o que tornará bastante difícil a solução do bug.
Vamos refazer o exemplo, retirando o bug através da colocação de uma nova variável no endereço:
Nota: No exemplo abaixo, primeiro dê um clique em Button1, e depois em Button2. E depois, para liberar
a memória, clique novamente em Button1 antes de encerrar o programa.
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
int * i = new int (100);
TForm1 *Form1;
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -192-
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
Label1 -> Caption =
"valor de i =
" + String(*i) +
"\nendereço =
" + IntToHex(int(&*i), 8);
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
delete i;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
i = new int;
*i = 200;
Label1 -> Caption =
"valor de i =
" + String(*i) +
"\nendereço =
" + IntToHex(int(&*i), 8);
// NÃO ESQUEÇA DE DESALOCAR A MEMÓRIA,
// CLICANDO NOVAMENTE (Button1Click)
// ANTES DE ENCERRAR O PROGRAMA
}
//---------------------------------------------------------------------------
Ao rodar o exemplo, você observará que o ponteiro continuará apontando sempre para o mesmo endereço
de memória. Poderíamos, também, retirar o bug fazendo com que o ponteiro apontasse para outro
endereço. Para isso alteraríamos apenas o código de Button2Click:
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
int j = 1000; // essa variável não foi criada por new
i = &j;
Label1 -> Caption = "valor de i =
" + String(*i) +
"\nendereço =
" + IntToHex(int(&*i), 8);
}
//---------------------------------------------------------------------------
Ponteiros & constantes
Veja algumas declarações usando const com ponteiros, e tente visualizar as possíveis diferenças de
resultados, em virtude da posição do prefixo const:
AnsiString * const Const_Str = &Str; // o ponteiro é constante
const int* Const_i = &i; // a variável é constante para *Const_i
Apostila de C++ Builder
Pagina -193-
const double
alterados
* const Const_dbl = &dbl; // Nem Const_i, nem *Const_i podem ser
Vejamos agora um exemplo com respectivos comentários abordando cada situação:
//--------------------------------------------------------------------------#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//--------------------------------------------------------------------------#pragma package(smart_init)
#pragma resource "*.dfm"
//////////////GRUPO DO PONTEIRO CONSTANTE //////////////////
AnsiString Str =
"Testando Ponteiros e Constantes";
AnsiString Str_1 =
"Um novo endereço não pode ser atribuído"
" a um ponteiro const";
AnsiString * const Const_Str = &Str; // ponteiro const para Str
////////////////////////////////////////////////////////////////////////////////
////////////GRUPO DO DADO APONTADO CONSTANTE //////
int i = 100; // obs. i pode ter seu valor alterado, mas não por *Const_i
int i_1 = 200;
const int *Const_i = &i; // o ponteiro (*Const_i) não consegue alterar i
////////////////////////////////////////////////////////////////////////////////
// PONTEIRO E VARIÁVEL CONSTANTES VIA PONTEIRO
double dbl = 0.123456789; // dbl é variável, mas não via ponteiro *Const_dbl
double dbl_1 = 9.876543210;
const double * const Const_dbl = &dbl; // Const_dbl e *Const_dbl constantes
////////////////////////////////////////////////////////////////////////////////
TForm1 *Form1;
//--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------void __fastcall TForm1::FormCreate(TObject *Sender)
{
// eventos na criação de Form1
Label1 -> Caption = *Const_Str;
Label2 -> Caption = *Const_i;
Label3 -> Caption = *Const_dbl;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
Apostila de C++ Builder
Pagina -194-
{
// altera a variável via ponteiro. Ok
*Const_Str = "alterando a variável via ponteiro - operação permitida.";
Label1 -> Caption = *Const_Str;
Const_i = &i_1; // altera endereço apontado via ponteiro. Ok
Label2 -> Caption =
"Alterando endereço via ponteiro. "
"Operação permitida. *Const_i =
"
+ String() + *Const_i;
Const_i = &i; // volta apontar para a variável inicial
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender)
{
// tenta redirecionar ponteiro const para outra variável... ERRO!!!!
Const_Str = &Str_1; // [C++ Error] Unit1.cpp(57): E2024 Cannot modify a const object.
Label1 -> Caption = *Const_Str;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button3Click(TObject *Sender)
{
// tenta alterar variável const para ponteiro ... ERRO!!!!
*Const_i = 400; // [C++ Error] Unit1.cpp(63): E2024 Cannot modify a const object.
Label2 -> Caption = *Const_i;
}
//--------------------------------------------------------------------------void __fastcall TForm1::Button4Click(TObject *Sender)
{// tenta alterar variável / ponteiro const e redirecionar ponteiro const ... ERRO!!!!
*Const_dbl = 5.43; // [C++ Error] Unit1.cpp(69): E2024 Cannot modify a const object.
Const_dbl = &dbl_1; // [C++ Error] Unit1.cpp(70): E2024 Cannot modify a const object.
Label3 -> Caption = *Const_dbl;
}
//---------------------------------------------------------------------------
Referências
É possível criar um segundo nome para determinado tipo de dados, ou seja, um "apelido". O nome
original e o "apelido" serão, exatamente, o mesmo dado, ocupando, inclusive, o mesmo endereço. Toda
modificação que implementarmos através do "apelido" estará afetando, diretamente, o dado original:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString esposa = "Marta"; // declara e inicializa variável esposa
AnsiString &ref_esposa = esposa; // cria e inicializa uma referência para esposa
ref_esposa = "Marta é minha esposa!"; // altera variável esposa através do "apelido"
Label1 -> Caption = esposa + '\n' + // imprime esposa (modificada por ref_esposa)
// demonstra que se trata do mesmo dado, através do endereço
"Endereço de esposa =
" + IntToHex(int(&esposa), 8) + '\n' +
"Endereço de ref_esposa =
" + IntToHex(int(&ref_esposa), 8);
}
//---------------------------------------------------------------------------
Apostila de C++ Builder
Pagina -195-
Analisando o código, percebemos que uma referência deve ser criada com o mesmo tipo de dados do dado
alvo (AnsiString, no exemplo) e com o operador & antecedendo o "apelido", bem como ser inicializada
com o nome do dado original (esposa, no exemplo). Outra observação importantíssima é que referências
devem ser inicializadas na mesma instrução de sua declaração.
Instruções do tipo:
AnsiString &ref_esposa;
ref_esposa = esposa;
Retornarão uma mensagem de erro, avisando que a referência deve ser inicializada:
[C++ Error] Unit1.cpp(19): E2304 Reference variable 'ref_esposa' must be initialized.
Reatribuição de uma referência
Desde minha infância, tenho um grande amigo chamado Wanderley. Desde que o conheço (trinta anos, ou
mais), o Wanderley tem um apelido: "Churrasco". Esse apelido está tão difundido na personalidade do
Wanderley, que uma vez ele me falou que eu seria a única pessoa que o chamava pelo nome verdadeiro.
Todas as outras pessoas tratavam-no por seu apelido. Muitos nem sabem seu verdadeiro nome. Agora
vamos supor uma hipótese meio absurda. Amanhã eu chego até o Wanderley para, formalmente,
comunicar-lhe de que, a partir daquele momento, o apelido não será mais dele. Que o apelido será meu. E
saio da presença do Wanderley convencido de que, a partir de então, todos me chamarão de Churrasco,
enquanto tratarão o antigo Churrasco por Wanderley. É possível????
NÃO DÁ PRA TROCAR APELIDO!!!!!
E, POR MAIS QUE SE POSSA QUERER,
NÃO SE PODE PEGAR O APELIDO DE NINGUÉM!!!!
Certamente você já entendeu onde queremos chegar: Uma referência, uma vez inicializada no momento
de sua criação, apontará sempre para o mesmo dado. Não podemos pegar o "apelido" daquele dado e
transferi-lo para outro dado:
//--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString irma = "Cybele"; // declara e inicializa variável irma
AnsiString &ref_irma = irma; // cria e inicializa uma referência para irma
ref_irma = "Cybele é minha irmã!"; // altera variável esposa através do "apelido"
Label1 -> Caption = irma + '\n' + // imprime irma (modificada por ref_irma)
// demonstra que se trata do mesmo dado, através do endereço
"Endereço de irma =
" + IntToHex(int(&irma), 8) + '\n' +
"Endereço de ref_irma =
" + IntToHex(int(&ref_irma), 8);
AnsiString irma_1 = "Daurylene também é minha irmã"; // variável irma_1
Apostila de C++ Builder
Pagina -196-
irma_1 = ref_irma;
/* parece
variável.
passa ser
O apelido
que está atribuindo a referência para a outra
Mas está alterando o conteúdo de irma_1, que
igual a irma.
continua pertencendo a irma (v. endereços) */
//ref_irma = irma_1; // eis outra possibilidade de verificação no lugar da anterior
Label2 -> Caption =
irma_1 + '\n' + // imprime irma_1, seja qual for o conteúdo
"Endereço de irma_1 =
" + IntToHex(int(&irma_1), 8) + '\n'
+
// mostra que ref_irma mantém o antigo endereço
"Endereço de ref_irma =
" + IntToHex(int(&ref_irma), 8) +
"\n\n\n" +
irma + '\n' + // mostra que irma foi alterada
// demonstra que se trata do mesmo dado, através do endereço
"Endereço de irma =
" + IntToHex(int(&irma), 8) + '\n' +
ref_irma + '\n' +
"Endereço de ref_irma =
" + IntToHex(int(&ref_irma), 8);
}
//---------------------------------------------------------------------------
Pelo exemplo, percebemos que a referência não pode apontar para outro dado. O que ela faz é alterar o
valor dos dados. E pode alterar tanto o valor do alvo, como do outro tipo que estivermos trabalhando,
atribuindo a esse segundo, o valor do alvo, ou vice-versa.
Este curso está disponível, online, no DicasBCB, o Site dos Programadores C++Builder
http://www.dicasbcb.com.br
nosso fórum:
http://www.dicasbcb.com
Autor: Thérbio de Lima Alves (DeLima)
Agradecimentos especiais:
Lucas Santos Sanches ( mais conhecido por scorpio) em nosso Fórum.
Lucas, em nome de toda nossa comunidade, muito obrigado por viabilizar esta edição em PDF de nosso
curso, antiga reivindicação de grande parte dos freqüentadores de nosso site!
Apostila de C++ Builder
Pagina -197-
Download

Apostila Básica para programação do ambiente Borland C++ Builder