Sie sind auf Seite 1von 360

MICROSOFT VISUAL C++ .NET PASSO A PASSO (Julian Templeman & Andy Olsen) INTRODUO ...........................................................................................

XI PARTE 1: INICIALIZAO EM C++ .NET................................................... 14

1.

HELLO, C++!.................................................................................................14
1.1. O QUE UM PROGRAMA C++?...............................................................................14 1.1.1. C++ UMA LINGUAGEM FORTEMENTE VINCULADA A TIPOS........14 1.1.2. C++ UMA LINGUAGEM EFICIENTE.........................................................14 1.1.3. C++ UMA LINGUAGEM ORIENTADA PARA OBJETOS.......................14 1.1.4. C++ BASEADA EM C (COMO VOC J SUSPEITAVA) ........................15 1.1.5. C++ UMA LINGUAGEM SENSVEL A CARACTERES MAISCULOS E MINSCULOS .................................................................................................................15 1.2. SEU PRIMEIRO PROGRAMA C++ ...........................................................................15 1.2.1. A FUNO main .................................................................................................16 1.2.2. PALAVRAS-CHAVES E IDENTIFICADORES..............................................17 1.3. CRIANDO UM PROGRAMA EXECUTVEL TEORIA ........................................18 1.3.1. EDITANDO OS ARQUIVOS-FONTES DO PROGRAMA ............................18 1.3.2. COMPILANDO OS ARQUIVOS-FONTES .....................................................18 1.3.3. VINCULANDO OS ARQUIVOS-OBJETOS....................................................18 1.3.4. EXECUTANDO E TESTANDO O PROGRAMA............................................18 1.4. CRIANDO UM PROGRAMA EXECUTVEL PRTICA ......................................19 1.4.1. CRIANDO UM PROJETO .................................................................................19 1.4.2. ADICIONANDO UM ARQUIVO-FONTE C++ AO PROJETO....................21 1.4.3. ADICIONANDO CDIGO C++ AO ARQUIVO-FONTE..............................22 1.4.4. CONSTRUINDO O EXECUTVEL .................................................................23 1.4.5. EXECUTANDO O PROGRAMA ......................................................................24 1.5. CONCLUSES .............................................................................................................24 1.6. RPIDAS REFERNCIAS SOBRE O CAPTULO 1................................................24

2. CAPTULO 2: INTRODUO PROGRAMAO ORIENTADA PARA OBJETOS ............................................................................................................25


2.1. O QUE PROGRAMAO ORIENTADA PARA OBJETOS?................................25 2.2. CARACTERSTICAS DAS LINGUAGENS DE PROGRAMAO ORIENTADA PARA OBJETOS.......................................................................................................................26 2.2.1. ENCAPSULAMENTO ........................................................................................26 2.2.2. HERANA............................................................................................................26 2.2.3. POLIMORFISMO ...............................................................................................27 2.3. CLASSES E OBJETOS ................................................................................................27 2.4. BENFCIOS NO ATO DO DESENVOLVIMENTO...................................................28 2.5. UM EXEMPLO SIMPLES ...........................................................................................29 2.6. RPIDAS REFERNCIAS SOBRE O CAPTULO 2................................................34

3.

CAPTULO 3: VARIVEIS E OPERADORES ..............................................35


3.1. O QUE UMA VARIVEL?.......................................................................................35 3.2. OS TIPOS FUNDAMENTAIS DE DADOS ................................................................35 3.3. DECLARANDO UMA VARIVEL .............................................................................36 3.3.1. NOMEANDO VARIVEIS ................................................................................37 3.4. DECLARANDO MLTIPLAS VARIVEIS...............................................................37 3.5. NOMEANDO VALORES PARA AS VARIVEIS......................................................37 i

3.6. MATRIZES....................................................................................................................38 3.7. PONTEIROS .................................................................................................................39 3.8. REFERNCIAS............................................................................................................39 3.9. CONSTANTES..............................................................................................................40 3.9.1. CONSTANTE LITERAL ....................................................................................40 3.9.2. CONSTANTE SIMBLICA...............................................................................40 3.10. ENUMERAES .....................................................................................................40 3.11. TIPOS DEFINIDOS PELO USURIO...................................................................41 3.12. ADICIONANDO VARIVEIS-MEMBROS S CLASSES....................................41 3.13. A CLASSE String DO .NET FRAMEWORK ..........................................................44 3.14. OPERADORES E EXPRESSES ...........................................................................45 3.14.1. OPERADORES DE ATRIBUIO...................................................................45 3.14.2. OPERADORES ARITMTICOS ......................................................................45
3.14.2.1. 3.14.2.2. 3.14.2.3. OPERADORES MATEMTICOS PADRES...........................................................45 OPERADORES ARITMTICOS DE ATRIBUIO ................................................46 OPERADORES DE INCREMENTO E DECREMENTO ...........................................46 OPERADORES RELACIONAIS ................................................................................47 OPERADORES LGICOS..........................................................................................47

3.14.3.

OPERADORES RELACIONAIS E LGICOS................................................47

3.14.3.1. 3.14.3.2.

3.14.4. OPERADORES BIT-A-BIT................................................................................48 3.14.5. O OPERADOR TERNRIO ..............................................................................49 3.14.6. O OPERADOR sizeof ..........................................................................................49 3.14.7. CONVERSO DE TIPOS...................................................................................49 3.14.8. PRECEDNCIA E ASSOCIATIVIDADE DE OPERADORES.....................50 3.15. RPIDAS REFERNCIAS SOBRE O CAPTULO 3............................................51

4.

CAPTULO 4: USANDO FUNES .............................................................52


4.1. DECLARANDO PROTTIPOS DE FUNO ..........................................................52 4.1.1. DECLARANDO UM PROTTIPO SIMPLES DE FUNO .......................52
4.1.1.1. EXERCCIO: DECLARANDO UM PROTTIPO DE FUNO ..............................53

4.1.2.
4.1.2.1.

DECLARANDO PARMETROS EM UM PROTTIPO DE FUNO .....53


EXERCCIO: PROTTIPO DE FUNO COM PARMETROS ............................53

4.1.3. DECLARANDO O TIPO DE RETORNO EM UM PROTTIPO DE FUNO...............................................................................................................................54


4.1.3.1. EXERCCIO: FUNO COM RETORNO NO-NULO ...........................................54

4.1.4. DECLARANDO VALORES PADRES PARA PARMETROS DE FUNO...............................................................................................................................54


4.1.4.1. EXERCCIO: PARMETROS PADRES..................................................................54

4.2. DEFININDO OS CORPOS DE FUNO..................................................................55 4.2.1. DEFININDO UM CORPO DE FUNO SIMPLES.......................................55


4.2.1.1. EXERCCIO: ADICIONANDO O CORPO DE UMA FUNO ...............................56

4.2.2.
4.2.2.1.

DEFININDO UM CORPO DE FUNO QUE UTILIZA PARMETROS 56


EXERCCIO: CORPO DE FUNO COM PARMETROS.....................................56

4.2.3.
4.2.3.1.

DEFININDO UM CORPO DE FUNO QUE RETORNA UM VALOR ...57


EXERCCIO: CORPO DE FUNO QUE RETORNA UM VALOR........................57

4.3. CHAMANDO FUNES.............................................................................................59 4.3.1. CHAMANDO FUNES NO APLICATIVO-AMOSTRA ............................59 4.3.2. PERCORRENDO O APLICATIVO COM O DEPURADOR ........................61 4.3.3. COMPREENDENDO O ESCOPO LOCAL E GLOBAL................................64
4.3.3.1. EXERCCIO: ESCOPO PARA VARIVEL GLOBAL ..............................................65

4.3.4.
4.3.4.1.

SOBRECARREGANDO FUNES .................................................................66


EXERCCIO: FUNES SOBRECARREGADAS.....................................................66

4.4. ii

RPIDAS REFERNCIAS SOBRE O CAPTULO 4................................................67

5.

CAPTULO 5: DECLARAES DE CONDICIONAIS E DE LAOS............ 69


5.1. TOMANDO DECISES COM A DECLARAO if ...............................................69 5.1.1. REALIZANDO TESTES SIMPLES ..................................................................69
5.1.1.1. EXERCCIO: UM NOVO APLICATIVO PARA TESTES SIMPLES........................70

5.1.2.
5.1.2.1.

REALIZANDO TESTES DUPLOS ...................................................................73


EXERCCIO: TESTE DUPLO ....................................................................................73

5.1.3.
5.1.3.1.

REALIZANDO MLTIPLOS TESTES............................................................74


EXERCCIO: NMERO MXIMO DE DIAS DE UM DADO MS ........................75

5.1.4.
5.1.4.1.

REALIZANDO TESTES ANINHADOS ...........................................................76


EXERCCIO: TESTES ANINHADOS.........................................................................77

5.2. TOMANDO DECISES COM A DECLARAO switch......................................78 5.2.1. DEFININDO DECLARAES switch SIMPLES .......................................78
5.2.1.1. EXERCCIO: MELHORANDO O CalendarAssistant......................................79

5.2.2.
5.2.2.1.

DEFININDO PASSAGEM DIRETA EM UMA DECLARAO switch ..80


EXERCCIO: PASSAGEM DIRETA EM UMA DECLARAO switch ..............81

5.3. EXECUTANDO LAOS...............................................................................................81 5.3.1. USANDO OS LAOS while.............................................................................82


5.3.1.1. EXERCCIO: USANDO O LAO while NO PROJETO CalendarAssistant 83

5.3.2.
5.3.2.1.

USANDO LAOS for ........................................................................................84


EXERCCIO: USADO O LAO for NO PROJETO CalendarAssistant........85

5.3.3.

USANDO LAOS do-while ............................................................................86

5.3.3.1. EXERCCIO: USANDO O LAO do-while NO PROJETO CalendarAssistant................................................................................................................87

5.3.4.
5.3.4.1.

REALIZANDO SALTOS INCONDICIONAIS ................................................88


EXERCCIO: USANDO SALTOS INCONDICIONAIS NO CalendarAssistant 88

5.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 5................................................89

PARTE 2: MAIS SOBRE PROGRAMAO ORIENTADA PARA OBJETOS... 92

6.

CAPTULO 6: MAIS SOBRE CLASSES E OBJETOS..................................92


6.1. ORGANIZANDO CLASSES EM ARQUIVOS DE CABEALHO E ARQUIVOSFONTES ....................................................................................................................................92 6.1.1. DEFININDO UMA CLASSE EM UM ARQUIVO DE CABEALHO .........94
6.1.1.1. EXERCCIO: CRIANDO O APLICATIVO CreditOrganizer E A CLASSE CreditCardAccount NO ARQUIVO DE CABEALHO CreditCardAccount.h........94

6.1.2.

IMPLEMENTANDO UMA CLASSE EM UM ARQUIVO-FONTE .............95

6.1.2.1. EXERCCIO: IMPLEMENTANDO A CLASSE CreditCardAccount NO ARQUIVO-FONTE CreditCardAccount.cpp.....................................................................95

6.2. CRIANDO E DESTRUINDO OBJETOS ....................................................................97 6.2.1. EXERCCIO: CRIANDO E DELETANDO UM OBJETO CreditCardAccount .......................................................................................................98 6.3. DEFININDO CONSTRUTORES E DESTRUTORES ...............................................99 6.3.1. DEFININDO CONSTRUTORES.......................................................................99
6.3.1.1. EXERCCIO: UM CONSTRUTOR PARA A CLASSE CreditCardAccount.....99

6.3.2.
6.3.2.1.

DEFININDO DESTRUTORES ........................................................................101


EXERCCIO: UM DESTRUTOR PARA A CLASSE CreditCardAccount......101

6.4. DEFININDO MEMBROS static DE UMA CLASSE..........................................102 6.4.1. DEFININDO MEMBROS DE DADOS static ............................................103
6.4.1.1. EXERCCIO: ADICIONANDO UM MEMBRO DE DADOS static CLASSE CreditCardAccount..............................................................................................................104

6.4.2.

DEFININDO FUNES-MEMBROS static .............................................105 iii

6.4.2.1.

EXERCCIO: FUNO-MEMBRO static NA CLASSE CreditCardAccount 106

6.5. DEFININDO RELAES ENTRE OBJETOS ........................................................107 6.5.1. DEFININDO A CLASSE LoyaltyScheme ..................................................108
6.5.1.1. EXERCCIO: DEFININDO A CLASSE LoyaltyScheme EM UM NOVO ARQUIVO DE CABEALHO ....................................................................................................108

6.5.2.

IMPLEMENTANDO A CLASSE LoyaltyScheme ....................................109

6.5.2.1. EXERCCIO: IMPLEMENTANDO A CLASSE LoyaltyScheme EM UM NOVO ARQUIVO DE CABEALHO ....................................................................................................109

6.5.3.

CRIANDO, USANDO E DESTRUINDO OBJETOS LoyaltyScheme.....110

6.5.3.1. EXERCCIO: ESTENDENDO A CLASSE CreditCardAccount PARA SUPORTAR A FUNCIONALIDADE DO ESQUEMA DE LEALDADE..................................110

6.5.4. 6.6.

TESTANDO O APLICATIVO .........................................................................112

6.5.4.1. EXERCCIO: TESTANDO A FUNCIONALIDADE DO ESQUEMA DE LEALDADE .................................................................................................................................112

RPIDAS REFERNCIAS SOBRE O CAPTULO 6..............................................113

7.

CAPTULO 7: CONTROLANDO OS TEMPOS DE VIDA DOS OBJETOS . 116


7.1. O GERENCIAMENTO DE MEMRIA TRADICIONAL C++ ...............................116 7.1.1. CRIANDO OBJETOS .......................................................................................116 7.1.2. DELETANDO OBJETOS .................................................................................116 7.1.3. VANTAGENS E DESVANTAGENS DA ALOCAO MANUAL DE MEMRIA .........................................................................................................................117 7.2. A ABORDAGEM .NET...............................................................................................118 7.2.1. FINALIZADORES.............................................................................................119 7.2.2. IMPLEMENTANDO UM FINALIZADOR....................................................120
7.2.2.1. EXERCCIO: IMPLEMENTANDO UM FINALIZADOR PARA UMA CLASSE GERENCIADA.............................................................................................................................120

7.2.3. 7.2.4.
7.2.4.1.

ALGUNS PONTOS SOBRE FINALIZAO................................................122 USANDO UM MTODO Dispose ................................................................122


EXERCCIO: ADICIONANDO SUPORTE Dispose EM UMA CLASSE............122

7.2.5.
7.2.5.1.

INTEGRANDO Finalize E Dispose.........................................................123


EXERCCIO: USANDO SINALIZADOR PARA LIBERAR RECURSOS ..............124

7.3.

RPIDAS REFERNCIAS SOBRE O CAPTULO 7..............................................126

8.

CAPTULO 8: HERANA ...........................................................................127


8.1. PROJETANDO UMA HIERARQUIA HEREDITRIA ..........................................127 8.2. DEFININDO UMA CLASSE-BASE .........................................................................128 8.2.1. EXERCCIO: CRIANDO UM NOVO APLICATIVO, DEFININDO E IMPLEMENTANDO UMA CLASSE-BASE ..................................................................128 8.3. DEFININDO UMA CLASSE-DERIVADA ...............................................................130 8.3.1. EXERCCIO: DEFININDO E IMPLEMENTANDO UMA CLASSEDERIVADA ........................................................................................................................130 8.4. ACESSANDO MEMBROS DA CLASSE-BASE.......................................................132 8.4.1. EXERCCIO: DEFININDO E IMPLEMENTANDO A CLASSEDERIVADA SavingsAccount......................................................................................133 8.5. CRIANDO OBJETOS.................................................................................................134 8.5.1. EXERCCIO: CRIANDO OBJETOS CurrentAccount E SavingsAccount ............................................................................................................134 8.6. SOBREPONDO FUNES-MEMBROS .................................................................136 8.6.1. EXERCCIO: FUNO VIRTUAL E FUNO VIRTUAL PURA...........137 8.7. DEFININDO CLASSES SELADAS ..........................................................................139 8.8. DEFININDO E USANDO INTERFACES................................................................140

iv

8.9.

RPIDAS REFERNCIAS SOBRE O CAPTULO 8..............................................140

PARTE 3: FUNDAMENTOS DE PROGRAMAO NO MICROSOFT .NET .. 142

9.

CAPTULO 9: TIPOS DE VALOR ...............................................................142


9.1. TIPOS DE REFERNCIA E TIPOS DE VALOR....................................................142 9.1.1. A NECESSIDADE DE TIPOS DE VALOR ....................................................142 9.1.2. PROPRIEDADES DOS TIPOS DE VALOR ..................................................143 9.2. ESTRUTURAS ............................................................................................................144 9.2.1. EXERCCIO: CRIANDO E USANDO UMA ESTRUTURA SIMPLES .....144 9.2.2. EXERCCIO: INVESTIGANDO A ESTRUTURA........................................145 9.2.3. DIFERENAS ENTRE ESTRUTURAS E CLASSES...................................147 9.2.4. EXERCCIO: IMPLEMENTANDO CONSTRUTORES PARA UMA ESTRUTURA .....................................................................................................................147 9.2.5. USANDO UMA ESTRUTURA DENTRO DE OUTRA.................................148
9.2.5.1. EXERCCIO: INVESTIGANDO COMO FUNCIONAM MEMBROS DE DADOS DE ESTRUTURA ...............................................................................................................................148

9.2.6. COPIANDO ESTRUTURAS ............................................................................151 9.3. ENUMERAES .......................................................................................................151 9.3.1. EXERCCIO: CRIANDO E USANDO UMA ENUMERAO...................151 9.3.2. EXERCCIO: USANDO ENUMERAES EM PROGRAMAS ................152 9.3.3. EVITANDO AMBIGIDADES .......................................................................153 9.3.4. USANDO MEMRIA EFICIENTEMENTE..................................................153 9.4. RPIDAS REFERNCIAS SOBRE O CAPTULO 9..............................................154

10.

CAPTULO 10: SOBRECARREGANDO OPERADORES....................... 155

10.1. O QUE SOBRECARREGAR OPERADOR? .....................................................155 10.1.1. QUAIS OS TIPOS QUE PRECISAM DE OPERADORES SOBRECARREGADOS?..................................................................................................155 10.1.2. O QUE VOC PODE SOBRECARREGAR?.................................................156 10.1.3. REGRAS PARA SOBRECARGA....................................................................156 10.2. SOBRECARREGANDO OPERADORES EM TIPOS GERENCIADOS ............156 10.2.1. SOBRECARREGANDO TIPOS DE VALOR ................................................157
10.2.1.1. EXERCCIO: SOBRECARREGANDO OPERADORES ARITMTICOS .............157 EXERCCIO: SOBRECARREGANDO FUNES DE OPERADOR ....................160 EXERCCIO: IMPLEMENTAO DO OPERADOR IGUALDADE.....................163

10.2.2. 10.2.3. 10.2.4. 10.2.5. 10.2.6. 10.2.7.

SOBRECARREGANDO FUNES DE OPERADOR.................................160 IMPLEMENTANDO OPERADORES LGICOS E A IGUALDADE........162 EXERCCIO: IMPLEMENTANDO Equals ................................................165 EXERCCIO: IMPLEMENTANDO ATRIBUIES...................................166 EXERCCIO: IMPLEMENTANDO INCREMENTO E DECREMENTO.168 SOBRECARREGANDO TIPOS DE REFERNCIA ....................................170

10.2.2.1. 10.2.3.1.

10.2.7.1. IMPLEMENTANDO OPERADORES SOBRECARREGADOS PARA TIPOS DE REFERNCIA..............................................................................................................................170

10.2.8. CHAMANDO OPERADORES SOBRECARREGADOS PARA TIPOS DE REFERNCIA ...................................................................................................................170 10.3. DIRETRIZES PARA FORNECIMENTO DE OPERADORES SOBRECARREGADOS..........................................................................................................170 10.4. RPIDAS REFERNCIAS SOBRE O CAPTULO 10........................................171

11.

CAPTULO 11: MANIPULANDO EXCEES ........................................172


11.1.1.1. EXERCCIO: ERRO DE DIVISO POR ZERO ......................................................173

11.1. O QUE SO EXCEES?.....................................................................................172 11.1.1. COMO AS EXCEES FUNCIONAM?........................................................173 11.1.2. TIPOS DE EXCEO ......................................................................................174 v

11.2. LANANDO EXCEES......................................................................................174 11.2.1. EXERCCIO: GERANDO UMA EXCEO ................................................176 11.3. MANIPULANDO EXCEES ..............................................................................177 11.3.1. EXERCCIO: FUNDAMENTOS DA MANIPULAO DE EXCEES.178 11.3.2. PERSONALIZANDO A MANIPULAO DE EXCEES.......................179 11.3.3. USANDO A HIERARQUIA DE EXCEO ..................................................180 11.3.4. EXERCCIO: USANDO EXCEES COM CONSTRUTORES................180 11.3.5. ANINHANDO E RELANANDO EXCEES ............................................181
11.3.5.1. EXERCCIO: RELANANDO EXCEES............................................................182

11.3.6. EXERCCIO: O BLOCO __finally ...........................................................184 11.3.7. O BLOCO catch(...) ..................................................................................185 11.4. CRIANDO SEUS PRPRIOS TIPOS DE EXCEO.........................................185 11.4.1. EXERCCIO: CRIANDO E USANDO UMA CLASSE DE EXCEO DERIVADA DE Exception ...........................................................................................186 11.4.2. USANDO CLASSES __value ........................................................................187 11.5. USANDO __try_cast PARA CONVERSO DINMICA ...............................188 11.6. USANDO EXCEES NAS LINGUAGENS .NET..............................................189 11.6.1. EXERCCIO: CRIANDO UMA CLASSE C++ E UTILIZANDO-A EM UM APLICATIVO DO VISUAL BASIC ................................................................................189 11.7. RPIDAS REFERNCIAS SOBRE O CAPTULO 11........................................193

12.

CAPTULO 12: MATRIZES E COLEES .............................................194

12.1. MATRIZES NATATIVAS C++ ..............................................................................194 12.1.1. EXERCCIO: CRIANDO E USANDO MATRIZES NATIVAS C++ ..........194 12.1.2. PASSANDO MATRIZES PARA FUNES..................................................196
12.1.2.1. EXERCCIO: PASSANDO UMA MATRIZ PARA UMA FUNO......................197

12.1.3. 12.1.4. 12.1.5.

INICIALIZANDO MATRIZES........................................................................197 MATRIZES MULTIDIMENSIONAIS............................................................198


EXERCCIO: CRIANDO E USANDO UMA MATRIZ 2D .....................................198 EXERCCIO: CRIANDO E USANDO MATRIZ DINAMICAMENTE ALOCADA 199

12.1.4.1. 12.1.5.1.

ALOCAO DINMICA E MATRIZES ......................................................199

12.1.6. MATRIZES __gc ..............................................................................................200 12.1.7. USANDO AS PALAVRAS-CHAVES __gc E __nogc ................................201 12.1.8. EXERCCIO: MATRIZES E TIPOS DE REFERNCIA ............................201 12.1.9. MATRIZES __gc MULTIDIMENSIONAIS .................................................202 12.2. A CLASSE .NET Array ........................................................................................203 12.2.1. EXERCCIO: OPERAES BSICAS EM MATRIZES ...........................203 12.2.2. OPERAES DE MATRIZ MAIS AVANADAS .......................................205
12.2.2.1. 12.2.2.2. 12.2.2.3. EXERCCIO: COPIANDO ELEMENTOS DE MATRIZ.........................................205 EXERCCIO: PROCURANDO ELEMENTOS DE MATRIZ ..................................207 EXERCCIO: ORDENANDO ELEMENTOS DE MATRIZ ....................................207 EXERCCIO: USANDO UM ENUMERADOR NUMA MATRIZ DE STRINGS ..210

12.2.3.

ENUMERADORES ...........................................................................................209

12.2.3.1.

12.3. OUTRAS CLASSES .NET DE COLEO............................................................210 12.3.1. A CLASSE ArrayList....................................................................................211


12.3.1.1. 12.3.1.2. EXERCCIO: CRIANDO E MANIPULANDO UM OBJETO ArrayList...........211 OUTRAS OPERAES ArrayList .....................................................................213 EXERCCIO: CRIANDO E MANIPULANDO UM OBJETO SortedList ........214 OUTRAS OPERAES SortedList ...................................................................216

12.3.2.

A CLASSE SortedList .................................................................................214

12.3.2.1. 12.3.2.2.

12.3.3. vi

A CLASSE StringCollection .............................................................................216

12.3.3.1.

EXERCCIO: CRIANDO E MANIPULANDO UM OBJETO StringCollection 216

12.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 12........................................217

13.

CAPTULO 13: PROPRIEDADES ...........................................................218

13.1. O QUE SO PROPRIEDADES? ...........................................................................218 13.1.1. OS DOIS TIPOS DE PROPRIEDADES..........................................................219 13.2. IMPLEMENTANDO PROPRIEDADES ESCALARES.......................................219 13.2.1. EXERCCIO: IMPLEMENTANDO PROPRIEDADES ESCALARES DE UMA CLASSE....................................................................................................................219 13.2.2. ERROS EM PROPRIEDADES ........................................................................221 13.2.3. PROPRIEDADES SOMENTE PARA LEITURA E SOMENTE PARA ESCRITA ............................................................................................................................221
13.2.3.1. EXERCCIO: IMPLEMENTANDO UMA PROPRIEDADE SOMENTE PARA LEITURA E CRIANDO UMA PROPRIEDADE DERIVADA ..................................................221

13.3. IMPLEMENTANDO PROPRIEDADES INDEXADAS.......................................223 13.3.1. O EXEMPLO DO BANCO ...............................................................................223 13.3.2. EXERCCIO: IMPLEMENTANDO A CLASSE Bank ................................223 13.3.3. EXERCCIO: ADICIONANDO UMA CLASSE Account ..........................226 13.3.4. EXERCCIO: CRIANDO PROPRIEDADES PARA A CLASSE Account 228 13.3.5. ADICIONANDO CONTAS NA CLASSE Bank.............................................228
13.3.5.1. EXERCCIO: IMPLEMENTANDO OS MTODOS Add E Remove.....................229 13.3.5.2. EXERCCIO: IMPLEMENTANDO UMA PROPRIEDADE INDEXADA PARA RETER CONTAS.........................................................................................................................229

13.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 13........................................231

14.

CAPTULO 14: DELEGADOS E EVENTOS ............................................232

14.1. O QUE SO DELEGADOS?..................................................................................232 14.1.1. O QUE FAZEM OS DELEGADOS? ...............................................................233 14.1.2. EXERCCIO: DEFININDO DELEGADOS ...................................................233 14.1.3. IMPLEMENTANDO DELEGADOS...............................................................234 14.1.4. EXERCCIO: CHAMANDO FUNES-MEMBROS static USANDO DELEGADOS.....................................................................................................................234 14.1.5. USANDO DELEGADOS PARA CHAMAR FUNES-MEMBROS NO static 235 14.1.6. USANDO DELEGADOS multicast ............................................................235
14.1.6.1. EXERCCIO: CRIANDO E USANDO UM DELEGADO multicast .................235

14.2. O QUE SO EVENTOS? .......................................................................................238 14.2.1. EXERCCIO: IMPLEMENTANDO UMA CLASSE DE FONTE DE EVENTOS...........................................................................................................................239 14.2.2. EXERCCIO: IMPLEMENTANDO UM RECEPTOR DE EVENTOS ......240 14.2.3. EXERCCIO: JUNTANDO TUDO..................................................................241 14.3. RPIDAS REFERNCIAS SOBRE O CAPTULO 14........................................244 PARTE 4: USANDO O .NET FRAMEWORK............................................... 246

15.

CAPTULO 15: A BIBLIOTECA DE CLASSES .NET FRAMEWORK ......... 246

15.1. O QUE O .NET Framework?..............................................................................246 15.1.1. A LINGUAGEM COMUM EM TEMPO DE EXECUO .........................246 15.1.2. A LINGUAGEM INTERMEDIRIA..............................................................247 15.1.3. O SISTEMA DE TIPO COMUM .....................................................................247 15.1.4. A ESPECIFICAO DA LINGUAGEM COMUM ......................................247 15.1.5. A BIBLIOTECA DE CLASSES .NET Framework........................................247 vii

15.1.6. 15.1.7.

ASSEMBLIES ....................................................................................................248 METADATA.......................................................................................................249


EXERCCIO: MODIFICANDO UM METADATA PADRO ................................249

15.1.7.1.

15.2. OS namespaces DA BIBLIOTECA .NET Framework......................................251 15.2.1. USANDO namespaces EM PROGRAMAS C++.........................................252 15.2.2. O namespace System ...................................................................................252
15.2.2.1. 15.2.2.2. TIPOS BSICOS .......................................................................................................253 TIPOS PONTOS-FLUTUANTES..............................................................................254

15.2.3. OS namespaces DE COLEES..................................................................254 15.2.4. AS INTERFACES DAS COLEES..............................................................255 15.2.5. O namespace IO .................................................................................................255 15.2.6. OS namespaces PARA DESENHO...............................................................256 15.2.7. O namespace Forms ...........................................................................................257 15.2.8. OS namespaces Net ............................................................................................258 15.2.9. OS namespaces Xml ...........................................................................................258 15.2.10. OS namespaces Data ......................................................................................258 15.2.11. OS namespaces Web ......................................................................................258 15.3. RPIDAS REFERNCIAS SOBRE O CAPTULO 15........................................259

16.

CAPTULO 16: INTRODUZINDO AS FORMAS DO WINDOWS ............. 260

16.1. APLICATIVOS WINDOWS FORMS ....................................................................260 16.1.1. AS FORMAS DO WINDOWS E AS REAS DE DESENHO ......................261 16.1.2. AS FORMAS DO WINDOWS VERSUS A MFC ...........................................261 16.1.3. UMA PALAVRA SOBRE ATL ........................................................................262 16.2. O namespace System::Windows::Forms................................................................262 16.3. CRIANDO E USANDO FORMAS.........................................................................263 16.3.1. EXERCCIO: CRIANDO UMA FORMA SIMPLES ....................................263 16.3.2. USANDO AS PROPRIEDADES DE Form .....................................................265
16.3.2.1. EXERCCIO: USANDO O EDITOR DE PROPRIEDADES....................................268

16.3.3. 16.3.4. 16.3.5.

RELAES ENTRE FORMAS.......................................................................270 EXERCCIO: COLOCANDO CONTROLES NA FORMA .........................271 MANIPULANDO EVENTOS...........................................................................272
EXERCCIO: ADICIONANDO MANIPULADORES DE EVENTOS AOS BOTES 273

16.3.5.1.

16.4. USANDO CONTROLES.........................................................................................274 16.4.1. RTULOS ..........................................................................................................275


16.4.1.1. EXERCCIO: ADICIONANDO UM RTULO A UMA FORMA E MANIPULANDO SUAS PROPRIEDADES...............................................................................275

16.4.2. 16.4.3. 16.4.4. 16.4.5. 16.4.6.

BOTES .............................................................................................................276 CAIXAS DE CHECAGEM E BOTES DE RDIO .....................................277 USANDO BOTES DE RDIO COM UM GRUPO.....................................278 CAIXAS DE LISTA E CAIXAS COMBO ......................................................278 CAIXAS DE TEXTO .........................................................................................282

16.4.4.1. EXERCCIO: CONFIGURANDO UMA CAIXA DE GRUPO CONTENDO BOTES DE RDIO ...................................................................................................................278 16.4.5.1. EXERCCIO: CONFIGURANDO UMA CAIXA COMBO E RESPONDENDO AOS EVENTOS QUE ELA DISPARA ................................................................................................281 16.4.6.1. EXERCCIO: ADICIONANDO UM CONTROLE DE EDIO PARA MANIPULAO DE TEXTO.....................................................................................................284

16.5. USANDO MENUS ..................................................................................................285 16.5.1. EXERCCIO: ADICIONANDO UM MENU SIMPLES FORMA PRINCIPAL DO APLICATIVO ......................................................................................286 16.5.2. MAIS SOBRE MENUS .....................................................................................288 viii

16.5.3. EXERCCIO: MOSTRANDO UM MENU DE CONTEXTO.......................289 16.6. RPIDAS REFERNCIAS SOBRE O CAPTULO 16........................................290

17.

CAPTULO 17: CAIXAS DE DILOGO E CONTROLES ........................291

17.1. USANDO CAIXAS DE DILOGO ........................................................................291 17.1.1. EXERCCIO: CRIANDO UM CAIXA DE DILOGO About....................292 17.1.2. A PROPRIEDADE DialogResult...............................................................294 17.1.3. EXERCCIO: USANDO DADOS COM CAIXAS DE DILOGO...............295 17.1.4. FIXANDO A ORDEM DE TABULAO......................................................297 17.2. USANDO AS CAIXAS DE DILOGO COMUNS ................................................297 17.2.1. EXERCCIO: ESCOLHENDO UMA FONTE E FIXANDO-A EM UM CONTROLE DE RTULO ..............................................................................................298 17.3. MAIS SOBRE CONTROLES .................................................................................299 17.3.1. USANDO O CONTROLE TreeView.............................................................300
17.3.1.1. 17.3.1.2. EXERCCIO: CRIANDO E PREENCHENDO UMA TreeView...........................303 EXERCCIO: ADICIONANDO PASTAS AO NAVEGADOR................................304

17.3.2.

USANDO O CONTROLE ListView.............................................................308

17.3.2.1. EXERCCIO: ADICIONANDO UM CONTROLE DE VISTA DE LISTA FORMA PRINCIPAL..................................................................................................................................310 17.3.2.2. EXERCCIO: MOSTRANDO DETALHES DE UMA PASTA................................312

17.3.3. 17.3.4. 17.3.5.

USANDO DIVISORES ......................................................................................315


EXERCCIO: ADICIONANDO UM CONTROLE Splitter A UMA FORMA .315 EXERCCIO: ADICIONANDO UMA ToolBar A UMA FORMA .......................317 EXERCCIO: ADICIONANDO UMA BARRA DE STATUS A UMA FORMA....319 EXERCCIO: MODIFICANDO A BARRA DE STATUS PARA EXIBIR PAINIS 320

17.3.3.1. 17.3.4.1. 17.3.5.1. 17.3.5.2.

USANDO BARRAS DE UTILITRIOS .........................................................315 USANDO BARRAS DE STATUS ....................................................................319

17.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 17........................................322

18.

CAPTULO 18: SADAS GRFICAS.......................................................323

18.1. GRFICOS COM GDI+ .........................................................................................323 18.1.1. OS namespaces System::Drawing.....................................................................323 18.1.2. A CLASSE Graphics ......................................................................................324 18.1.3. CRIANDO OBJETOS Graphics ...................................................................325 18.1.4. DESENHANDO OBJETOS ..............................................................................325
18.1.4.1. 18.1.4.2. OBJETOS Pen ...........................................................................................................325 OBJETOS Brush ......................................................................................................325

18.1.5. 18.1.6. 18.1.7.

CANETAS E PINCIS PADRONIZADOS ....................................................326 OPERAES DE DESENHO..........................................................................326


EXERCCIO: DESENHANDO FORMAS SIMPLES...............................................327 EXERCCIO: MANIPULANDO EVENTOS Paint E INTERAGINDO COM O 329

18.1.6.1. 18.1.7.1. MOUSE

EVENTOS Paint..............................................................................................329

18.1.8. 18.1.9. 18.2. 18.3. 18.4.

USANDO CORES ..............................................................................................331 USANDO FONTES ............................................................................................332


EXERCCIO: CRIANDO E USANDO UMA Font.................................................332 EXERCCIO: CRIANDO UMA LISTA PARA TEXTO ..........................................334 EXERCCIO: MOSTRANDO UM BITMAP EM UMA FORMA............................336 EXERCCIO: USANDO A FUNCIONALIDADE DE IMPRESSORA DENTRO DA 337

18.1.9.1. 18.1.9.2. 18.2.1.1. 18.3.1.1. GDI+

MANIPULANDO IMAGENS.................................................................................336 IMPRESSES.........................................................................................................337 RPIDAS REFERNCIAS SOBRE O CAPTULO 18........................................340 ix

19.

CAPTULO 19: TRABALHANDO COM ARQUIVOS...............................341

19.1. O namespace System::IO ........................................................................................341 19.2. I/O DE TEXTO USANDO LEITORES E ESCRITORES ....................................342 19.2.1. USANDO TextWriter ........................................................................................342
19.2.1.1. EXERCCIO: ESCREVENDO DADOS DE CARACTERES PARA UM ARQUIVO DE TEXTO ...................................................................................................................................343

19.2.2. 19.2.3.

A CLASSE FileStream .................................................................................344 USANDO TextReader........................................................................................345


EXERCCIO: PROGRAMA SIMILAR AO UNIX more..........................................345

19.2.3.1.

19.3. TRABALHANDO COM ARQUIVOS E PASTAS .................................................347 19.3.1. OBTENDO INFORMAES SOBRE ARQUIVOS E PASTAS .................348
19.3.1.1. EXERCCIO: USANDO AS CLASSES DE MANIPULAO DE PASTAS E ARQUIVOS..................................................................................................................................350

19.4. I/O BINRIA...........................................................................................................355 19.4.1. A CLASSE BinaryWriter..................................................................................355 19.4.2. A CLASSE BinaryReader .................................................................................355
19.4.2.1. EXERCCIO: USANDO AS CLASSES BinaryReader E BinaryWriter .....356

19.5.

RPIDAS REFERNCIAS SOBRE O CAPTULO 19........................................360

INTRODUO Desde a sua introduo em 2002, as pessoas tm concentrado suas atenes nas diversas caractersticas novas que constituem parte do Microsoft .NET, tais como a principal mudana para o Microsoft Visual Basic, a introduo do C#, os novos modelos ASP.NET e ADO.NET, e o crescente uso do XML. Os desenvolvedores de C++ no precisam se sentir esquecidos pois uma substancial quantidade de novas caractersticas no Microsoft Visual C++ .NET torna o C++ um membro de primeira classe da famlia .NET de linguagens de programao. Este livro lhe fornece uma introduo s novas caractersticas especficas .NET que a Microsoft adicionou ao Visual C++ para suportar desenvolvimento .NET e ser completamente atualizado para se livrar do Microsoft .NET Framework 1.1. Esta nova funcionalidade chamada de Extenses Gerenciadas para C++ e, alm de suprir os programadores C++ com acesso a toda a funcionalidade das bibliotecas de classe .NET, ela tambm permite operaes com cdigos C++, objetos COM e APIs Win32 existentes. REQUERIMENTOS DO SISTEMA Para completar os exerccios neste livro, voc precisar ter o Microsoft Visual Studio .NET instalado. O Visual Studio .NET est disponvel em vrias edies; Voc precisar de uma das seguintes: Visual C++ .NET Standard; Visual Studio .NET Professional; Visual Studio .NET Enterprise

A menos que voc tenha comprado este livro como parte da edio de luxo de aprendizagem do Visual Studio .NET, o software Visual Studio .NET no est includo no livro. Voc deve comprlo separadamente e instal-lo antes de iniciar os exerccios deste livro. Voc tambm precisar de um computador capaz de executar o Visual Studio .NET. Para detalhes completos, verifique o pacote de produtos ou a pgina eletrnica do Microsoft Visual Studio (http://msdn.microsoft.com/vstudio). INSTALANDO E USANDO OS ARQUIVOS PARA PRTICA O CD que acompanha este livro contm os arquivos para prtica que voc usar como exerccios no livro. Usando os arquivos para prtica voc no perder tempo para criar objetos que no sejam relevantes para um dado exerccio. Assim, ter mais tempo para se concentrar em aprender a programao orientada para objetos com as extenses gerenciadas para C++. Os arquivos e as instrues passo-a-passo nas lies tambm o direcionam a aprender fazendo, o que um modo fcil e efetivo para adquirir e relembrar novas habilidades. IMPORTANTE: Antes de quebrar o selo no pacote do CD de acompanhamento, assegure-se de que este livro corresponde verso do software. Este livro foi projetado para uso com o Microsoft Visual Studio .NET para sistemas operacionais do Microsoft Windows. Para descobrir qual o software que voc est executando, verifique o pacote do produto ou inicialize o software e, ento, clique em About Microsoft Development Environment no menu Help no topo da tela. xi

Instalando os Arquivos para prtica Siga os passos abaixo para instalar os arquivos para prtica no disco rgido do seu computador para que voc possa utiliz-los com os exerccios deste livro: PASSO 1: Insira o CD de acompanhamento no drive de CD-ROM do seu computador. IMPORTANTE: Em alguns computadores, a tela do menu de inicializao pode aparecer automaticamente quando voc fechar o drive de CD-ROM. Neste caso, siga as instrues na tela, pulando os PASSOs 2, 3 e 4. PASSO 2: No menu Start, clique em My Computer ou d um clique duplo no cone My Computer no desktop; PASSO 3: D um clique duplo no cone do drive de CD-ROM do seu computador; PASSO 4: D um clique duplo em StartCD.exe. Uma tela de menu de inicializao aparece com as opes que lhe permitem ver, navegar e instalar os contedos do CD-ROM, alm de acessar a pgina eletrnica da Microsoft Press para suporte tcnico. PASSO 5: Clique em Install Practice Files. Clique ok na caixa de mensagens inicial. A janela de configurao do programa aparece com as opes recomendadas pr-selecionadas para voc. Para otimizar os resultados no uso dos arquivos para prtica com este livro, aceite a configurao prselecionada. Uma pasta chamada Microsoft Visual C++ .NET Step By Step foi criada em Meus Documentos, e os arquivos para prtica foram ali colocados. Desinstalando os Arquivos para Prtica Siga os passos abaixo para remover os arquivos para prtica do seu computador. Estes passos foram escritos para o sistema operacional Windows XP Profissional. Se voc estiver usando uma verso diferente do Windows, use as referncias da documentao de ajuda do Windows para remover programas. PASSO 1: Clique Iniciar Painel de Controle; PASSO 2: No Painel de Controle, d um clique duplo em Adicionar ou Remover Programas; PASSO 3: Na janela Adicionar ou Remover Programas, clique em Microsoft Visual C++ .NET Step By Step na lista Programas e Atualizaes Instalados; PASSO 4: Clique em Alterar/Remover. Aparece a caixa de dilogo Confirmar Desinstalao. Clique Sim para deletar os arquivos para prtica. CONVENES E CARACTERSTICAS NESTE LIVRO Este livro apresenta informaes usando convenes projetadas para tornar mais legvel a informao e mais fcil as conexes entre os tpicos. O livro tambm inclui caractersticas que contribuem para estudo mais aprofundado do material. Convenes xii Cada exerccio constitui uma srie de tarefas. Cada tarefa apresentada como uma srie de passos rotulados. Se uma tarefa tiver apenas um passo, este indicado por um ponto. As notas rotuladas por DICAS fornecem mais informaes para completar um passo com sucesso.

As notas rotuladas por IMPORTANTE chamam a sua ateno para informaes que precisam ser verificadas antes de continuar o estudo. O texto que voc deve digitar aparece em negrito. Os termos so mostrados em itlico quando so definidos. Um sinal mais (+) entre dois nomes de tecla significa que voc deve pressionar estas pressionar estas teclas ao mesmo tempo. Por exemplo, Alt + Tab significa que voc deve manter Alt pressionada enquanto pressiona Tab.

Outras Caractersticas deste Livro Voc pode aprender tcnicas especiais, informaes bsicas ou caractersticas relacionadas com a informao que estiver sendo discutida lendo as barras laterais sombreadas que aparecem ao longo das lies. Estas barras laterais freqentemente realam terminologia difcil ou sugerem futuras reas para explorao. Voc pode obter uma lembrana rpida de como realizar as tarefas que aprendeu lendo as REFERNCIAS RPIDAS no final da lio. CORREES, COMENTRIOS E AJUDA Todo o esforo foi feito para assegurar a preciso deste livro e dos contedos dos arquivos para prtica no CD. A Microsoft Press fornece correes e contedos adicionais para seus livros atravs da pgina http://www.microsoft.com/mspress/support/. Se voc tiver problemas, comentrios ou idias relativas a este livro ou ao CD de acompanhamento, por favor as envie para a Microsoft Press, email mspinput@microsoft.com, ou para o endereo postal Microsoft Press Attn: Step by Step Series Editor One Microsoft Way Redmond, WA 98052-6399 Por favor, note que o suporte para o software Visual Studio .NET no oferecido atravs dos endereos acima. Para ajuda a respeito do Visual Studio .NET, visite http://support.microsoft.com. VISITE A PGINA ELETRNICA DA MICROSOFT PRESS Voc tambm convidado a visitar a pgina da Microsoft Press, www.microsoft.com/mspress. Ali voc encontrar descries para a linha completa dos livros da Microsoft Press, informaes sobre a compra de ttulos, notcias sobre caractersticas e eventos especiais, contedos adicionais para livros da Microsoft Press, e muito mais. Voc tambm pode encontrar os desenvolvimentos mais recentes do Visual Studio .NET e as notcias sobre a Microsoft Corporation visitando http://msdn.microsoft.com/vstudio/nextgen.

xiii

PARTE 1: INICIALIZAO EM C++ .NET 1. HELLO, C++!

Neste captulo, voc aprender como Reconhecer caractersticas C++; Reconhecer funes C++; Reconhecer palavras-chaves e identificadores C++; Criar um programa C++.

Bem-vindo ao excitante mundo da programao .NET com o Microsoft Visual C++. Este captulo introduz a linguagem C++ e entradas/sadas simples (input/output = I/O) usando as flexibilidades do console da biblioteca padro C++.

1.1.

O QUE UM PROGRAMA C++?

Bem, um programa C++ contm exatamente os mesmos elementos de qualquer outro programa de computador dados para armazenar suas informaes e blocos de cdigo que manipulam estes dados. Se voc j tem experincia com o Microsoft Visual Basic ou com o velho COBOL dos anos 1940, os elementos bsicos lhe sero familiar. Muitos mistrios cercam a prtica de programao C++, mas a maioria deles no se justifica e desnecessria. Vamos comear com algumas regras fundamentais para C++.

1.1.1.

C++ UMA LINGUAGEM FORTEMENTE VINCULADA A TIPOS

Se voc tiver uma varivel que foi declarada com capacidade de armazenar mas, nela voc so pode armazenar mas. Porm, esta exigncia no to severa quanto soa. C++ contm muitas caractersticas para fornecer converses implcitas onde for necessrio. Esta intensa checagem de tipo elimina muitos bugs comuns em programao porque ela explicitamente probe quaisquer converses entre tipos de dados que possam resultar em perda de dados.

1.1.2.

C++ UMA LINGUAGEM EFICIENTE

Se voc escreve cdigos que precisam executar rapidamente (por exemplo, cdigos de procura em listas ou que realizam clculos matemticos com complexos), C++ pode ser a sua linguagem escolhida.

1.1.3.

C++ UMA LINGUAGEM ORIENTADA PARA OBJETOS

Programadores modernos gostam das muitas vantagens da programao orientada para objetos (veja o captulo 2 para mais informaes sobre programao orientada para objetos). C++ uma das primeiras linguagens de programao orientada para objetos.

14

1.1.4.

C++ BASEADA EM C (COMO VOC J SUSPEITAVA)

C uma linguagem bem estabelecida. C++ inclui algumas caractersticas estranhas para manter sua compatibilidade com C, mas provavelmente ela no seria to popular sem esta compatibilidade.

1.1.5.

C++ UMA LINGUAGEM SENSVEL A CARACTERES MAISCULOS E MINSCULOS

Se o compilador lhe adverte sobre uma varivel no declarada, provavelmente voc digitou um caractere maisculo ao invs de um minsculo (ou vice-versa).

1.2.

SEU PRIMEIRO PROGRAMA C++

Agora vamos sujar nossas mos com um programa C++ simples. Naturalmente, nenhum livro sobre C++ estaria completo sem incluir o famoso programa Hello, World. Assim, vamos comear com ele.
#include <iostream> using namespace std; int main()//************************************************************** { cout << "Hello, World!" << endl; return 0; }//***********************************************************************

Este pequeno programa ilustra alguns conceitos fundamentais: A primeira linha usa a diretiva #include para dizer ao compilador C++ que copie o arquivo chamado iostream no incio do programa. Por que esta incluso necessria? Uma regra de ouro de C++ que qualquer coisa deve ser declarada antes de ser usada, incluindo o fluxo de sada chamado cout usado posteriormente no programa (o fluxo de sada cout provoca sada no console). Assim, por que no declarar cout explicitamente neste arquivo? Porque o arquivo iostream uma unidade separada que pode ser includa em qualquer programa, o que facilita a reutilizao das declaraes. Em outras palavras, este arquivo economiza digitao por parte do programador. A segunda linha (que comea com using) diz ao compilador que a biblioteca C++ padro utilizada (std uma abreviatura de standard). Muitas bibliotecas diferentes podem ser usadas em um nico projeto. A declarao using nos permite dizer ao compilador qual a biblioteca que pretendemos usar. O restante do programa um exemplo de uma funo C++. Todos os blocos de cdigo em C++ so chamados funes no h entidades como um procedimento ou uma sub-rotina. Cada funo C++ contm o cabealho (a primeira linha deste programa) e o corpo (todo o texto entre as chaves { e }). O cabealho mostra o valor de retorno da funo (no caso int, de integer), o nome da funo (main) e a lista de parmetros dentro dos parnteses. A funo deste exemplo no tem parmetros. Assim, o espao entre os parnteses est vazio mas os parnteses devem estar presentes. Todas as declaraes em C++ terminam com o ponto-e-vrgula.

Das sete linhas do texto no programa do exemplo, apenas duas apenas duas so declaraes C++: a que contm cout e a que contm return. A linha de cout mostra caracteres no console. A sintaxe para 15

uso de cout consiste na palavra cout seguida por um operador <<, seguido pelos itens que voc quer imprimir na tela (o operador de manipulao de fluxo endl insere um caractere de nova linha no fluxo). Voc imprimir muitos itens usando um nico cout. Basta separar quaisquer itens extras pelo operador <<, como no exemplo:
cout << Hello << , << World << endl;

Alternativamente, voc pode usar vrias declaraes de cout produzindo exatamente o mesmo efeito:
cout cout cout cout << << << << Hello; , ; World; endl;

Como de se esperar, os programadores tendem a preferir a verso aninhada em uma nica declarao.

1.2.1.

A FUNO main

Por que a nica funo deste exemplo se chama main? A resposta simples que o exemplo no compilaria se a funo no se chamasse main. Porm, pode ser mais til explicar como a linguagem funciona. Um programa C++ normal contm muitas funes (e tambm muitas classes, como discutiremos no captulo 2). Como o compilador sabe qual a primeira funo a ser chamada? Obviamente, no podemos permitir que ele escolha aleatoriamente uma funo! A regra que o compilador sempre ir gerar cdigo que olhe para uma funo chamada main. Se voc omitir a funo main, o compilador lhe informar que houve um erro e no criar um programa executvel no final. LINGUAGENS DE FORMATO LIVRE A linguagem C++ tem formato livre, o que significa que o compilador ignora todos os espaos, retornos de carro, caracteres de nova linha, tabulaes, smbolos de formas, etc.. Coletivamente, estes caracteres so referenciados como espaos em branco. A nica vez que o compilador reconhece espao em branco quando ele ocorre dentro de uma string (seqncia de caracteres). As linguagens de formato livre do muita liberdade de ao ao programador para usar tabulao ou espao de pargrafo como modo de organizar o layout de um programa. As declaraes dentro de um bloco de cdigo como, por exemplo, em um lao for ou em uma declarao condicional if so tipicamente so recuadas como pargrafos (freqentemente um recuo de 4 caracteres). Este recuo facilita a visualizao do contedo de um bloco completo. A natureza do formato livre de C++ d origem a uma das polmicas mais comuns (e pouco til) na comunidade C++: como recuar as chaves? Devem ser recuadas com o cdigo ou devem permanecer um pargrafo esquerda indicando os limites de declaraes como o lao for e o condicional if? No h resposta certa ou errada para esta questo (fica no gosto pessoal), mas um usurio consistente, qualquer que seja o seu estilo, tornar seu programa mais legvel. Do ponto de vista da compilao, seu programa poderia ser escrito em uma nica linha! Portanto, o compilador esperar uma funo chamada main. Isto tudo o que devemos inform-lo? Bem, tudo no. H alguns itens adicionais como, por exemplo, o tipo de retorno e os parmetros devem estar corretamente informados mas no caso de main, algumas das regras de C++ so 16

relaxadas. Em particular, main pode receber parmetros que representam os argumentos de linha de comando, mas voc pode omiti-los se no quiser usar a linha de comando.

1.2.2.

PALAVRAS-CHAVES E IDENTIFICADORES

Uma palavra-chave C++ (tambm chamada palavra reservada) um item especial de texto que o compilador espera ser usado de um modo particular. As palavras-chaves usadas no programa do exemplo so using, namespace e return. Voc no pode usar estas palavras-chaves como variveis ou nomes de funo o compilador indicar um erro se isto ocorrer. Um identificador qualquer nome que o programador use para representar variveis e funes. Um identificador deve comear por uma letra e deve conter apenas letras, nmeros ou sublinhados. Os identificadores abaixo so legais em C++:
My_variable AReallyLongName

Os identificadores mostrados na Tabela 1.1 no so aceitos em C++. Tabela 1.1: Exemplos de identificadores proibidos em C++.
Identificador 0800Number You+Me return Justificativa Identificadores no devem comear por nmeros. Identificadores devem conter apenas letras, nmeros e sublinhados. Identificadores no podem ser palavras-chaves.

ERRO DO COMPILADOR OU DO VINCULADOR Para ser absolutamente correto, o vinculador (linker) que nos informa mensagens de erro. Voc encontrar mais sobre erros de compilador e vinculador ao longo deste captulo. Tirando estas restries, qualquer identificador funcionar (estranhamente, o identificador main no uma palavra reservada. Voc pode definir uma varivel chamada main dentro do programa mas isto no recomendado). A Tabela 1.2 mostra algumas escolhas no recomendadas. Tabela 1.2: Nomes de identificadores permitidos mas no recomendados.
Identificador main INT B4ugotxtme _identifier1 Justificativa Pode ser confundido com a funo main. Tambm parecido com a palavra-chave int. Identificador muito complicado. Sublinhados no incio dos nomes so permitidos, mas no so recomendados porque os compiladores freqentemente usam este O procedimento quando criam nomes de variveis internas. procedimento tambm pode ser usado para variveis no cdigo do sistema. Para evitar graves conflitos ao nomear variveis, voc no dever usar sublinhado como primeiro caractere.

17

1.3.

CRIANDO UM PROGRAMA EXECUTVEL TEORIA

Vrios estgios so requeridos para construir um programa executvel. O Microsoft Visual Studio .NET nos ajuda a automatiz-los. Porm, para examinar e compreender estes estgios, vamos dar uma rpida observada neles.

1.3.1.

EDITANDO OS ARQUIVOS-FONTES DO PROGRAMA

Antes de criar um programa, voc deve escrever alguma coisa! O Visual Studio .NET fornece um editor C++ integrado, com sintaxe realada por cores e o Microsoft IntelliSense para mostrar informaes sobre os parmetros das funes e complemento de palavras.

1.3.2.

COMPILANDO OS ARQUIVOS-FONTES

O compilador C++ o utilitrio que converte arquivos-fontes de texto no cdigo de mquina dos arquivos-objetos cuja extenso .obj (o objeto neste sentido nada tem a ver com programao orientada para objetos). O compilador invocado dentro do ambiente do Visual Studio .NET e quaisquer erros ou cuidados so mostrados na rea de trabalho. Porm, os arquivos-objetos produzidos ainda no so arquivos executveis. Eles esto incompletos, faltando referncias para quaisquer funes no contidas nos arquivos-fontes para a compilao particular.

1.3.3.

VINCULANDO OS ARQUIVOS-OBJETOS

O passo final na produo de um arquivo executvel a vinculao de todos os arquivos-objetos para compor um projeto particular. Isto inclui juno no apenas dos arquivos-objetos produzidos do seu prprio cdigo fonte, mas tambm os arquivos-objetos das bibliotecas do sistema como, por exemplo, a biblioteca C++ padro ou a biblioteca Microsoft Foundation Class (MFC). Erros de vinculao tendem a ser menos teis do que erros de compilao. Os erros de compilao fornecero o nome do arquivo e o nmero da linha onde ocorreu o erro; o vinculador fornecer apenas o nome do arquivo-objeto. Assim, freqentemente precisamos agir como um detetive para solucionar tais erros. BIBLIOTECAS DE SISTEMA E DE CLASSE As bibliotecas de sistema e de classe freqentemente tm muitos arquivos .obj a elas associados. Manipular todos os arquivos-objetos da uma biblioteca em um projeto ficaria bastante oneroso. Assim, por convenincia, os arquivos .obj freqentemente so combinados em arquivos de biblioteca .lib.

1.3.4.

EXECUTANDO E TESTANDO O PROGRAMA

Embora a vinculao seja o passo final na criao de um arquivo executvel, ela no o ltimo passo no desenvolvimento. Voc ainda precisa executar e testar o programa. Para muitos ambientes, a execuo e o teste freqentemente so as partes mais difceis do ciclo de desenvolvimento do programa. Porm, o Visual Studio .NET ainda tem outro s escondido na sua manga o depurador integrado. O depurador tem um sortido conjunto de caractersticas que 18

permitem fcil depurao em tempo de execuo, como, por exemplo, fixao de pontos de ruptura (breakpoints) e assistncia de variveis.

1.4.

CRIANDO UM PROGRAMA EXECUTVEL PRTICA

Quando voc inicializa o Microsoft Visual Studio .NET, surge a convidativa janela mostrada na Figura 1.1.

Figura 1.1: Janela do Microsoft Visual Studio .NET. Esta janela o potente ambiente de desenvolvimento integrado do Visual Studio .NET (IDE = Integrated Development Environment). Ela contm todos os utilitrios que voc precisar para criar aplicativos com caractersticas completas e de fcil manuseio.

1.4.1.

CRIANDO UM PROJETO

A primeira tarefa criar um novo projeto para o programa Hello, World. Para criar um projeto siga os passos: PASSO 1: Clique em File New Project... (alternativamente, pressione o conjunto de teclas Ctrl + Shift + N). A caixa de dilogo New Project ser mostrada (Figura 1.2). PASSO 2: Selecione Visual C++ Projects na caixa Project Types, selecione o cone Win32 Console Project na caixa Templates e, ento, digite HelloWorld na caixa de texto Name. PASSO 3: Escolha uma localizao para seu novo projeto na caixa combo Location, ou digite o caminho da pasta, ou clique no boto Browse e procure a pasta apropriada. 19

Figura 1.2: Caixa de dilogo New Project. PASSO 4: Clique OK para inicializar o Win32 Application Wizard (Figura 1.3).

Figura 1.3: Caixa de dilogo do Win32 Application Wizard. Este projeto possuir uma interface simples, baseada em texto (similar a uma tela do MS-DOS) que ser adequada para nossos propsitos. Entretanto, antes de prosseguir voc precisa mudar uma configurao. PASSO 5: Clique na legenda Application Settings no lado esquerdo na caixa de dilogo da Figura 1.3. O lado direito muda para mostrar a configurao atual (Figura 1.4). PASSO 6: Em Additional Options, marque a caixa Empty project. PASSO 7: Clique em Finish para criar o projeto. 20

Figura 1.4: Configurao padro para projetos Win32 Console. O Win32 Application Wizard inicializa corretamente todas as configuraes do compilador para um projeto Win32 Console.

1.4.2.

ADICIONANDO UM ARQUIVO-FONTE C++ AO PROJETO

Um projeto vazio no serve para nada. Assim, vamos adicionar um novo arquivo-fonte C++. Como sempre ocorre no Visual Studio .NET, voc tem muitas maneiras de realizar uma tarefa. Eis os passos: PASSO 1: Clique com o boto direito do mouse no cone HelloWorld no Explorer Solution, aponte para Add Add New Item..., ou clique no boto Add New Item na barra de utilitrios (Figura 1.5).

Figura 1.5: cone Add New Item da barra de utilitrios. Qualquer uma das aes abrir a caixa de dilogo Add New Item mostrada na Figura 1.6. PASSO 2: Selecione C++ File (.cpp) da caixa Templates, digite HelloWorld na caixa de texto Name e clique no boto Open. O Visual Studio .NET cria um arquivo de cdigo fonte vazio e o adiciona ao projeto para voc. Agora hora de aposentar o mouse, tomar o teclado e comear a digitar algum cdigo C++.

21

Figura 1.6: Caixa de dilogo Add New Item.

1.4.3.

ADICIONANDO CDIGO C++ AO ARQUIVO-FONTE

Digite no arquivo-fonte HelloWorld.cpp, o cdigo mostrado na Figura 1.7.

Figura 1.7: Cdigo do arquivo-fonte HelloWorld.cpp. Note que as palavras reservadas automaticamente se tornam azuis (contanto que voc as digite corretamente).

22

1.4.4.

CONSTRUINDO O EXECUTVEL

O prximo passo construir o executvel. O termo build no Visual Studio .NET se refere compilao e vinculao do programa. O Visual Studio .NET compila quaisquer arquivos-fontes que tenham sido alterados na atual edio e se no houver erros de compilao realiza um vnculo. Para construir o executvel, selecione o menu Build Build Solution ou pressione o conjunto de teclas Ctrl + Shift + B. Na parte inferior da janela do Visual Studio .NET, aparecer a janela Output exibindo o progresso da construo. Se no houver erros, veremos em Output a mensagem:
Build: 1 succeded, 0 failed, 0 skipped

Se ocorrer algum problema, a janela Output conter uma lista de erros e avisos, como mostrado na Figura 1.8.

Figura 1.8: Janela Task List mostrando lista de erros no Visual Studio .NET. Se o erro ou o aviso for gerado pelo compilador e no pelo vinculador, d um clique duplo na linha do erro na janela Output para colocar o cursor na linha do arquivo-fonte onde o compilador encontrou o erro. Corrija o erro (no caso da Figura 1.8, esquecemos de colocar o ponto-e-vrgula aps endl) e reconstrua o projeto. COMO DEVEMOS TRATAR OS AVISOS? Sempre considere os avisos como erros em outras palavras, tente se livrar deles. Os avisos tm uma razo para existir significam que o cdigo no est totalmente correto.

23

1.4.5.

EXECUTANDO O PROGRAMA

Uma vez construdo o projeto, sem erros ou avisos, voc finalmente pode executar o programa. Clique no menu Debug Start Without Debugging ou pressione Ctrl + F5 para executar o programa. A Figura 1.9 mostra a sada do programa. A linha Press any key to continue adicionada pelo IDE (ambiente de desenvolvimento integrado) de modo que a janela console no desaparece quando o programa finaliza a execuo.

Figura 1.9: Janela console do programa HelloWorld.

1.5.

CONCLUSES

Embora o exemplo neste captulo no seja o programa mais poderoso que j se escreveu, ele demonstra alguns pontos-chaves no desenvolvimento C++. Ele introduz o IDE do Visual Studio .NET e o habilita para compilar e vincular um programa, alm de servir como uma introduo linguagem C++. Agora no h mais retorno. Toda nova caracterstica C++ e do Visual Studio .NET que voc aprender servir para incendiar sua imaginao a aprender mais e ser produtivo. O desenvolvimento de software passa ser um mundo excitante. Finalmente, no se esquea de relaxar. Volte e experimente algumas variaes no programa deste captulo, clique nos menus e se familiarize com o ambiente. Voc nada tem a perder.

1.6.

RPIDAS REFERNCIAS SOBRE O CAPTULO 1

A Tabela 1.3 mostra um resumo deste captulo. Tabela 1.3: Resumo do captulo 1.
PARA Criar um novo projeto no Visual Studio .NET Adicionar um arquivo a um projeto Construir um projeto no Visual Studio .NET Executar um programa dentro do Visual Studio .NET FAA ISTO Use o menu File New Project, ou Ctrl + Shift + N. Use o menu File New File, ou Ctrl + N. Use o menu Build Build Solution, ou Ctrl + Shift + B. Use menu Debug Start Without Debugging, ou Ctrl + F5.

24

2.

CAPTULO 2: INTRODUO PROGRAMAO ORIENTADA PARA OBJETOS

Neste captulo, voc aprender Os conceitos-chaves da programao orientada para objetos; Como estes conceitos so suportados pelos construtores da linguagem C++; Sobre os principais benefcios de desenvolvimento da programao orientada para objetos; Como criar e usar classes simples.

2.1.

O QUE PROGRAMAO ORIENTADA PARA OBJETOS?

A programao orientada para objetos tem a vantagem de ser um paradigma natural no qual os sistemas so desenvolvidos. Ns percebemos o mundo constitudo de objetos: mesas, cadeiras, computadores, carros, contas bancrias, partidas de futebol e esboos. Tambm uma caracterstica natural do homem tentar organizar estes, ajustando-os segundo alguma classificao, destacando algumas caractersticas dos objetos em detrimento de outras. Assim, cachorros e gatos so mamferos, torradeiras e refrigeradores so eletrodomsticos, futebol e tnis so esportes, caminhes e carros so veculos, e assim por diante. Pode haver muitos nveis para estas categorias e muitos modos de classificar os objetos no mundo. Como as pessoas classificam os objetos depende muito do que desejam fazer com eles e das caractersticas que realam estes desejos. Por exemplo, provvel que um engenheiro eltrico classifique eletrodomsticos em categoria diferente e em alguns aspectos mais fundamentada e com mais recursos do que um professor poderia classificar. Quando agrupamos objetos em esquemas de classificao, tambm tendemos a realar certos atributos dos objetos em detrimento de outros. Por exemplo, a cor de um carro pode no se manter na mente de um engenheiro, mas pode ser aspecto pesadamente relevante em um modelo mental para classificao de carro de um vendedor de Ferrari. A idia de construir hierarquias de objetos relacionados usada em programao orientada para objetos. Desde os anos 1960, os pesquisadores perceberam que muitas entidades dos modelos de programa computacional podem ser nomeadas e suas propriedades e comportamentos podem ser descritos. Eles notaram que os programas lidam com dados como contas bancrias, matrizes, arquivos e usurios, que so anlogos aos objetos no mundo real. A programao orientada para objetos a grosso modo ser caracterizada por identificar os objetos relevantes, organiz-los em hierarquias, adicionar os atributos aos objetos que descrevem as caractersticas relevantes no contexto do problema, e adicionar funes (mtodos) para os objetos realizarem as tarefas requeridas num dado objeto. Os detalhes so um pouco mais complicados, mas, essencialmente, este um processo simples e natural. Ser simples e natural no significa necessariamente ser fcil. Uma coleo de objetos pode ser classificada de modos virtualmente incontveis. A chave ter habilidade para identificar os atributos importantes dos objetos e formar boas abstraes e hierarquias apropriadas. Mesmo dentro do contexto de um domnio do problema, s vezes difcil determinar nveis corretos de abstrao e hierarquias de classificao satisfatria. Mesmo para decidir a que classe ou agrupamento um dado objeto pertence pode ser muito difcil. Como disse WITTGENSTEIN, (1953), alguns objetos suportaro mais de uma semelhana familiar para um conceito do que outros. Por exemplo, futebol e tnis so, obviamente, mais esportes do que xadrez e nado sincronizado.

25

2.2.

CARACTERSTICAS DAS LINGUAGENS DE PROGRAMAO ORIENTADA PARA OBJETOS

J salientamos que programao orientada para objetos significa definir e construir hierarquias de objetos, e definir suas propriedades e comportamento. At certo ponto, voc pode fazer isto em qualquer linguagem de programao, do mesmo modo que voc pode viajar pelo mundo de bicicleta. Contudo muito mais fcil programar orientado para objetos se voc usar uma linguagem que projetada para suportar mtodos de programao orientada para objetos. As linguagens que suportam programao orientada para objetos como C++ e C# so caracterizadas por: Encapsulamento; Herana e Polimorfismo.

Linguagens que possuem estas caractersticas suportam o processo natural de identificao e classificao de objetos.

2.2.1.

ENCAPSULAMENTO

Um dos problemas enfrentados por desenvolvedores de softwares que os sistemas que desenvolvemos so cada vez maiores e mais complexos. O encapsulamento nos ajuda a dividir um programa em pequenas entidades auto-suficientes. Por exemplo, se voc constri um sistema de contabilidade, provavelmente ir precisar de objetos para representar contas e faturas. Uma vez que voc j tenha desenvolvido a classe Account, no precisar mais se preocupar com detalhes da implementao desta classe. Voc pode usar a classe em qualquer lugar do seu programa de modo similar utilizao de tipos, como, por exemplo, inteiros. A classe expor as caractersticas essenciais do objeto Account escondendo os detalhes da implementao. O nome da conta e o estado do seu balancete so alguns dos atributos do objeto que o cliente est interessado e precisa conhecer. Detalhes de como o nome da conta armazenado se ele est em uma matriz de 50 caracteres ou em um objeto string, ou o fato de que o balancete da conta mantido como uma varivel atual so irrelevantes para o cliente. O processo de esconder as estruturas de dados e os detalhes de implementao de um objeto em relao a outros objetos no sistema chamado encapsulamento de dados. Ele evita que outros objetos acessem detalhes que no precisam ser mostrados. O encapsulamento facilita a compreenso de programas grandes. O ato de esconder dados torna os programas mais robustos. Objetos podem interagir com outros objetos apenas atravs dos atributos expostos publicamente e dos mtodos do objeto. Quanto mais atributos e mtodos expostos publicamente, mas difcil ser modificar a classe sem afetar o cdigo que a utiliza. Uma varivel escondida poderia ser mudada de um long para um double sem afetar o cdigo que usa objetos criados (colocados em instncia) desta classe. O programador teria que se preocupar apenas com os mtodos na classe que acessasse esta varivel, ao invs de se preocupar com todos os lugares no programa que um objeto colocado em instncia desta classe pudesse ser chamado.

2.2.2.

HERANA

A tendncia natural do homem de classificar objetos em hierarquias til na perspectiva de um programador e suportada em todas as verdadeiras linguagens orientadas para objetos, incluindo C++, por herana. A herana fornece duas vantagens para o programador C++. A primeira, e a mais 26

importante, permite que ele construa hierarquias que expressem as relaes entre tipos. Suponha que voc tenha duas classes, SavingsAccount e CheckingAccount, ambas derivadas da classe-me Account. Se tiver uma funo que requeira uma Account como um argumento, voc pode passar para esta funo uma SavingsAccount ou uma CheckingAccount porque ambas as classesfilhas so do tipo Account. Account uma classificao geral e CheckingAccount e SavingsAccount so tipos mais especficos. A segunda vantagem da programao orientada para objetos que as classes podem herdar caractersticas das classes de hierarquia mais alta. Ao invs de desenvolver novas classes do nada, estas classes novas podem herdar a funcionalidade das classes existentes e, ento, voc pode modificar ou estender sua funcionalidade. A classe-me da qual a classe nova herda caractersticas chamada de classe-base. A classe nova conhecida como classederivada. Uma das principais tarefas enfrentadas pelos desenvolvedores encontrar classificaes apropriadas para os objetos e classes para seus programas. Por exemplo, se voc precisa desenvolver classes para um jogo de corridas, faz sentido desenvolver uma classe geral carro e, ento, usar esta como uma classe base para especificar tipos de carro tais como Jaguar ou Ford. Ento, estas classes derivadas estenderiam ou modificariam a classe geral carro, adicionando novos atributos e mtodos ou sobrescrevendo os mtodos existentes. Decompondo os objetos em sub-objetos por exemplo, um carro consiste de uma mquina e um chassi simplificaremos o esforo do desenvolvimento. Como resultado, cada um dos objetos se torna mais simples e, portanto, mais fcil de desenhar e implementar do que a coleo completa.

2.2.3.

POLIMORFISMO

A terceira caracterstica das linguagens de programao orientada para objetos o polimorfismo, que uma palavra grega significando muitas formas. Este um conceito de difcil definio. Assim, usaremos alguns exemplos para lhe mostrar o que polimorfismo e deixamos as definies mais precisas para escritores acadmicos. Polimorfismo essencialmente significa que as classes podem ter o mesmo comportamento mas implement-lo de modos diferentes. Considere vrios tipos diferentes de veculos: todos eles precisam ser ligados. Assim, em termos de programao, poderamos dizer que todos os veculos tm a funcionalidade start. Exatamente como a inicializao implementada depende do veculo: se for um Ford modelo T, inicializar significa ligar a chave de ignio; se for uma locomotiva a vapor, inicializar ser um processo muito diferente e mais complexo. Outro exemplo: Considere os tipos SavingsAccount e CheckingAccount acima mencionados. Todos os tipos derivados de Account compartilham algumas funcionalidades, tais como a habilidade para depsito, retirada e exame do balancete. As duas classes podem implementar estas funcionalidades de modos diferentes porque CheckingAccount pode permitir verificaes proibidas em SavingsAccount e vice-versa, mas ambas funcionariam do mesmo modo. Isto significa que se passarmos uma Account, no importa que tipo de conta exatamente. Ainda podemos depositar fundos, retirar e examinar saldos. Esta funcionalidade til em termos de programao porque ela lhe d a habilidade para trabalhar com tipos de objetos genricos contas e veculos quando voc no se preocupa com o modo como cada classe implementa a funcionalidade.

2.3.

CLASSES E OBJETOS

At este ponto do captulo, os termos classe e objeto foram usados praticamente como sinnimos. Porm, classes e objetos no so a mesma coisa e precisamos esclarecer as diferenas entre estes 27

termos. Como o nome insinua, a programao orientada para objetos se baseia em objetos. Um objeto composto de dados que o descrevem e de operaes que podem ser realizadas sobre ele. Porm, quando voc cria um programa em C++, precisa declarar e definir classes, no objetos. Uma classe um tipo definido pelo usurio. Ela tanto encapsula dados quanto mtodos que manipulam estes dados. Com exceo das funes static, voc no pode usar classes diretamente. Uma classe se parece mais com um modelo que usado para criar (instancializar) objetos. Exatamente do mesmo modo que voc declara uma varivel int antes de us-la, tem que instancializar um objeto da classe antes que ele possa ser usado. Por exemplo, voc no declara e define um objeto Animal. Ao invs disto, voc declarar e define uma classe Animal, com seus atributos e mtodos. A classe representa o conceito. Assim, a classe Animal no representa um animal especfico mas a classe de todos os animais. Quando voc quiser usar um objeto Animal, tem que instancializar o objeto Animal da classe. A classe pode ser considerada como a representao abstrata de uma entidade, enquanto a instancializao da classe o objeto a representao concreta.

2.4.

BENFCIOS NO ATO DO DESENVOLVIMENTO

H trs benefcios chaves para programao orientada para objetos: Compreensibilidade; Reutilizao; Extensividade.

Dividindo o cdigo em classes tornamo-lo mais compreensvel impondo uma estrutura medida que os programas crescem. O ideal unir sistemas orientados para objetos de classes pr-escritas e fazer as modificaes requeridas para suportar as novas funcionalidades usando herana para derivar novas classes das j existentes. As classes existentes so reutilizadas como blocos construdos e no so alteradas de nenhum modo. A criao de sistemas de componentes reutilizveis naturalmente leva a uma alta produtividade, o que provavelmente o benefcio mais freqentemente citado das abordagens orientadas para objetos. A programao orientada para objetos tambm deve resultar em sistemas de alta qualidade. As classes que so reutilizadas tendo sido desenvolvidas e testadas em projetos anteriores provavelmente contm menos bugs do que classes desenvolvidas do nada. Com o passar do tempo, os bugs so descobertos e corrigidos nestas classes, enquanto uma classe que escrita a partir do nada tem ainda que passar atravs dos mesmos processos de deteco e correo de bugs. As caractersticas da programao orientada para objetos (encapsulamento, herana e polimorfismo) tambm produzem benefcios. O encapsulamento transforma sistemas grandes em pequenos. Para uma grande extenso, independentemente do tamanho do sistema, o desenvolvedor simplesmente cria objetos. Os sistemas grandes podem requer mais objetos do que os sistemas pequenos, mas o nvel de complexidade que o desenvolvedor enfrenta no significativamente maior. A herana ajuda a melhorar a flexibilidade e a extensividade dos sistemas, conseqentemente reduzindo seus custos de manuteno. A derivao de novas classes de classes existentes produz funcionalidade adicional e permite a extenso do software sem alterar as classes existentes. Finalmente, o encapsulamento de dados tambm leva a sistemas mais seguros. O estado de um objeto pode ser modificado apenas pelos seus mtodos publicamente expostos, o que aumenta a previsibilidade do comportamento do objeto.

28

2.5.

UM EXEMPLO SIMPLES

Este exemplo lhe mostrar como: Criar uma classe; Instancializar objetos da classe; Acessar funes-membros e atributos da classe.

PASSO 1: Inicialize o Microsoft Visual Studio .NET. PASSO 2: Clique no menu File New Project. A caixa de dilogo New Project aparece na tela. PASSO 3: Em Project Types, selecione Visual C++ Projects. PASSO 4: Em Templates, selecione Console Application (.Net). Em Name, digite Animals. Escolha uma localizao para o novo projeto no disco rgido e clique OK (Figura 2.1).

Figura 2.1: Caixa de dilogo New Project. PASSO 5: No Solution Explorer, d um clique duplo no arquivo Animals.cpp que se encontra na pasta Source Files. PASSO 6: Imediatamente abaixo da linha using namespace System;, adicione a seguinte definio de classe:
__gc class Animal//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ { int legs; String *strName; };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Para declarar uma classe em C++, voc usa a palavra-chave class seguida por um nome para a classe como Animal, neste exemplo. Ento, voc lista todos os membros da classe entre as chaves { e }. At este PASSO voc criou uma classe Animal com uma varivel int para o nmero de suas pernas e uma varivel String para seu nome. Como falamos, nenhum outro programa ou classe podero ter 29

acesso a estas variveis. Os membros de uma classe dados e mtodos so privados (private) por padro e s podem ser acessados por mtodos da prpria classe. O C++ fornece trs modificadores de acesso public, private e protected para especificar a visibilidade dos vrios membros da classe. PASSO 7: Adicione a palavra public seguida por dois pontos em uma nova linha entre a chave inicial e a declarao da primeira varivel da classe, como mostrado abaixo:
__gc class Animal//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ { public: int legs; String *strName; };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

As declaraes de variveis aps a palavra-chave public tornam estas variveis acessveis. Porm, usualmente no uma boa idia permitir que outras classes e outras partes do seu programa acessem as variveis de uma dada classe. Como discutimos antes, na seo de encapsulamento, melhor no permitir que usurios acessem os detalhes da implementao de uma classe, controlando o acesso aos dados da classe atravs das funes. Neste exemplo, a palavra-chave private ser usada para evitar acesso direto varivel String da classe. A varivel int de nome legs permanece public, simplesmente para mostrar como, assim, ela pode ser acessada diretamente pelo programa principal. PASSO 8: Adicione a palavra-chave private seguida dos dois pontos entre a primeira varivel int e a segunda varivel String, como indicado abaixo:
__gc class Animal//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ { public: int legs; private: String *strName; };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Para fornecer acesso varivel private String, precisamos adicionar classe mtodos public de acesso para que outras funes manipulem o valor da string. PASSO 9: Aps a declarao da varivel int e antes do modificador de acesso private, adicione as seguintes declaraes de mtodos ou linhas de implementao:
__gc class Animal//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ { public: int legs; void SetName(String *name)//-----------------------------------------{ strName = strName->Copy(name); }//------------------------------------------------------------------String* GetName()//--------------------------------------------------{ return strName; }//------------------------------------------------------------------private: String *strName; };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

30

Como estes mtodos so funes pequenas, mais fcil declar-los e implement-los como funes inline. As funes inline sero abordadas no captulo 6, quando estudaremos classes com mais detalhes. A classe Animal, agora, est completa. A sintaxe da declarao :
class classname { //Palavras-chaves de controle de acesso (public, private ou protected) //A declarao das variveis e mtodos da classe };

Voc provavelmente notou a palavra-chave __gc. Esta palavra-chave uma das extenses gerenciadas para C++ que simplificam a interao com componentes do .NET Framework. Colocando __gc em frente da palavra-chave class, a classe se torna uma classe gerenciada. Quando o objeto instancializado, ele ser criado em tempo de execuo na heap (uma rea da memria reservada para estruturas de dados dinmicos) comum da linguagem (CLR, Common Language Runtime) e o operador new retornar o endereo de memria deste objeto. O tempo de vida de um objeto instancializado da classe ser gerenciado pelo coletor de lixo do .NET Framework. Quando o objeto cai fora do escopo, a memria usada por ele ser recolhida ao lixo e no precisaremos fazer nenhuma chamada explcita ao operador delete. Classes __gc so conhecidas como tipos de referncia porque na verdade a varivel no contm o objeto mas um ponteiro para a memria onde ele est. Porm, precisamos ter cuidado com a performance ao utilizarmos tipos de referncia. A memria tem que ser alocada da heap gerenciada, o que pode forar a ocorrncia de uma coleta de lixo. Alm disso, tipos de referncia tm que ser acessados via seus ponteiros, afetando tanto o tamanho quanto a velocidade do aplicativo compilado. Por causa destes problemas de performance, o .NET Framework tambm suporta tipos de valor. Os tipos de valor so objetos criados na stack (pilha). A varivel contm o prprio objeto ao invs de um ponteiro para o objeto. Conseqentemente, a varivel no tem que ser desreferenciada para manipular o objeto, o que naturalmente melhora a performance. Para declarar uma classe de tipo de valor, a palavra-chave __value deve ser usada ao invs de __gc. Neste caso, as variveis sero criadas na stack. Ao invs de declarar ponteiros para esta classe e, ento, criar os objetos na heap CLR usando o operador new, os objetos so declarados do mesmo modo que os tipos C++ embutidos e as variveismembros acessadas pelo operador ponto (.) e no pelo operador de desreferncia (->). Agora que a classe Animal foi construda, podemos us-la no programa exatamente como usamos tipos embutidos. PASSO 1: Na funo _tmain, delete a linha
Console::WriteLine(S"Hello World");

PASSO 2: Declare e crie dois objetos Animal na sua funo _tmain, como indicado abaixo:
int _tmain()//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { Animal *cat, *dog; cat = new Animal; dog = new Animal; return 0; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

31

A palavra-chave new, seguida pela classe do objeto que est sendo criado, cria o objeto na heap CLR ao invs de criar na stack. O endereo de memria do objeto criado retornado e armazenado no ponteiro. PASSO 3: Use a funo-membro SetName para designar os nomes Gato e Cachorro para os respectivos objetos cat e dog, e fixe a varivel legs em ambos os objetos para 4.
int _tmain()//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { Animal *cat, *dog; cat = new Animal; dog = new Animal; cat->SetName("Gato"); cat->legs = 4; dog->SetName("Cachorro"); dog->legs = 4; return 0; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Para acessar as variveis-membros e as funes-membros, voc tem que desreferenciar o ponteiro usando uma das duas maneiras: Voc pode usar o operador de desreferencia, um asterisco (*) seguido pela notao de ponto por exemplo, (*cat).legs; ou usar o operador de taquigrafia para acesso indireto, que um sinal de menos seguido do smbolo maior (->). PASSO 4: Tendo criado um par de objetos Animal e nomeando dados para eles, agora voc precisa exibir estes dados na tela. Adicione as linhas abaixo:
int _tmain()//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { Animal *cat, *dog; cat->SetName("Gato"); cat->legs = 4; dog->SetName("Cachorro"); dog->legs = 4; dog->SetName("Dog"); dog->legs = 4; Console::WriteLine("Animal 1"); Console::WriteLine("Nome: "); Console::WriteLine(cat->GetName()); Console::Write("N de Pernas: "); Console::WriteLine(cat->legs); Console::WriteLine(); Console::WriteLine("Animal 2"); Console::Write("Nome: "); Console::WriteLine(dog->GetName()); Console::Write("N de Pernas: "); Console::WriteLine(dog->legs); Console::WriteLine(); return 0; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 5: Construa o aplicativo. Selecione o menu Build Build Solution ou use Ctrl + Shift + B. 32

No caso de voc ter problemas ao juntar os fragmentos nos passos do programa, eis o programa inteiro:
// This is the main project file for VC++ application project // generated using an Application Wizard. #include "stdafx.h" #using <mscorlib.dll> using namespace System; __gc class Animal//+++++++++++++++++++++++++++++++++++++++++++++++++++++++ { public: int legs; void SetName(String *name)//-----------------------------------------{ strName = strName->Copy(name); }//------------------------------------------------------------------String* GetName()//--------------------------------------------------{ return strName; }//------------------------------------------------------------------private: String *strName; };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ int _tmain()//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { Animal *cat, *dog; cat = new Animal; dog = new Animal; cat->SetName("Gato"); cat->legs = 4; dog->SetName("Cachorro"); dog->legs = 4; Console::WriteLine("Animal 1"); Console::Write("Nome: "); Console::WriteLine(cat->GetName()); Console::Write("N de Pernas: "); Console::WriteLine(cat->legs); Console::WriteLine(); Console::WriteLine("Animal 2"); Console::Write("Nome: "); Console::WriteLine(dog->GetName()); Console::Write("N de Pernas: "); Console::WriteLine(dog->legs); Console::WriteLine(); return 0; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 6: Se a construo foi bem sucedida, execute o aplicativo selecionando o menu Debug Start Without Debugging ou use Ctrl + F5. A Figura 2.2 mostra o resultado. 33

Figura 2.2: Console do programa Animals.

2.6.

RPIDAS REFERNCIAS SOBRE O CAPTULO 2

A Tabela 2.1 mostra um resumo do que estudamos neste captulo. Tabela 2.1: Resumo do captulo 2.
PARA Criar uma classe Controlar a visibilidade de variveis e mtodos Declarar uma classe de tipo de referncia Declarar uma classe de tipo de valor Instancializar um objeto de classe do tipo de referncia FAA ISTO Use a palavra-chave class Use as palavras-chaves para controle private ou protected, seguidas de : de acesso public,

Coloque a palavra-chave __gc antes do especificador da classe

Coloque a palavra-chave __value antes do especificador da classe Declare um ponteiro do tipo de classe para o objeto. Use a palavra-chave new seguida pelo nome da classe para criar o objeto na heap CLR e nomeie o ponteiro retornado para ponteiro do objeto declarado acima. Por exemplo: HeapClass *pObject1; pObject1 = new HeapClass; Instancializar um Defina o objeto class + nome da varivel. Por exemplo: objeto de classe do ValueClass object1; tipo de valor

34

3.

CAPTULO 3: VARIVEIS E OPERADORES

Neste captulo, voc aprender como: Declarar (criar) variveis; Usar tipos de dados embutidos C++; Criar tipos definidos pelo usurio; Adicionar variveis-membros a uma classe; Usar a classe String do Microsoft .NET Framework; Atribuir valores a uma varivel; Criar expresses usando os operadores C++; Moldar (mudar) o tipo de uma varivel.

No captulo anterior, falamos um pouco sobre as vantagens da programao orientada para objetos e desenvolvemos um programa simples para ilustrar a criao e o uso de classes. Neste captulo, veremos com mais detalhes como criar e usar variveis, os tipos de dados fundamentais em C++, como acessar e usar classes do .NET Framework e como criar expresses usando os operadores C++.

3.1.

O QUE UMA VARIVEL?

Variveis so locais na memria onde os dados podem ser temporariamente armazenados para uso pelo programa. Elas tm um nome, um tipo e um valor. O valor da varivel pode ser mudado durante a execuo do programa. Da o nome varivel. Uma varivel deve ser declarada antes de ser usada: seu tipo tem que ser especificado e a ela deve ser atribudo um nome. O tipo de uma varivel define o intervalo vlido dos valores que o tipo pode suportar e as operaes que podem ser realizadas na varivel.

3.2.

OS TIPOS FUNDAMENTAIS DE DADOS

A Tabela 3.1 mostra o conjunto dos tipos de dados embutidos no C++. Destes tipos embutidos, voc pode construir outros tipos, como veremos neste captulo: Tipos ponteiros, por exemplo, int*; Tipos matrizes, por exemplo, int[]; Tipos Referncias, por exemplo, double&.

Voc tambm pode construir tipos definidos pelo usurio criando estruturas de dados e classes. As classes so abordadas no captulo 5.

35

Tabela 3.1: Tipos de dados embutidos em C++.


TIPO bool char short int DESCRIO COMENTRIOS Um tipo booleano que pode conter os valores true e false. Um tipo caractere que pode Os valores variam de 128 a 127. suportar valores ASCII. Inteiro; armazena o nmero Os valores variam de -32768 a 32767. completo. Inteiro; armazena o nmero Os valores variam de -2147483648 a completo. 2147483647. Um unsigned int varia de 0 a 4294967295 (232 1). Inteiro com int, exceto que em No Microsoft Visual C++, o long tem alguns compiladores, ele tem o mesmo tamanho do int. E, portanto, ele s pode armazenar o mesmo tamanho duplicado. intervalo de valores. Um tipo de inteiro especfico da Microsoft, equivalente ao tipo de dados char Um tipo de inteiro especfico da Microsoft, equivalente ao tipo de dados short Um tipo de inteiro especfico da Microsoft, equivalente ao tipo de dados int Um tipo de inteiro especfico da Valores no intervalo de 264/2 a Microsoft que armazena o nmero 264/2 1. completo Armazena nmeros de ponto- No Visual C++, o float armazena at flutuante, por exemplo, 3.7 7 casas decimais. O intervalo de valores 3.4E+/-38. Armazena nmeros de ponto- O double armazena at 15 casas flutuante como float mas com decimais. O intervalo de valores 1.7E+/-308. maior preciso Um tipo caractere largo ou tipo caractere multibyte

long

__int8

__int16

__int32

__int64

float

double

wchar_t

3.3.

DECLARANDO UMA VARIVEL

Como mencionamos antes, as variveis devem ser declaradas antes de serem usadas. Uma declarao simples consiste de
Um tipo + um ou mais nomes de variveis separados por vrgulas + ponto-e-vrgula no final.

Por exemplo:
int double nPrimeNumber; x, y, z;

Cada varivel pode receber um qualificador antes do tipo (por exemplo, unsigned). Voc tambm pode colocar um inicializador aps o nome da varivel para fornec-lo um valor inicial (por exemplo, int i = 0;). O qualificador e o inicializador so opcionais e no precisam aparecer na declarao, mas o tipo base e o nome da varivel devem estar presentes. A declarao termina em ponto-e-vrgula.
[qualificador] tipo nome [inicializador];

Por exemplo:
unsigned int i; //Uma varivel inteiro sem sinal i. Note o //qualificador limitando a varivel a nmeros //positivos.

36

long double

lSalario = 0; y;

//Uma varivel long inicializada com zero. //Uma varivel double sem qualificador ou //inicializador.

A declarao da varivel permite que o compilador: Aloque memria suficiente para armazenar a varivel de um dado tipo e para associar o nome da varivel com esta localizao de memria. Reserve o nome da varivel para preveni-lo se houver uso do mesmo nome para outras variveis dentro do mesmo escopo. NOTA: Escopo se refere parte do cdigo onde a varivel visvel i.e., onde pode ser usada. O conceito de escopo explicado com mais detalhe no captulo 4. Garante que a varivel usada de uma maneira consistente com seu tipo. Por exemplo, se voc tiver declarado uma varivel como char, no poder armazenar nela o valor 3.7.

3.3.1.

NOMEANDO VARIVEIS

Um nome de varivel em C++ pode ser qualquer combinao de letras, nmeros e sublinhados, contanto que o primeiro caractere da varivel seja uma letra ou um sublinhado. Embora o C++ no tenha quaisquer restries nossa escolha dos nomes das variveis, voc deve usar nomes significativos e ser consistente nas suas convenes de nomear variveis para melhorar a legibilidade do seu cdigo. O C++ sensvel notao maiscula/minscula, i.e., letras maisculas so consideradas diferentes das suas similares minsculas. Por exemplo, myvariable e myVariable so variveis diferentes. Porm, no boa idia diferenciar variveis apenas com base na sua grafia; isto pode confundi-lo em leitura posterior do cdigo. No raro digitarmos, por acidente, uma letra minscula ao invs de uma maiscula ou vice-versa. NOTA: Como mencionamos no captulo 1, no uma boa idia criar identificadores que comeam com dois sublinhados ou um sublinhado seguido por uma letra maiscula (por exemplo, _A). Isto porque a Microsoft usa esta conveno de nomes para especificar macros e palavras-chaves especficas dela. Assim, comear suas variveis com estas combinaes pode levar a conflitos de nomes.

3.4.

DECLARANDO MLTIPLAS VARIVEIS

Voc pode declarar diversas variveis do mesmo tipo na mesma linha simplesmente separando-as com vrgula:
int x = 10, y, z = 11;

Esta declarao cria trs inteiros chamados x, y e z. O primeiro inteiro inicializado com 10 e o terceiro com 11, enquanto o segundo no inicializado.

3.5.

NOMEANDO VALORES PARA AS VARIVEIS

Voc nomeia um valor para uma varivel usando o operador de atribuies = (um sinal igual). O valor no lado direito do operador armazenado na varivel do lado esquerdo. Quando atribuir um valor para uma varivel, o valor deve pertencer ao mesmo tipo da varivel, ser um tipo para o qual

37

C++ possa realizar uma converso de atribuio (tal como entre float e tipos inteiros), ou ser convertido explicitamente para o tipo correto. CONVERSES DE ATRIBUIO As converses de atribuio ocorrem quando variveis de lados opostos de um sinal igual so de tipos diferentes e o compilador pode converter entre os dois tipos sem qualquer possibilidade de perda de dados. Por exemplo, atribuir um inteiro para um double resultar em uma converso de atribuio onde tudo que o compilador tem que fazer adicionar .0 ao inteiro. Ocasionalmente voc pode precisar dizer ao compilador que realize uma converso que de outro modo ele no faria. Por exemplo, a diviso de dois inteiros resulta em um inteiro. Se voc quiser um resultado ponto-flutuante, pode forar o compilador a converter um dos valores para double, como no exemplo:
double result = double(640) / 480;

Voc d o nome do tipo para o qual o resultado deve ser convertido, seguido pelo valor entre parnteses. Este processo chamado de molde (casting), e ele pode ser bastante perigoso porque voc est dizendo ao compilador para aplicar uma converso e, portanto, deve estar seguro de que est agindo corretamente. Por exemplo:
int float double x = 1; z = x; y = 3.56; x = y; x; y; z;

//Esta converso de float para int resulta em perda de dados. //O inteiro 3 armazenado na varivel x.

O compilador ir gerar a advertncia C4244: = conversion from float to int possible loss of data. A razo para isto que a atribuio de 3.56 para inteiro provocar a perda da parte decimal, i.e., truncar o resultado em 3.

3.6.

MATRIZES

Uma matriz uma coleo de locais para armazenamento de dados, todos do mesmo tipo por exemplo, int ou double. As matrizes so teis quando voc quer representar uma coleo de valores, como o nmero de dias em cada ms ou os nomes dos empregados de uma companhia. Cada local de armazenamento chamado de um elemento da matriz. Os elementos da matriz so acessados referenciando a partir de um offset o nome da matriz. Os elementos da matriz comeam de zero at o tamanho da matriz menos 1:
int nArray[10]; int x; nArray[0] = 23; nArray[9] = 21; x = nArray[0]; //Declarao de uma matriz com 10 inteiros

//O 1 elemento na matriz comea no offset 0 //O ltimo elemento na matriz comea no offset 9

Escrever dados alm do fim da matriz constitui um srio problema e a causa de muitos bugs em programas C++. Quando acessa um elemento da matriz, o compilador calcula sua posio em relao ao incio da matriz. Se voc estiver escrevendo para um elemento de matriz e der um ndice invlido 38

de modo que o offset calculado esteja aps o fim da matriz, o compilador sobrescrever tudo o que estiver naquele local de memria.

3.7.

PONTEIROS

Um ponteiro uma varivel que retm o endereo da memria de outra varivel ou funo, o que significa que voc pode usar um ponteiro para se referir indiretamente a uma varivel. NOTA: Por que os ponteiros so teis? A mais pragmtica razo para usar ponteiros consiste em sabermos que eles fazem parte da famlia C de linguagens diretas desde o incio e so extensivamente usados. Assim, voc precisar conhecer alguma coisa sobre eles. Entretanto, h muitas outras razes. Resumiremos a seguir duas delas. A primeira: ponteiros so um dos modos principais de passar argumentos para as funes. Os argumentos so usualmente passados por valor como uma cpia. Assim voc no pode modificar o valor e obt-lo de volta para o cdigo aps a funo. Os ponteiros permitem que voc passe argumentos para uma funo de tal modo que pode modific-los dentro desta. A segunda razo: Algumas operaes em sries de dados como, por exemplo, em matrizes podem ser realizadas muito eficientemente usando ponteiros. Embora uma varivel-ponteiro contenha um endereo e, portanto, pode armazenar um endereo de memria de qualquer tipo de dados, as variveis-ponteiros so declaradas para ser tipo de dados especficos. Um ponteiro para um tipo de dados inteiros (int) no armazena o endereo de um double. A varivel-ponteiro declarada do mesmo modo que as variveis de tipo de dados, mas o operador de ponteiro * (um asterisco) anexado ao tipo de dados:
int* double* char* pi;//Ponteiro para um int pd;//Ponteiro para um double pc;//Ponteiro para char

Toda varivel tem um endereo e este endereo pode ser obtido usando o operador de endereo &. O endereo da varivel pode ser armazenado em uma varivel-ponteiro e, usando este ponteiro, os contedos da varivel podem ser manipulados atravs do operador de desreferncia * (tambm um asterisco):
int x = 10, y = 0; int* pX = NULL; pX = &x; y = *pX; *pX = 20; //Declara um ponteiro. //Armazena o endereo da varivel inteira x. //Atribui o valor de x para y. //Atribui o valor 20 para x.

Nas duas ltimas linhas do cdigo acima, *pX pode ser lido como o valor que pX aponta. NOTA: Pode parecer confuso que o operador asterisco seja usado tanto como o operador de ponteiro quanto como operador de desreferncia. Porm, voc s encontrar o operador de ponteiro nas declaraes de variveis, enquanto o operador de desreferncia usado no cdigo. Assim, torna-se bvio saber qual operador um dado asterisco representa.

3.8.

REFERNCIAS

Uma varivel-referncia um apelido para outra varivel. Todas as variveis-referncias devem ser inicializadas quando declaradas. Da em diante, qualquer mudana feita na varivel-referncia tambm afetar a varivel apelidada. As variveis-referncias so particularmente importantes quando 39

passamos variveis para funes, tpico abordado com mais detalhes posteriormente. Por enquanto, eis um exemplo:
int x = 10; int& y = x; y = 4; //Declara y como referncia para x. //O valor de x tambm muda para 4.

3.9.

CONSTANTES

Como as variveis, as constantes so locais nomeados para armazenamento de dados. Porm, ao contrrio de uma varivel, o valor de uma constante no pode ser mudado aps sua declarao. Uma constante tem que ser inicializada quando criada e no lhe pode ser atribudo outro valor posteriormente. O C++ tem dois tipos de constantes: literal e simblica.

3.9.1.

CONSTANTE LITERAL

Uma constante literal simplesmente um valor digitado no programa. As declaraes no cdigo abaixo atribuem as constantes literais 40 e Dog para as respectivas variveis NoOfEmployees e strName:
NoOfEmployees = 40; strName = Dog;

3.9.2.

CONSTANTE SIMBLICA

uma constante representada por um nome. Ela definida exatamente do mesmo modo que uma varivel, mas o qualificador deve comear com a palavra-chave const e a varivel deve ser inicializada. Aps a declarao, o nome da constante pode ser usado em qualquer lugar permissvel para uma varivel do seu tipo, como mostrado abaixo:
const unsigned long NoOfFullTimeEmployees = 49; const unsigned long NoOfPartTimeEmployees = 234; unsigned int NoOfEmployees; NoOfEmployees = NoOfFullTimeEmployees + NoOfPartTimeEmployees;

H uma dupla vantagem em usar constantes simblicas ao invs de constantes literais: Os nomes simblicos tornam o programa mais legvel. A constante NoOfFullTimeEmployees mais significativa do que a constante literal 49. simblica

mais fcil mudar uma nica declarao de constante simblica do que encontrar e substituir todas as ocorrncias de uma constante literal em um programa.

Porm, o uso de constantes simblicas no lugar de constantes literais pode ser muito trabalhoso. No h necessidade de substituir todas as constantes literais por simblicas. H algumas constantes que so intuitivamente bvias para qualquer pessoa e que no mudaro. Por exemplo, o nmero de dias em uma semana ou os meses do ano. Estes valores podem ser deixados literalmente sem reduo da legibilidade ou manuteno do cdigo.

3.10.

ENUMERAES

Em situaes onde uma varivel s pode assumir um conjunto especfico de valores, tais como cores ou gneros, as enumeraes permitem que voc crie novos tipos e declare variveis cujos valores so restritos ao conjunto enumerado. 40

As enumeraes so declaradas da seguinte maneira:


enum + nome do tipo + { + os valores enumerados separados por vrgulas + } + um ponto-e-vrgula

Ento, as variveis so declaradas como um tipo enumerado e elas s podem receber um dos valores enumerados. Por exemplo:
enum WorkDays {Monday, Tuesday, Wednesday, Thursday, Friday}; const int Saturday = 7; WorkDays WorkingDay; WorkingDay = Thursday; //Thursday um membro do tipo enumerado //WorkDays e, portanto, pode ser nomeado //para a varivel WorkingDay. WorkingDay = Saturday; //Embora Saturday seja uma constante inteira, //ela no membro do tipo enumerado WorkingDay //e, portanto, no pode ser nomeada para a //varivel WorkingDay.

3.11.

TIPOS DEFINIDOS PELO USURIO

typedef sinnimo de tipo definido pelo usurio para um tipo existente. Para criar um sinnimo para um dado tipo:
typedef + nome do tipo + o novo nome que voc quer usar + ponto-e-vrgula

Por exemplo:
typedef unsigned int positiveNumber;

Este typedef declara positiveNumber como sinnimo de unsigned int e, a partir de ento, voc pode fazer declaraes como:
positiveNumber one, two;

3.12.

ADICIONANDO VARIVEIS-MEMBROS S CLASSES

PASSO 1: Entre no Microsoft Visual Studio .NET e selecione File New Project. Aparece a caixa de dilogo New Project. PASSO 2: Em Project Type, escolha Visual C++ Projects e em Templates escolha Console Application (.NET). Na caixa de texto Name, digite Variables e escolha uma localizao para o seu novo projeto no disco rgido. Clique OK. PASSO 3: Clique no menu Project Add Class. Aparece na tela a caixa de dilogo Add Class (Figura 3.1). PASSO 4: Em Templates, escolha Generic C++ Class. Clique Open. Surge, ento, a caixa de dilogo Generic C++ Class Wizard (Figura 3.2). PASSO 5: Na caixa de texto Class name, digite Animal. Note como os nomes padres para os arquivos de cabealho e fonte so criados automaticamente. PASSO 6: Deixe a caixa de texto Base class em branco. PASSO 7: Na lista Access, selecione public (note que public o modificador de acesso padro. Assim, ele j est selecionado). PASSO 8: Marque a caixa de checagem Virtual destructor. 41

Figura 3.1: Caixa de dilogo Add Class.

Figura 3.2: Caixa de dilogo Generic C++ Class Wizard. PASSO 9: Clique Finish. Note que o arquivo de cabealho para a nova classe, Animal.h, foi carregado na janela principal. No Solution Explorer, o arquivo Animal.cpp foi adicionado pasta Source Files e o arquivo de cabealho, Animal.h, foi adicionado pasta Header Files (Figura 3.3). Na janela Class View, se voc expandir o n Variables, ver que a nova classe Animal foi adicionada ao projeto. Se expandir a classe Animal, voc ver o construtor, Animal(void), e o destrutor, ~Animal(void), padres para a classe (Figura 3.4).

42

Figura 3.3: Janela principal do Visual Studio .NET, mostrando o contedo do arquivo de cabealho Animal.h e do Solution Explorer.

Figura 3.4: Janela principal do Visual Studio .NET, mostrando o contedo do Animal.h e da janela Class View.

PASSO 10: Na janela Class View, expanda o n Variables, clique com o boto direito do mouse na classe Animal, selecione Add Add Variable (Figura 3.5). Surge a caixa de dilogo Add Member Variable Wizard (Figura 3.6). Aceite o modificador de acesso padro, public.

Figura 3.5: Adicionando nova varivel classe Animal. PASSO 11: No menu drop-down Variable type, selecione int. 43

PASSO 12: Na caixa de texto Variable name, digite legs. PASSO 13: Clique Finish.

Figura 3.6: Caixa de dilogo Add Member Variable Wizard. A Figura 3.7 mostra o arquivo-fonte Animal.cpp e a janela Class View expandida.

Figura 3. 7: Janela principal do Visual Studio .NET, mostrando o contedo do Animal.c e da janela Class View.

Neste exerccio, voc viu como criar uma nova classe usando o Visual Studio e como adicionar membros a esta classe.

3.13.

A CLASSE String DO .NET FRAMEWORK

A classe String no um tipo embutido de dados como int ou long, mas faz parte do .NET Framework. Por no ser um tipo embutido, voc tem que incluir alguns arquivos no seu projeto antes 44

de compilar e utilizar a classe String. Qualquer cdigo que utilize a classe String ter que incluir as duas linhas abaixo no topo do cdigo do arquivo-fonte:
#using <mscorlib.dll> using namespace System;

A linha #using diz ao compilador para procurar no arquivo mscorlib.dll os detalhes da classe String. mscorlib.dll o arquivo de biblioteca que contm a essncia dos componentes .NET e voc o encontrar com bastante freqncia ao longo deste livro. A segunda linha facilita o uso de algumas classes .NET. Por enquanto, apenas a digite no incio dos arquivos-fontes que utilizem a classe String. A classe String contm um grande nmero de mtodos que simplificam a manipulao de strings, como, por exemplo, Insert e Replace. NOTA: Embora a classe String seja muito poderosa, uma vez que voc inicialize um objeto String, ele se torna imutvel, i.e., voc no pode alter-lo aps a criao. As funes-membros da classe String que parecem alterar strings, como, por exemplo, Insert e Replace, na verdade retornam um novo objeto String, que contm a string modificada. Este comportamento torna muito ineficientes os mtodos da classe String quando precisamos realizar repetidas modificaes em uma string. Se voc precisar fazer tais mudanas repetidamente em uma string, deve usar a classe StringBuilder. Neste caso, voc ter que incluir o conjunto (assembly) mscorlib.dll e o namespace System::Text para simplificar o acesso aos membros.

3.14.

OPERADORES E EXPRESSES

Expresses so construes que usam operadores que lidam com dados os operandos para obter um resultado. Veja o exemplo abaixo:
Remuneration = Salary + Bonus;

Acima, o operador de adio + (um sinal mais) usado para adicionar os operandos Salary e Bonus, e o operador de atribuio = (um sinal igual) usado para armazenar o total na varivel Remuneration.

3.14.1.

OPERADORES DE ATRIBUIO

Um operador de atribuio usado para nomear um valor para uma varivel. Todas as expresses retornam um valor quando avaliadas e o valor da expresso de atribuio o novo valor do objeto no lado esquerdo. Esta funcionalidade permite a nomeao do mesmo valor para um grupo de variveis.
NoOfMammals = NoOfDogs = NoOfCats = 0;

Neste exemplo, todas as trs variveis NoOfMammals, NoOfDogs e NoOfCats so fixados para 0.

3.14.2.
3.14.2.1.

OPERADORES ARITMTICOS
OPERADORES MATEMTICOS PADRES

C++ tem 12 operadores aritmticos, 5 dos quais operam como os operadores matemticos padres: o operador de adio + (um sinal mais); 45

o de subtrao (um sinal menos); o de multiplicao * (um asterisco); o de diviso / (uma barra) e o operador de mdulo % (um sinal de porcentagem), que retorna o resto de uma diviso.

Por exemplo:
Result = 4 + 2 - 3; //Result = 3 Result = 4 * 5; //Result = 20 Reminder = 7 % 3; //Reminder = 1

3.14.2.2.

OPERADORES ARITMTICOS DE ATRIBUIO

Alm dos operadores matemticos usuais, h alguns operadores aritmticos de atribuio: o operador de atribuio de adio += (um sinal mais seguido de um sinal igual); o operador de atribuio de subtrao = (um sinal menos seguido de um sinal igual); o operador de atribuio de multiplicao *= (um asterisco seguido de um sinal igual); o operador de atribuio de diviso /= (uma barra seguida de um sinal igual) e o operador de atribuio de mdulo %= (um sinal de percentagem seguido de um sinal igual).

Estes operadores so formas taquigrficas que combinam a operao matemtica correspondente com a operao de atribuio. Assim, A = A + 5; pode ser expressa como A += 5;. Ao invs de usar o operador de adio para adicionar 5 ao valor da varivel A e, ento, usar o operador de atribuio para nomear o novo valor para a varivel A, voc s precisar usar o operador de atribuio de adio para adicionar 5 ao valor da varivel A e nomear o novo valor. O operador de atribuio de adio um operador de compactao. No h diferena entre as duas declaraes. Em ambas, uma adio realizada, seguida por uma atribuio. A segunda forma apenas uma maneira compactada de expressar uma operao usada com freqncia.

3.14.2.3.

OPERADORES DE INCREMENTO E DECREMENTO

Os operadores de incremento e decremento so similares aos operadores taquigrficos, sendo que o operador de incremento adiciona apenas 1 ao valor da varivel e o de decremento subtrai 1:
A++; A--; //Adiciona 1 ao valor da varivel A //Subtrai 1 ao valor da varivel A

H duas formas para os operadores de incremento e decremento: a forma prefixada: ++A ou A e a forma ps-fixada: A++ ou A--;

Ambas as formas adicionam ou subtraem 1 ao valor da varivel. Na forma prefixada, a operao matemtica realizada antes que a varivel seja usada na expresso; na ps-fixada, a varivel incrementada ou decrementada aps ter sido usada na expresso.
int a = b = c = a, b, c; b = c = 0; ++a; //a = 1, b = 1 a++; //c = 1, a = 2

46

Neste fragmento de cdigo, os valores finais das variveis so a = 2, b = 1 e c = 1. A expresso com o operador de incremento prefixado adiciona 1 ao valor de a antes de atribuir o valor da varivel a varivel b. A expresso com o operador de incremento ps-fixado atribui o valor da varivel a varivel c e, ento, incrementa o valor da varivel a de 1. NOTA: Quando lidam com objetos, as verses prefixadas dos operadores de incremento e decremento so mais eficientes do que as ps-fixadas porque no precisa ser feita uma cpia temporria do objeto para uso na expresso antes que o objeto seja incrementado ou decrementado.

3.14.3.
3.14.3.1.

OPERADORES RELACIONAIS E LGICOS


OPERADORES RELACIONAIS

Os operadores relacionais so usados para comparar dois valores ou expresses, retornando um valor true ou false. O C++ tem 6 operadores relacionais:
a a a a a a

o operador maior que >; o maior ou igual >=; o menor que <; o menor ou igual <=; o operador de igualdade == (dois sinais de igualdade) e o operador de desigualdade != (um ponto de exclamao e um sinal de igualdade).
> b >= b < b <= b == b != b //retorna //retorna //retorna //retorna //retorna //retorna true true true true true true se se se se se se a a a a a a for for for for for for maior do que b maior ou igual a b menor do que b menor ou igual a b igual a b diferente b

3.14.3.2.

OPERADORES LGICOS

Um operador lgico usado para comparar duas expresses relacionais. O C++ tem 3 operadores lgicos: o operador AND &&; o OR || (duas barras verticais) e o NOT ! (um ponto de exclamao).

O operador AND relaciona duas expresses e retorna true se ambas forem true. O operador OR retorna true se pelo menos uma das expresses for avaliada como true.
a && (a > a || (a > b b) && (a < c) b b) || (a < c) //Retorna //Retorna //Retorna //Retorna true true true true se se se se a a a a e b forem true for maior do que b e menor do que c ou b forem true for maior do que b ou menor do que c

A avaliao de uma expresso relacional pra assim que o valor lgico da expresso inteira determinado. Por exemplo, a expresso
expr1 && expr2

47

true somente se expr1 e expr2 forem true. Se expr1 for false, o valor final da expresso deve ser false e, portanto, expr2 no avaliada. O operador NOT retorna a negao do valor booleano do seu operando:
!a //Se a true, o operador NOT retornar false; e vice-versa.

Estes operadores so freqentemente usados em decises ou em estruturas de lao, que abordaremos no captulo 5.

3.14.4.

OPERADORES BIT-A-BIT

O C++ tem 6 operadores bit-a-bit: o operador AND &; o operador OR |; o operador OR exclusivo ^; o operador complementar ~; o operador de deslocamento para a direita >>; o operador de deslocamento para a esquerda <<.

Estes operadores funcionam nos bits individuais do byte e so aplicados apenas a operandos inteiros os tipos char, short, int e long. O operador bit-a-bit AND compara os bits de dois operandos: se o bit na mesma posio de cada operando for 1, o bit resultante 1; se, porm, houver um 0, o bit resultante fixado para 0. Este operador freqentemente usado para mscaras de bits. O operador bit-a-bit OR compara os bits de dois operandos: se um dos bits for 1, o bit resultante 1 e se ambos os bits for 0, o bit resultante 0. O operador bit-a-bit OR freqentemente usado para ligar bits, sinalizadores e outras operaes similares. O operador OR exclusivo fixa o bit resultante para 1 somente se um dos operandos tiver o correspondente bit igual a 1. Se o bit correspondente de ambos os operandos for 1 ou 0, o bit resultante 0. O operador complementar inverte o estado do bit do operando. Se o bit for 1, ~ fixa-o para 0; se for 0, ~ fixa-o para 1. O operador de deslocamento para a esquerda move o padro de bits do seu operando esquerdo para a esquerda pelo nmero de bits especificado por seu operando direito. Os bits desocupados por causa do deslocamento para a esquerda so preenchidos com zeros. O operador de deslocamento para a direita move o padro de bits do seu operando esquerdo para a direita pelo nmero de bits especificado por seu operando direito. Os bits desocupados so preenchidos com o bit do sinal.
int a; a = 5; a = a << 2; //Os bits //e a = 5; a = a >> 2; //Os bits //e

de a so deslocados 2 bits para a esquerda o valor resultante, 20, atribudo a a. de a so deslocados 2 bits para a direita o valor resultante, 1, atribudo a a.

A Figura 3.8 mostra as duas operaes bit-a-bit do exemplo acima. 48

0 0

0 0

0 0

0 1 (a)

0 0

1 1

0 0

1 0

0 0

0 0

0 0

0 0 (b)

0 0

1 0

0 0

1 1

Figura 3.8: (a) Operador de deslocamento para a esquerda: na primeira linha, a = 5 em notao binria (22 + 20); na segunda, o resultado da operao bit-a-bit a = a << 2, i.e., a = 20 (24 + 22). (b) Operador de deslocamento para a direita: na primeira linha, a = 5; na segunda, o resultado da operao bit-a-bit a = a >> 2, i.e., a = 1 (20).

3.14.5.

O OPERADOR TERNRIO

O operador ternrio ?: (composto de uma interrogao e dois pontos) uma declarao if inline (ver o captulo 5 para mais informaes sobre declaraes if). A expresso para a esquerda do operador ternrio avaliada: se ela for true, o valor ou a expresso entre a interrogao e dos dois pontos retornada; se for false, o valor ou a expresso aps os dois pontos retornada.
int a; bool b; b = true; a = b ? 1 : 2; b = false; a = b ? 1 : 2;

//b true. Ento a recebe 1. //b false. Ento a recebe 2.

3.14.6.

O OPERADOR sizeof

O operador sizeof retorna o tamanho em bytes do tipo de dados C++, seja tipo embutido ou definido pelo usurio. Por exemplo, sizeof(int) e sizeof(Animal).

3.14.7.

CONVERSO DE TIPOS

O C++ suporta o operador converso (cast) estilo C, onde o tipo para o qual voc quer converter a expresso colocado entre parnteses em frente expresso. Por exemplo, (float)7. Ele tambm suporta 4 novos operadores: static_cast<type>: muda o tipo de dados da varivel. Por exemplo, se em uma expresso precisamos converter um inteiro em um nmero ponto-flutuante para um clculo, o nmero deve ser convertido usando o operador static_cast<double>. const_cast<type>: muda o carter const da varivel. dynamic_cast<type>: usado para converter objetos para baixo ou para cima do nvel de hierarquia herdada. reinterpret_cast<type>: permite que qualquer ponteiro possa ser convertido para um ponteiro de outro tipo.

int a = 10; double b; b = (double)a; b = static_cast<double>(a);

//Velho operador de converso estilo C //Operador static_cast C++

49

3.14.8.

PRECEDNCIA E ASSOCIATIVIDADE DE OPERADORES

H dois modos para avaliar a expresso 2 + 3 * 4: (2 + 3) * 4 = 20 e 2 + (3 * 4) = 14. As regras de precedncia de operadores permitem avaliao sem ambigidade das expresses. Operadores mais altos na hierarquia tm precedncia sobre operadores mais baixos. Como o operador * tem hierarquia mais alta do que o operador +, a segunda interpretao da expresso acima, 2 + (3 * 4) = 14, seria avaliada. Em situaes onde os dois operadores tm o mesmo nvel na hierarquia, a onda de avaliao procede da esquerda para a direita. Assim, 2 * 3 / 2 * 3 = ((2 * 3) / 2) * 3 = (6 / 2) * 3 = 3 * 3 = 9. Podemos usar parnteses para agrupar operadores e anular a hierarquia de precedncia. Por exemplo, (2 * 3) / (2 * 3) = 1. Idealmente, voc deveria usar parnteses mesmo quando eles no so estritamente requeridos simplesmente para deixar claras as suas intenes. A Tabela 3.2 mostra a hierarquia de precedncia da maior para a menor. Os operadores na mesma linha da tabela compartilham o mesmo nvel de precedncia. Tabela 3.2: Hierarquia de precedncia dos operadores C++ (da maior para a menor).
OPERADOR :: . -> [] () ++ -static_cast<type> const_cast<type> dynamic_cast<type> reinterpret_cast<type> sizeof() ++ -^ ! + & * new delete delete[] .* ->* * / % + >> << < <= > >= == != NOME Resoluo de escopo Seleo de membro, subscrio, chamadas de funo, incremento ps-fixado, decremento ps-fixado, operadores de converso

sizeof(), incremento prefixado, decremento prefixado, complementar, negao, menos unrio, mais unrio, endereo de, desreferncia, new, delete, delete[] Ponteiro para operador de membro Multiplicao, Diviso, Mdulo Adio, Subtrao Deslocamento para a direita, deslocamento para a esquerda Operadores relacionais (desigualdades)

Igualdade, Desigualdade

50

& ^ | && || ?: = += -= *= /= %= = <<= >>= &= |= ^= ,

AND bit-a-bit OR exclusivo OR bit-a-bit AND lgico OR lgico Operador ternrio Operadores de atribuio

Vrgula

3.15.

RPIDAS REFERNCIAS SOBRE O CAPTULO 3

A Tabela 1.3 mostra um resumo deste captulo. Tabela 3. 3: Resumo do captulo 3.


PARA Declarar uma varivel FAA ISTO Tipo + espaos + nome da varivel + pontoe-vrgula. Ex.: int number1; long longNumber1; Use o operador de atribuio = Use uma matriz. Transforme a varivel em constante. Ex.: const int x = 10; Declare uma constante enumerada e declare a varivel para ser deste tipo. Use o Add Member Variable Class Wizard. Use a classe .NET String. Use o operador sizeof(). Use o operador static_cast<type> Use parnteses para agrupar operadores.

Atribui valores para uma varivel Agrupar dados homogneos Proteger dados de mudanas Restringir os valores que uma varivel pode aceitar a um pequeno conjunto Adicionar variveis-membros a uma classe Acessar uma classe String Saber o tamanho de um objeto ou de um tipo em bytes Converter um tipo de dados para outro Anular precedncia padro de operador, ou tornar o cdigo mais legvel

51

4.

CAPTULO 4: USANDO FUNES

Neste captulo, voc aprender como: Declarar prottipos de funo; Definir corpos de funo; Chamar funes; Lidar com escopo de varivel local de global; Definir e usar funes sobrecarregadas.

Supomos que at agora voc deve estar bastante confortvel com a sintaxe bsica de C++. Voc viu como declarar variveis, escrever declaraes, usar operadores e realizar sada simples no console. Porm, como seus programas vo comear a crescer, voc precisa organizar seu cdigo para enfrentar a complexidade crescente. Neste captulo, voc aprender como dividir um programa Microsoft Visual C++ em funes. Primeiramente, voc ver como declarar prottipos de funo para introduzir as funes no compilador. A seguir, ver como definir os corpos de funo para realizar o processamento requerido. Por exemplo, voc pode escrever uma funo para calcular o crescimento esperado em um investimento ou para obter a senha de usurio de uma tela de Logon. Finalmente, voc ver como chamar uma funo em qualquer lugar do seu programa. POR QUE USAR FUNES? H muitas boas razes para dividir um programa em funes. Eis algumas: Cada funo usualmente bastante curta e discreta. mais fcil escrever um programa como uma srie de funes do que um nico cdigo, porque voc pode se concentrar em uma funo de cada vez. Tambm mais fcil ler e depurar um programa que contenha algumas pequenas funes do que um que contenha um nico corpo, porque voc no precisa relembrar o que o programa inteiro faz. As funes so reutilizveis. Uma vez escrita uma funo, voc pode cham-la sempre que precisar dela no seu programa, o que reduz o esforo de codificao e, conseqentemente, melhora a produtividade do desenvolvedor.

4.1.

DECLARANDO PROTTIPOS DE FUNO

Um prottipo de funo uma declarao de uma linha que introduz o nome da funo no compilador. O prottipo tambm indica quais tipos de parmetros podem ser passados na funo e qual o tipo de valor que ela retorna.

4.1.1.

DECLARANDO UM PROTTIPO SIMPLES DE FUNO

Eis um prottipo simples de funo:


void DisplayWelcome();

52

Neste exemplo, o nome da funo DisplayWelcome. Os parnteses so requeridos para indicar que isto uma funo. Os parnteses esto vazios neste exemplo, o que significa que a funo no recebe parmetros. A palavra-chave void no incio do prottipo de funo indica que a funo no retorna valor presumivelmente, ela apenas exibe uma mensagem de boas-vindas na tela. NOTA: Algumas linguagens de programao fazem diferena entre funes (que retornam um valor) e sub-rotinas (que no retornam um valor). Por exemplo, O Microsoft Visual Basic .NET usa a palavrachave Function para funes e a palavra-chave Sub para sub-rotinas. C++ possui somente funes. Use o tipo de retorno void se a funo no retornar um valor. Note o ponto-e-vrgula no final do prottipo de funo. O ponto-e-vrgula um indicador de fim de declarao. Um prottipo de funo no lhe informa o que a funo faz; apenas lhe mostra a assinatura da funo.

4.1.1.1.

EXERCCIO: DECLARANDO UM PROTTIPO DE FUNO

Neste exerccio, voc ir declarar um prottipo simples de funo em um aplicativo do Visual C++. A funo no receber parmetros e no retornar valores. Eis os passos: PASSO 1: Inicialize o Microsoft Visual Studio .NET e abra um novo projeto Visual C++ Console Application (.NET) chamado InvestmentPlanner. PASSO 2: Em Solution Explorer, d um clique duplo em InvestmentPlanner.cpp para visualizar o cdigo deste arquivo. PASSO 3: No topo do arquivo, imediatamente abaixo da declarao using System;, adicione o seguinte prottipo de funo:
void DisplayWelcome();

namespace

Este o prottipo de funo visto anteriormente. Voc deve colocar os prottipos de funo prximos ao topo do arquivo-fonte de modo que eles sejam visveis para o resto do cdigo no arquivo. PASSO 4: Selecione o menu Build Build Solution para verificar se no h erros de sintaxe. O programa ainda no mostrar nada se executado porque voc ainda no definiu nem chamou a funo DisplayWelcome. Faremos isto mais adiante, neste captulo.

4.1.2.

DECLARANDO PARMETROS EM UM PROTTIPO DE FUNO

As funes podem receber parmetros para torn-las mais genricas. Voc deve declarar os tipos de dados para estes parmetros no prottipo da funo.

4.1.2.1.

EXERCCIO: PROTTIPO DE FUNO COM PARMETROS

Neste exerccio, voc ir declarar um prottipo de funo que usa parmetros. PASSO 1: Abra o projeto criado no exerccio anterior. PASSO 2: Adicione o seguinte prottipo de funo:
void DisplayProjectedValue(double amount, int years, double rate);

Este prottipo de funo declara uma funo chamada DisplayProjectedValue. A funo recebe trs parmetros: um double, um int e outro double. O compilador usa esta informao para garantir que a funo sempre seja chamada com o nmero e os tipos corretos de parmetros. 53

DICA: Os nomes dos parmetros so opcionais no prottipo de funo. Estritamente falando, voc pode omiti-los e apenas especificar os tipos dos parmetros. Porm, os nomes dos parmetros ajudam a informar seus significados. Assim, boa prtica us-los. PASSO 3: Construa o programa e cheque a sintaxe.

4.1.3.

DECLARANDO O TIPO DE RETORNO EM UM PROTTIPO DE FUNO

Assim como voc especifica os parmetros de entrada para uma funo, tambm deve especificar um tipo de retorno para ela. Como voc viu antes, o tipo de retorno void indica que a funo no retorna valor.

4.1.3.1.

EXERCCIO: FUNO COM RETORNO NO-NULO

Neste exerccio, voc ver como especificar um tipo de retorno no-nulo para uma funo. PASSO 1: Abra o projeto do exerccio anterior. PASSO 2: Adicione o seguinte prottipo de funo:
double GetInvestmentAmount();

Este prottipo de funo declara uma funo chamada GetInvestmentAmount. A funo no recebe parmetros, mas retorna um double. PASSO 3: Adicione, tambm, a o prottipo de funo abaixo:
int GetInvestmentPeriod(int tmin, int tmax);

Este exemplo mostra como declarar uma funo que recebe parmetros e retorna um valor. A funo GetInvestmentPeriod recebe dois parmetros int e retorna um int. NOTA: Os tipos dos parmetros e o tipo do retorno so independentes um do outro. O fato do tipo dos parmetros e do retorno de GetInvestmentPeriod serem todos int pura coincidncia. muito fcil imaginar uma funo cujos tipos de parmetros e o tipo de retorno sejam diferentes. Por exemplo,
double CalculateAverageValue(int number1, int number2);

PASSO 4: Construa seu programa.

4.1.4.

DECLARANDO VALORES PADRES PARA PARMETROS DE FUNO

Quando declara um prottipo de funo, voc pode especificar valores padres para algum ou todos os parmetros. Estes valores padres so teis para parmetros que normalmente tm o mesmo valor cada vez que a funo chamada. Especificar um valor padro para um parmetro de funo significa que voc pode omitir o valor do parmetro quando chamar a funo; o compilador substituir o valor padro no seu lugar.

4.1.4.1.

EXERCCIO: PARMETROS PADRES

Neste exerccio, voc ir definir parmetros padres em um dos prottipos de funo j declarados no projeto InvestmentPlanner. PASSO 1: Abra o projeto InvestmentPlanner. 54

PASSO 2: Encontre o prottipo de funo


int GetInvestmentPeriod(int tmin, int tmax);

e modifique-o como indicado abaixo para definir os valores de parmetros padres:


int GetInvestmentPeriod(int tmin = 10, int tmax = 25);

Este prottipo de funo tem dois parmetros chamados tmin e tmax. Os parmetros so seguidos por um sinal de igualdade e, ento, um valor padro. Definimos um valor padro 10 para o parmetro tmin e um valor padro 25 para o parmetro tmax. Voc ver como chamar esta funo na seo CHAMANDO FUNES ainda neste captulo. PASSO 3: Construa seu programa.

4.2.

DEFININDO OS CORPOS DE FUNO

Na seo anterior, voc aprendeu como declarar prottipos de funo. Como voc viu, um prottipo de funo especifica o nome de uma funo, sua lista de parmetros e seu tipo retornado. Porm, os prottipos de funo no contm quaisquer declaraes executveis; eles no indicam o que a funo far quando for chamada. Para descrever o comportamento de uma funo, voc deve definir um corpo de funo. O corpo de uma funo contm declaraes executveis para realizar as operaes desejadas na funo. Nesta seo, voc definir corpos de funo para todos os prottipos introduzidos no projeto InvestmentPlanner.

4.2.1.

DEFININDO UM CORPO DE FUNO SIMPLES

O exemplo abaixo mostra um corpo de funo simples, correspondente ao prottipo da funo DisplayWelcome:
void DisplayWelcome()//+++++++++++++++++++++++++++++++++++++++++++++++++++ { Console::WriteLine(S"----------------------------------------------------"); Console::WriteLine(S"Bem-vindo ao seu Planejador de Investimentos Amigvel"); Console::WriteLine(S"----------------------------------------------------"); return; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Note que a primeira linha do corpo de funo idntica ao prottipo de funo, exceto que no termina com ponto-e-vrgula. Esta primeira linha conhecida como o cabealho da funo. Aps o cabealho da funo, um par de chaves {} delimita as declaraes executveis para o corpo da funo. Neste exemplo, a funo DisplayWelcome mostra uma simples mensagem de boas-vindas na tela. Nas duas prximas sees veremos funes mais complexas que realizam entradas no console e clculos matemticos. A palavra-chave return no final da funo faz com que o fluxo de controle retorne para a funo que chamou DisplayWelcome. Neste exemplo, a palavra-chave return suprflua porque a chave que fecha o corpo da funo atua como um retorno implcito. Porm, voc pode usar return em outros locais na funo, tais como dentro de um condicional if, para retornar prematuramente de uma funo. Voc ver mais sobre o condicional if no captulo 5.

55

4.2.1.1.

EXERCCIO: ADICIONANDO O CORPO DE UMA FUNO

Neste exerccio, voc ir adicionar o corpo da funo DisplayWelcome para seu aplicativo no Visual C++. PASSO 1: Abra o projeto InvestmentPlanner. PASSO 2: Localize o final da funo _tmain. Abaixo dela, defina o corpo da funo DisplayWelcome como abaixo:
void DisplayWelcome()//+++++++++++++++++++++++++++++++++++++++++++++++++++ { Console::WriteLine(S"----------------------------------------------------"); Console::WriteLine(S"Bem-vindo ao seu Planejador de Investimentos Amigvel"); Console::WriteLine(S"----------------------------------------------------"); return; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 3: Construa seu programa. No dever haver erros de compilao. NOTA: Voc pode definir corpos de funo em qualquer ordem no Visual C++. Por exemplo, voc pode colocar o corpo da funo DisplayWelcome antes ou aps o corpo da funo _tmain. Porm, as funes no podem ser aninhadas, i.e., voc no pode definir um corpo de funo dentro das chaves de outra funo.

4.2.2.

DEFININDO UM CORPO DE FUNO QUE UTILIZA PARMETROS

Quando define um corpo de funo que utiliza parmetros, voc deve definir exatamente o mesmo nmero de tipos de parmetros do prottipo da funo. Isto bastante razovel o objetivo principal do prottipo de funo introduzir a assinatura da mesma. DICA: O corpo de funo pode usar nomes de parmetros diferentes dos utilizados no prottipo porque os nomes dos parmetros no prottipo existem apenas para documentao. Porm, para manter a consistncia, voc deve usar os mesmos nomes de parmetros no prottipo e no corpo da funo.

4.2.2.1.

EXERCCIO: CORPO DE FUNO COM PARMETROS

Neste exerccio, voc definir um corpo de funo para DisplayProjectedValue. J mostramos o prottipo desta funo antes:
void DisplayProjectedValue(double amount, int years, double rate);

O corpo da funo ter a mesma assinatura do prottipo e calcular o valor projetado de um investimento aps um nmero especificado de anos, a uma dada taxa de crescimento. PASSO 1: Abra o projeto InvestmentPlanner. PASSO 2: No final do arquivo-fonte InvestmentPlanner.cpp, digite o cdigo da funo DisplayProjectedValue, que comea por:
void DisplayProjectedValue(double amount, int years, double rate)//+++++++ {

PASSO 3: A seguir, defina algumas variveis locais da funo:


void DisplayProjectedValue(double amount, int years, double rate)//+++++++ { double rateFraction = 1 + rate / 100;

56

double finalAmount = amount * Math::Pow(rateFraction, years); finalAmount = Math::Round(finalAmount, 2);

Aqui, a varivel rateFraction organiza a taxa de crescimento como um valor fracional. Por exemplo, se a taxa for 6%, rateFraction = 1.06. A expresso Math::Pow(rateFraction, years) mostra como elevar um nmero a uma potncia em C++. Por exemplo, Math::Pow(1.06, 3) equivalente a 1.06 * 1.06 * 1.06. A expresso Math::Round(finalAmount, 2) arredonda o nmero finalAmount com duas casas decimais. Por exemplo, se finalAmount = 1000.775, o valor arredondado ser 1000.78. PASSO 4: Agora, adicione declaraes para mostrar o resultado dos clculos:
void DisplayProjectedValue(double amount, int years, double rate)//+++++++ { double rateFraction = 1 + rate / 100; double finalAmount = amount * Math::Pow(rateFraction, years); finalAmount = Math::Round(finalAmount, 2); Console::Write(S"Quantia investida: "); Console::WriteLine(amount); Console::Write(S"Taxa de Crescimento (%): "); Console::WriteLine(rate); Console::Write(S"Perodo (anos): "); Console::WriteLine(years); Console::Write(S"Valor final projetado para o investimento: "); Console::WriteLine(finalAmount); return; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 5: Construa o programa.

4.2.3.

DEFININDO UM CORPO DE FUNO QUE RETORNA UM VALOR

Quando define uma funo com um tipo de retorno no-nulo, voc deve retornar um valor apropriado da funo. Para retornar um valor, use a palavra-chave return seguida pelo valor desejado. NOTA: Se esquecer de retornar um valor, o compilador lhe mostrar uma mensagem de erro indicando a chave final do corpo da funo. neste ponto que o compilador detecta que voc no retornou um valor apropriado da funo.

4.2.3.1.

EXERCCIO: CORPO DE FUNO QUE RETORNA UM VALOR

Neste exerccio, voc definir um corpo de funo para GetInvestmentAmount. J vimos o prottipo desta funo:
double GetInvestmentAmount();

A funo perguntar ao usurio quanto dinheiro ele quer investir; e retornar este valor como um tipo double. Voc tambm definir um corpo para a funo GetInvestmentPeriod. O prottipo desta funo tambm j foi visto:
int GetInvestmentPeriod(int tmin = 10, int tmax = 25);

Esta funo perguntar ao usurio por quanto tempo ele quer investir o dinheiro e retornar este valor como um tipo de dados int. 57

PASSO 1: Abra o projeto InvestmentPlanner. PASSO 2: No final do arquivo InvestmentPlanner.cpp, digite o seguinte corpo para a funo GetInvestmentAmount:
double GetInvestmentAmount()//++++++++++++++++++++++++++++++++++++++++++++ { Console::Write(S"Quantos Reais voc quer investir? "); String __gc *input = Console::ReadLine(); double amount = input->ToDouble(0); return amount; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

A primeira declarao exibe uma mensagem de prompt no console, perguntando quanto em dinheiro o usurio quer investir. A chamada de funo Console::ReadLine l no formato de texto o que foi digitado no console e o resultado passado para uma varivel String. A chamada de funo input->ToDouble analisa gramaticalmente a String e a converte para um valor double. A declarao return retorna este valor de volta funo que estiver chamando GetInvestmentAmount. DICA: Em C++, voc pode declarar variveis locais em qualquer local em uma funo. Por exemplo, as variveis input e amount so declaradas na parte intermediria do corpo da funo GetInvestmentAmount. Tipicamente, voc deve declarar as variveis no ponto onde elas so chamadas pela primeira vez na funo, o que difere da linguagem de programao C, onde voc tem que declarar todas as variveis locais em um bloco no incio do corpo da funo. PASSO 3: Agora, adicione o seguinte corpo de funo:
int GetInvestmentPeriod(int tmin, int tmax)//+++++++++++++++++++++++++++++ { Console::Write(S"Durante quantos anos ["); Console::Write(S"tmin="); Console::Write(tmin); Console::Write(S", tmax="); Console::Write(tmax); Console::Write(S"] ? "); String __gc *input = Console::ReadLine(); int years = input->ToInt32(0); return years; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

A chamada de funo Console::Write pede que o usurio digite um valor entre tmin e tmax; estes valores so fornecidos como parmetros funo GetInvestmentPeriod. A chamada de funo Console::ReadLine l a entrada do usurio como uma String e a chamada input->ToInt32 converte este valor em inteiro de 32 bits. A declarao return retorna este valor para a funo que chamar GetInvestmentPeriod. NOTA: O prottipo de GetInvestmentPeriod declara valores padres para os parmetros tmin e tmax. O valor padro para tmin 10 e o para tmax 25. Valores padres so especificados somente no parmetro da funo voc no menciona estes valores padres no corpo da funo. Se, acidentalmente, voc definir os valores padres no corpo e no prottipo da funo, ver um erro de compilador no corpo da funo. PASSO 4: Construa seu programa.

58

4.3.

CHAMANDO FUNES

Voc j definiu todos os corpos das funes no aplicativo-amostra que estamos desenvolvendo neste captulo. O ltimo passo chamar as funes em lugar apropriado no aplicativo. Para chamar uma funo, especifique seu nome seguido por um par de parnteses. Por exemplo, voc pode chamar a funo DisplayWelcome como segue:
DisplayWelcome();

Este um exemplo simples porque a funo no receber parmetros nem retorna valores. Se quiser chamar uma funo que retorne um valor, voc pode atribuir o valor de retorno a uma varivel. O exemplo abaixo chama a funo GetInvestmentAmount e atribui seu valor de retorno a uma varivel local (double) chamada sum:
double sum = GetInvestmentAmount();

NOTA: Voc pode ignorar o valor de retorno de uma funo se quiser. Quando chamar uma funo, omita o operador de atribuio no lado esquerdo do nome da funo. Ela ainda retornar o valor, mas este ser descartado. Se voc quiser chamar uma funo que recebe parmetros, passe os valores dos parmetros entre parnteses na chamada da funo. O exemplo abaixo chama DisplayProjectedValue, passando trs valores literais como parmetros:
DisplayProjectedValue(10000, 25, 6.0);

NOTA: Voc no deve especificar os tipos de dados dos parmetros quando chama uma funo. Apenas fornea os valores dos parmetros. O exemplo abaixo mostra como chamar uma funo que recebe parmetros e retorna um valor. Neste exemplo, chamamos GetInvestmentPeriod para obter um valor entre 5 e 25. Atribumos o valor de retorno a uma varivel local int chamada period:
int period = GetInvestmentPeriod(5, 25);

4.3.1.

CHAMANDO FUNES NO APLICATIVO-AMOSTRA

A seguir, voc estender seu aplicativo-amostra para incluir as chamadas de funo como descrito acima. PASSO 1: Abra o projeto InvestmentPlanner. PASSO 2: Na funo _tmain, logo aps a chave {, adicione a chamada funo DisplayWelcome:
int _tmain()//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { DisplayWelcome();

PASSO 3: Adicione as seguintes declaraes para exibir uma ilustrao do crescimento do investimento. A chamada funo DisplayProjectedValue mostrar o valor 10000 aps 25 anos a uma taxa de crescimento de 6%.
Console::WriteLine(S"\nIlustrao..."); DisplayProjectedValue(10000, 25, 6.0);

PASSO 4: Adicione as seguintes declaraes para perguntar ao usurio quanto ele quer investir e por quanto tempo. As chamadas s funes GetInvestmentAmount e GetInvestmentPeriod retornam estes valores: 59

Console::WriteLine(S"\nDigite os detalhes para o seu investimento:"); double sum = GetInvestmentAmount(); int period = GetInvestmentPeriod(5, 25);

NOTA: A funo GetInvestmentPeriod tem valores padres para cada um dos seus parmetros (o primeiro parmetro tem como padro 10 e o segundo 25). Voc pode usar estes valores padres quando chamar a funo. Por exemplo, a chamada abaixo usa o valor padro para o segundo parmetro:
int period = GetInvestmentPeriod(5); //O primeiro parmetro 5; o //segundo o padro 25.

Se usar um valor padro para um parmetro, voc deve usar os valores padres para cada parmetro subseqente na lista de parmetros. Por exemplo, a chamada de funo abaixo invlida:
int period = GetInvestmentPeriod(, 20); //Usar valor padro apenas para o //primeiro parmetro ilegal.

PASSO 5: Adicione as declaraes abaixo para calcular e mostrar o valor final projetado deste investimento, assumindo uma taxa de crescimento de 6%:
Console::WriteLine(S"\nSeu plano..."); DisplayProjectedValue(sum, period, 6.0);

PASSO 6: Eis a funo _tmain completa:


int _tmain()//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { DisplayWelcome(); Console::WriteLine(S"\nIlustrao..."); DisplayProjectedValue(10000, 25, 6.0); Console::WriteLine(S"\nDigite os detalhes para o seu investimento:"); double sum = GetInvestmentAmount(); int period = GetInvestmentPeriod(5, 25); Console::WriteLine(S"\nSeu plano..."); DisplayProjectedValue(sum, period, 6.0); return 0; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Construa seu programa e corrija os eventuais erros de compilador. Ento, execute-o para reproduzir a sada mostrada na Figura 4.1.

Figura 4.1: Sada do programa InvestmentPlanner.

60

4.3.2.

PERCORRENDO O APLICATIVO COM O DEPURADOR

Agora voc vai percorrer o aplicativo passo-a-passo com o depurador. Isto o ajudar a compreender como o fluxo de controle passa de uma funo para outra no aplicativo. Este exerccio tambm ilustrar o conceito de escopo de varivel. Voc ver como variveis locais em uma funo entram no escopo durante a execuo da funo e desaparecem do escopo aps a funo. PASSO 1: Abra o projeto InvestmentPlanner. PASSO 2: Localize a funo _tmain. PASSO 3: Insira um ponto de quebra para depurao clicando na borda cinza do lado esquerdo do cdigo. A seguir clique na chamada de DisplayWelcome e voc ver um boto vermelho aparecer na borda, como na Figura 4.2.06

Figura 4.2: Inserindo um ponto de quebra no incio da funo _tmain. PASSO 4: Inicie a sesso de depurao pressionando F5. Uma vez o programa tenha carregado, ele ir executar at parar no ponto de quebra na funo _tmain. Uma seta amarela aparece na margem prxima chamada de DisplayWelcome (Figura 4.3). Esta seta indica que DisplayWelcome a prxima declarao a ser executada. PASSO 5: Pressione F11 para entrar na funo DisplayWelcome. O depurador chama a funo DisplayWelcome e mostra uma seta amarela no incio desta funo (Figura 4.4). NOTA: Alternativamente, voc pode usar a barra de utilitrios Debug para controlar o depurador. Voc pode exibir esta barra selecionando o menu View Toolbars e, ento, escolhendo Debug na lista de barras de utilitrios. Cada uma das teclas de funo de depurao mencionadas acima tem um boto correspondente na barra Debug. PASSO 6: Pressione F10 vrias vezes para percorrer de declarao em declarao a funo DisplayWelcome, que exibir uma mensagem de boas-vindas na janela do console. No fim da funo, o depurador retornar para a funo _tmain (Figura 4.5). PASSO 7: Pressione F10 para depurar a declarao contendo a funo Console::WhiteLine. O depurador executa a funo Console::WriteLine mas no o levar passo-a-passo atravs dela. A seta amarela se desloca para a chamada da funo DisplayProjectedValue em _tmain. 61

Figura 4.3: A seta amarela (F5) indica a prxima declarao a ser executada.

Figura 4.4: A seta amarela (F11) aponta para o incio da funo DisplayWelcome.

Figura 4.5: Aps a depurao de cada declarao (F10) de DisplayWelcome, o depurador retorna para a funo _tmain. 62

PASSO 8: Pressione F11 para entrar na funo DisplayProjectedValue. Selecione o menu Debug Windows Locals. As variveis locais nesta funo sero exibidas na janela Locals (Figura 4.6).

Figura 4.6: Exibindo variveis locais de uma funo atravs da depurao (menu Debug Windows Locals). As trs primeiras variveis amount, years e rate so parmetros da funo. Estas variveis j esto inicializadas com os valores que voc passou para a funo. As duas ltimas variveis finalAmount e rateFraction no tm valores significantes porque ainda no lhes foram atribudos valores. Na realidade, aqui, o depurador pratica uma pequena fraude porque as variveis finalAmount e rateFraction ainda nem foram declaradas. Estas variveis no existem de fato at que o depurador passe pelas suas declaraes na funo. PASSO 9: Pressione F10 vrias vezes para percorrer as declaraes da funo DisplayProjectedValue. Observe como as variveis finalAmount e rateFraction mudam durante a funo (o depurador mostra os valores que foram mudados durante a execuo da declarao anterior em vermelho). D uma olhada na janela console para ver o que est sendo exibido. PASSO 10: Continue pressionando F10 at alcanar o fim da funo DisplayProjectedValue e retornar para _tmain. PASSO 11: Em _tmain, pressione F10 para executar a declarao Console::WriteLine. PASSO 12: Pressione F11 para entrar na funo GetInvestmentAmount. Percorra as declaraes desta funo. Quando o depurador executa a declarao ReadLine, a janela console aparece e voc dever entrar com um nmero. Digite, por exemplo, 20 e pressione a tecla Enter. PASSO 13: Continue percorrendo a funo GetInvestmentAmount at voltar para _tmain.

63

PASSO 14: Pressione F10 mais uma vez e, ento, examine as variveis locais em _tmain. Note que o valor retornado de GetInvestmentAmount foi atribudo varivel local sum em _tmain (Figura 4.7).

Figura 4.7: Janela Locals mostrando como os valores das variveis locais de _tmain mudam medida que voc pressiona F10. PASSO 15: Continue viajando atravs do aplicativo deste modo at que ele finalize. DICA: Se o depurador lhe guia a uma funo que no lhe interessa, pressione Shift + F11 para sair dela. Se voc quer somente executar o aplicativo sem mais paradas, pressione F5.

4.3.3.

COMPREENDENDO O ESCOPO LOCAL E GLOBAL

Como voc viu no exerccio anterior, cada funo define seu prprio escopo para variveis locais. As variveis locais so criadas durante a execuo da funo e so automaticamente destrudas no fim da funo, o que significa que felizmente podemos ter variveis com o mesmo nome em diferentes funes sem interferncia. Tambm possvel declarar variveis globalmente, i.e., fora de qualquer funo. As variveis globais so visveis em todos os corpos de funo que estejam aps suas definies no arquivo-fonte. Voc pode usar variveis globais como um modo rudimentar de compartilhar informaes entre mltiplas funes. IMPORTANTE: As variveis globais geralmente no so consideradas uma boa prtica de programao, especialmente em linguagens orientada para objetos como C++. Como elas so muito expostas e freqentemente podem ser usadas em vrias funes, se uma for corrompida, pode ser difcil definir onde o problema aconteceu. Alm disso, as variveis globais introduzem dependncia entre as funes. Por estas razes voc deve economizar no uso de variveis globais. Um modo melhor de compartilhar informaes entre funes passar parmetros e retornar valores como j vimos neste captulo. 64

4.3.3.1.

EXERCCIO: ESCOPO PARA VARIVEL GLOBAL

Neste exerccio, voc definir uma varivel global no seu aplicativo. Voc usar esta varivel em diversas funes para ilustrar seu escopo global. PASSO 1: Abra o projeto InvestmentPlanner. PASSO 2: Antes da funo _tmain (Figura 4.8), defina uma varivel global int chamada numberOfYourFunctionsCalled, como abaixo:
int numberOfYourFunctionsCalled = 0;

Figura 4.8: Declarando variveis globais. PASSO 3: Encontre a definio da funo DisplayWelcome e, no seu incio, incremente de 1 a varivel numberOfYourFunctionsCalled, como mostrado na Figura 4.9.

Figura 4.9: Incrementando a varivel numberOfYourFunctionsCalled na funo DisplayWelcome.

PASSO 4: Adicione uma declarao similar no incio de todas as demais funes do seu aplicativo. PASSO 5: Modifique a funo _tmain. No final desta funo, exatamente antes da declarao return, mostre na tela o valor da varivel numberOfYourFunctionsCalled (Figura 4.10).

65

Figura 4.10: Imprimindo na tela o nmero de chamadas das funes do usurio. PASSO 6: Construa e execute o programa. Quantas vezes suas funes so chamadas durante a execuo do programa?

4.3.4.

SOBRECARREGANDO FUNES

O Visual C++ nos fornece muitas funes com o mesmo nome, contanto que cada funo tenha uma diferente lista de parmetros. Este processo conhecido como sobrecarga de funo. Uma sobrecarga de uma til se voc tiver diversos modos diferentes de realizar uma operao particular baseada em diferentes parmetros de entrada. Por exemplo, voc pode precisar de uma funo average para calcular o valor mdio de dois valores double e de outra funo average para calcular o valor mdio de uma matriz de inteiros. Voc pode definir duas funes para suportar estes requerimentos. D a cada funo o mesmo nome, average, para enfatizar o propsito comum delas. Defina diferentes listas de parmetros para as funes, para diferenciar uma da outra.
double average(double number1, double number2); double average(int array[], int arraySize);

Voc ter ainda que implementar as duas funes no h mgica em C++! Quando voc chamar a funo average, o compilador deduz qual a verso da funo com base nos valores dos parmetros fornecidos. NOTA: Se voc definir funes sobrecarregadas, elas devem ter diferentes listas de parmetros. Se voc definir funes sobrecarregadas que diferem apenas nos seus tipos de valores de retorno, haver um erro de compilador.

4.3.4.1.

EXERCCIO: FUNES SOBRECARREGADAS

66

Neste exerccio, voc ir definir uma verso sobrecarregada da funo DisplayProjectedValue. A nova verso calcular uma taxa aleatria de crescimento entre 0 e 20%, ao invs de uma taxa de crescimento especfica. PASSO 1: Abra o projeto InvestmentPlanner. PASSO 2: Adicione o seguinte prottipo de InvestmentPlanner.cpp, aps o outro prottipo. funo no incio do arquivo-fonte

void DisplayProjectedValue(double amount, int years);

PASSO 3: Em _tmain, localize a segunda chamada funo DisplayProjectedValue e modifique-a de modo que voc passe somente dois parmetros para ela, como abaixo:
DisplayProjectedValue(sum, period);

PASSO 4: No arquivo-fonte InvestmentPlanner.cpp, aps a definio da outra verso, defina o corpo da nova funo DisplayProjectedValue, como abaixo:
void DisplayProjectedValue(double amount, int years)//++++++++++++++++++++ { numberOfYourFunctionsCalled++; Random __gc *r = new Random(); int randomRate = r->Next(0, 20); DisplayProjectedValue(amount, years, randomRate); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Este funo usa a classe Random para calcular um nmero aleatrio entre 0 e 20. A funo passa o nmero aleatrio para a verso original de DisplayProjectedValue para calcular o valor do investimento usando este taxa aleatria. PASSO 5: Insira pontos de quebra no incio das duas verses de DisplayProjectedValue. PASSO 6: Construa o programa e o inicialize no depurador. PASSO 7: Observe quais as verses de DisplayProjectedValue so chamadas quando seu programa executa. Veja Qual o nmero aleatrio que o programa usa para a taxa de crescimento. PASSO 8: Execute o programa algumas vezes para verificar se a taxa de crescimento realmente aleatria.

4.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 4

A Tabela 4.3 mostra um resumo deste captulo.

67

Tabela 4.1: Resumo do captulo 4.


PARA Declarar um prottipo de funo Definir parmetros padres FAA ISTO Tipo de retorno + nome da funo + (lista de parmetros) + ; Ex.: double MyFunction(int p1, short p2); Defina parmetros padres no prottipo da funo, se requerido. Tipo do parmetro + nome + = + valor padro. Ex.: double MyFunction(int p1, short p2 = 100); Tipo de retorno + nome da funo + (lista de parmetros){}. No especifique aqui parmetros padres. Define o corpo da funo como no exemplo: double MyFunction(int p1, short p2) { int n = p1 + p2; //... } return + valor + ; Ex.: return (p1 + p2) / 2.00; Nome da funo + (valores dos parmetros). Se a funo retornar um valor, voc pode atribui-lo a uma varivel. Ex.: double result = MyFunction(100, 175); Defina a varivel global fora de qualquer funo. Use-a em qualquer funo subseqente no arquivo-fonte. Por exemplo: int myGlobal; void MyFunction() { myGlobal++; } Defina diversas funes com o mesmo nome mas com diferentes listas de parmetros. Implemente cada funo. Chame a verso que deseja usando os valores de parmetro apropriados. Por exemplo: //Prottipos void MyFunction(int p1); void MyFunction(double p1, double p2); //... //Chamando as funes MyFunction(100); MyFunction(2.5, 7.5); //... //Definies das funes void MyFunction(int p1) { //... } void MyFunction(double p1, double p2) { //... }

Definir um corpo de funo

Retornar um valor de uma funo Chamar uma funo

Definir e usar variveis globais

Definir e usar funes sobrecarregadas

68

5.

CAPTULO 5: DECLARAES DE CONDICIONAIS E DE LAOS

Neste captulo, voc aprender como: Tomar decises usando a declarao if; Tomar decises com vrias opes usando a declarao switch; Executar laos usando as declaraes while, for e do-while; Executar saltos incondicionais em um lao usando as declaraes break e continue.

Todas as linguagens de alto nvel fornecem palavras-chaves que lhe permitem tomar decises e executar laos. C++ no exceo, pois fornece a declarao if e a switch para tomadas de deciso, e as declaraes while, for e do-while para laos. Alm disso, C++ fornece a declarao break para sada imediata de um lao e continue para retornar ao incio do lao para a iterao seguinte. Neste captulo, voc ver como usar estas declaraes para controlar o fluxo de execuo atravs de um aplicativo desenvolvido no Microsoft Visual C++.

5.1.

TOMANDO DECISES COM A DECLARAO if

O modo mais comum de tomar uma deciso no Visual C++ usar a declarao if. Voc pode usar if para realizar teste simples, teste duplo, mltiplos testes ou teste aninhado. Vamos considerar inicialmente testes simples.

5.1.1.

REALIZANDO TESTES SIMPLES

A Figura 5.1 ilustra a ocorrncia de um teste simples no fluxo de um programa.

Figura 5.1: Um testes simples no fluxo de um programa. O exemplo abaixo mostra como definir um teste simples no Visual C++: 69

if(number < 0) Console::WriteLine(S"O nmero negativo"); Console::WriteLine(S"Fim");

A palavra-chave if seguida por uma expresso condicional entre parnteses (os parnteses so obrigatrios). Se o resultado da avaliao da expresso condicional for true, a prxima declarao executada a mensagem O nmero negativo ser exibida. Note que a mensagem Fim sempre ser executada independentemente do resultado do teste, porque ela no faz parte do corpo do if. NOTA: No h ponto-e-vrgula aps o parntese de fechamento do teste if. Um dos erros de mais comuns em programao C++ consiste em, por esquecimento, colocarmos um ponto-e-vrgula ali. Veja um exemplo de como no fazer:
if(number < 0); //Note o ponto-e-vrgula desastroso.

A declarao acima equivale seguinte declarao (que provavelmente no a que desejamos):


if(number < 0) ; //Corpo do if vazio, i.e., nada fazer se number < 0.

Se voc quer incluir mais de uma declarao no corpo do if, limite este corpo por chaves:
if(number < 0) { Console::Write(S"O nmero "); Console::Write(number); Console::WriteLine(S" negativo"); } Console::WriteLine(S"Fim");

DICA: boa prtica colocar o corpo do if entre chaves, mesmo se ele contiver apenas uma declarao. Este o estilo defensivo de programar, caso voc (ou outro programador) adicione mais declaraes ao corpo do if no futuro.

5.1.1.1.

EXERCCIO: UM NOVO APLICATIVO PARA TESTES SIMPLES

Neste exerccio, voc ir criar um novo aplicativo para realizar teste simples. medida que avanamos neste captulo, voc estender o aplicativo para tomadas de decises mais complexas e para executar laos. Por enquanto, o aplicativo, chamado CalendarAssistant, pedir que o usurio digite uma data e, ento, ele realizar uma validao simples e exibir a data no console em um formato amigvel. PASSO 1: Abra o Microsoft Visual Studio .NET e crie um novo projeto Visual C++ Console Application (.NET). Chame-o de CalendarAssistant. PASSO 2: Coloque na tela o cdigo do arquivo-fonte CalendarAssistant.cpp. PASSO 3: No topo deste arquivo, imediatamente aps a linha using namespace System;, adicione os prottipos de funo abaixo (voc os implementar ao longo deste captulo).
//PROTTIPOS DE FUNES DO USURIO**************************************** int GetYear(); int GetMonth(); int GetDay(int year, int month); void DisplayDate(int year, int month, int day); //************************************************************************

PASSO 4: Aps a funo _tmain, implemente a funo GetYear do seguinte modo: 70

int GetYear()//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { Console::Write(S"Ano? "); String *input = Console::ReadLine(); int year = input->ToInt32(0); return year; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 5: Implemente a funo GetMonth como mostrado abaixo (este cdigo uma implementao simplificada. Mais adiante, neste captulo, voc ir melhorar a funo para garantir que o usurio entre com um ms vlido).
int GetMonth()//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { Console::Write(S"Ms? "); String *input = Console::ReadLine(); int month = input->ToInt32(0); return month; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 6: Implemente a funo GetDay como mostrado abaixo (voc melhorar esta funo mais tarde para garantir que o usurio entre com um dia vlido, dado o ano e o ms).
int GetDay(int year, int month)//+++++++++++++++++++++++++++++++++++++++++ { Console::Write(S"Dia? "); String *input = Console::ReadLine(); int day = input->ToInt32(0); return day; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 7: Implemente a funo DisplayDate como mostrado abaixo para exibir a data com os trs nmeros (mais adiante, neste captulo, voc tambm melhorar esta funo para exibir a data num formato mais amigvel).
void DisplayDate(int year, int month, int day)//++++++++++++++++++++++++++ { Console::WriteLine(S"\nEsta a data que voc digitou:"); Console::Write(day); Console::Write(S"-"); Console::Write(month); Console::Write(S"-"); Console::Write(year); Console::WriteLine(); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 8: Adicione o cdigo abaixo na funo _tmain. Este cdigo pede que o usurio digite um ano, um ms e um dia. Se a data passar em um teste de validao simplificado, ela exibida no console; caso contrrio, nada ser exibido.
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Bem-vindo ao Assistente de Calendrio"); Console::WriteLine(S"\nPor favor, digite uma data (ano, ms, dia)"); int year = GetYear(); int month = GetMonth(); int day = GetDay(year, month); //Teste simplificado (assume que todo ms tem 31 dias). if(month >= 1 && month <= 12 && day >= 1 && day <= 31) { DisplayDate(year, month, day);

71

} Console::WriteLine(S"\nFim\n"); return 0; }//************************************************************************

NOTA: Esta declarao if combina vrios testes usando o operador lgico AND (&&). Como voc aprendeu no captulo 3, os testes lgicos so realizados da esquerda para a direita. Somente se o resultado do teste for true a funo DisplayDate chamada. Se, por exemplo, o ms for 0, o resultado do teste false e nenhum outro teste realizado a data definitivamente invlida. Este teste conhecido como avaliao de atalho. PASSO 9: Construa o programa e corrija quaisquer eventuais erros de compilao. PASSO 10: Execute o programa. Entre com nmeros vlidos para ano, ms e dia (por exemplo, 2001, 12 e 31). O programa mostra mensagens como as exibidas na Figura 5.2.

Figura 5.2: Sada do programa CalendarAssistant para uma data vlida. Note que o programa exibe a data porque ela vlida. A mensagem Fim tambm mostrada. PASSO 11: Agora, execute o programa, entrando com uma data invlida (por exemplo, 2001, 0 e 31). O programa mostra as mensagens exibidas na Figura 5.3.

Figura 5.3: Sada do CalendarAssistant para uma data invlida.

72

Note que o programa no exibe a data porque ela no vlida. Neste caso, ele exibe apenas a mensagem Fim. Voc pode tornar este programa mais interativo exibindo uma mensagem de erro se a data for invlida. Veja como fazer isto na seqncia.

5.1.2.

REALIZANDO TESTES DUPLOS

A Figura 5.4 ilustra o que ocorre num fluxo de cdigo quando voc realiza um teste duplo.

Figura 5.4: Teste duplo ao longo de um fluxo de cdigo. O cdigo abaixo mostra como definir um teste duplo para o aplicativo CalendarAssistant:
if(month >= 1 && month <= 12 && day >= 1 && day <= 31) { DisplayDate(year, month, day); } else { Console::WriteLine(S"Data invlida"); }

O corpo do else define qual a ao a ser empregada se o teste condicional for false.

5.1.2.1.

EXERCCIO: TESTE DUPLO

Neste exerccio voc modificar o CalendarAssistant para que ele exiba uma mensagem de erro se ocorrer uma entrada de data errada. PASSO 1: Abra o CalendarAssistant. PASSO 2: Modifique a funo _tmain de modo que ela use a declarao if-else abaixo para datas vlidas e tambm invlidas.
if(month >= 1 && month <= 12 && day >= 1 && day <= 31) { DisplayDate(year, month, day); } else { Console::WriteLine(S"Data invlida"); }

73

PASSO 3: Construa e execute o programa. Entre com uma data invlida, como por exemplo, 2001, 0, 31. Agora, o programa exibe a mensagem de erro mostrada na Figura 5.5.

Figura 5.5: Sada do CalendarAssistant para uma entrada de data invlida.

5.1.3.

REALIZANDO MLTIPLOS TESTES

Voc pode organizar declaraes if-else em uma cascata para executar mltiplas decises. A Figura 5.6 ilustra o fluxograma de um mltiplo teste.

Figura 5.6: Fluxograma de um mltiplo teste. O cdigo abaixo mostra como usar um mltiplo teste para determinar o nmero mximo de dias (maxDay) em um ms: 74

int maxDay; if(month == 4 || month == 6 || month == 9 || month == 11) { maxDay = 30; } else if(month == 2) { maxDay = 28; } else { maxDay = 31; }

Se o ms for abril, junho, setembro ou novembro, maxDay = 30; se for fevereiro, maxDay = 28 (por enquanto, estamos ignorando anos bissextos); caso contrrio, maxDay = 31. NOTA: H um espao entre as palavras-chaves else e if porque elas so distintas, o que no ocorre, por exemplo, com o Microsoft Visual Basic .NET, onde a palavra-chave nica Elseif.

5.1.3.1.

EXERCCIO: NMERO MXIMO DE DIAS DE UM DADO MS

Neste exerccio, voc melhorar o seu CalendarAssistant para que ele mostre o nmero mximo de dias do ms escolhido pelo usurio. PASSO 1: Abra o CalendarAssistant. PASSO 2: Modifique a funo GetDay para que ela use a declarao if-else para determinar o nmero mximo de dias.
int GetDay(int year, int month)//+++++++++++++++++++++++++++++++++++++++++ { int maxDay; if(month == 4 || month == 6 || month == 9 || month == 11) { maxDay = 30; } else if(month == 2) { maxDay = 28; } else { maxDay = 31; } Console::Write(S"Dia [1 a "); Console::Write(maxDay); Console::Write(S"]? "); String *input = Console::ReadLine(); int day = input->ToInt32(0); return day; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 3: Construa e execute o programa. Entre com ano 2001 e ms 1. O programa pedir que voc entre com um dia entre 1 e 31, como vemos na Figura 5.7. Entre com um valor de dia vlido e feche o console quando a data for mostrada. PASSO 4: Execute o programa novamente. Entre com ano 2001 e ms 2. O programa pedir que voc entre com um dia entre 1 e 28, como vemos na Figura 5.8. 75

Figura 5.7: Nova sada para o CalendarAssistant (ms de 31 dias).

Figura 5.8: Nova sada para o CalendarAssistant (ms de fevereiro). Entre com um valor de dia vlido e feche o console quando a data for mostrada (no se preocupe com a validao de datas em _tmain. Voc a remover mais adiante e a trocar por uma validao mais compreensvel nas funes GetMonth e GetDay).

5.1.4.

REALIZANDO TESTES ANINHADOS

Voc pode realizar testes aninhados, como ilustrados na Figura 5.9.

Figura 5.9: Fluxograma de testes aninhados. 76

O cdigo abaixo mostra como usar testes aninhados para processar a ocorrncia de anos bissextos no aplicativo CalendarAssistant:
int maxDay; if(month == 4 || month == 6 || month == 9 || month == 11) { maxDay = 30; } else if(month == 2) { bool isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400) == 0); if(isLeapYear) { maxDay = 29; } else { maxDay = 28; } } else { maxDay = 31; }

Se o ms for fevereiro, definimos uma varivel bool para determinar se o ano bissexto. Um ano bissexto se divisvel por 4 mas no divisvel por 100 (exceto os anos que so divisveis por 400, que so bissextos). A Tabela 5.1 mostra alguns exemplos de anos bissextos e no-bissextos. Tabela 5.1: Exemplos de anos bissextos e anos normais.
ANO 1996 1997 1900 2000 BISSEXTO? Sim No No Sim

Ento, usamos uma declarao if aninhada para testar a varivel bool isLeapYear para podermos atribuir um valor apropriado para maxDay. NOTA: A declarao if aninhada no contm um teste explcito. O condicional if(isLeapYear) equivalente a if(isLeapYear != false).

5.1.4.1.

EXERCCIO: TESTES ANINHADOS

Neste exerccio, voc melhorar seu aplicativo CalendarAssistant para lidar corretamente com anos bissextos. PASSO 1: Abra o CalendarAssistant. PASSO 2: Modifique a funo GetDay para que ela funcione com o bloco de cdigo mostrado acima para anos bissextos.

77

PASSO 3: Construa e execute o programa. Entre com o ano de 1996 e o ms 2. O programa pede que voc digite um dia entre 1 e 29. Digite um valor vlido e feche a janela do console quando a data for exibida. PASSO 4: Execute novamente o programa. Entre com o ano de 1997 e o ms 2. Agora, o programa pede que voc digite um dia entre 1 e 28. PASSO 5: Execute o programa algumas vezes usando os dados de teste da Tabela 5.1.

5.2.

TOMANDO DECISES COM A DECLARAO switch

Agora que voc viu como funciona a declarao if, vamos dar uma olhada na declarao switch. Esta declarao nos permite testar uma nica varivel e executar uma das vrias ramificaes que dependem do seu valor.

5.2.1.

DEFININDO DECLARAES switch SIMPLES

A Figura 5.10 ilustra o funcionamento da declarao switch.

Figura 5.10: Fluxograma da declarao switch. O exemplo abaixo mostra a sintaxe para a declarao switch. Esta declarao testa a varivel int numberOfSides, que o nmero de lados de uma figura geomtrica, e mostra uma mensagem com o nome da figura:
int numberOfSides; //N de lados de uma figura geomtrica //... switch(numberOfSides) { case 3:

78

Console::Write(S"Tringulo"); break; case 4: Console::Write(S"Quadriltero"); break; case 5: Console::Write(S"Pentgono"); break; case 6: Console::Write(S"Hexgono"); break; case 7: Console::Write(S"Heptgono"); break; case 8: Console::Write(S"Octgono"); break; case 9: Console::Write(S"Enegono"); break; case 10: Console::Write(S"Decgono"); break; default: Console::Write(S"Polgono"); break; }

A palavra-chave switch seguida por uma expresso entre parnteses (a expresso deve avaliar um valor baseado em inteiro, um caractere ou um valor de enumerao). Na seqncia, limitada por chaves, temos uma srie de ramificaes case. NOTA: Cada nvel case especifica uma nica constante numrica. Voc no pode especificar mltiplos valores e tambm no pode definir um intervalo de valores. Cada ramificao case pode conter qualquer nmero de declaraes. No final de cada ramificao, use a declarao break para sair da declarao switch. NOTA: Novamente no h necessidade de usar chaves dentro de cada ramificao. A declarao break delimita o fim de cada ramificao case. Porm, voc precisa usar chaves se tiver que declarar uma varivel dentro do cdigo da ramificao. DICA: boa prtica definir uma ramificao default mesmo se voc no tiver qualquer processamento padro a realizar. A incluso da ramificao default mostra que voc no esqueceu destas recomendaes. Tambm, a ramificao default pode ajud-lo a capturar valores inesperados e, ento, exibir uma advertncia conveniente ao usurio.

5.2.1.1.

EXERCCIO: MELHORANDO O CalendarAssistant

Neste exerccio, voc melhorar seu aplicativo CalendarAssistant para exibir meses como uma string como Janeiro, Fevereiro, etc.. PASSO 1: Abre o aplicativo CalendarAssistant. PASSO 2: Modifique a funo DisplayDate de acordo com o cdigo abaixo. Ao invs de mostrar o ms como um inteiro, use uma declarao switch para mostr-lo como uma string:
switch(month) {

79

case 1: Console::Write(S"Janeiro"); break; case 2: Console::Write(S"Fevereiro"); break; case 3: Console::Write(S"Maro"); break; case 4: Console::Write(S"Abril"); break; case 5: Console::Write(S"Maio"); break; case 6: Console::Write(S"Junho"); break; case 7: Console::Write(S"Julho"); break; case 8: Console::Write(S"Agosto"); break; case 9: Console::Write(S"Setembro"); break; case 10: Console::Write(S"Outubro"); break; case 11: Console::Write(S"Novembro"); break; case 12: Console::Write(S"Dezembro"); break; default: Console::Write(S"Valor desconhecido"); break; }

PASSO 3: Construa o programa. PASSO 4: Execute o programa algumas vezes e entre com meses diferentes. Verifique se o programa mostra o nome correto do ms.

5.2.2.

DEFININDO PASSAGEM DIRETA EM UMA DECLARAO switch

Se voc omitir a declarao break no final de uma ramificao case, o fluxo de controle continua para a declarao seguinte. Este processo chamado passagem direta. O exemplo abaixo ilustra como a passagem direta pode ser til. Este exemplo testa uma letra minscula para verificar se ela vogal ou consoante:
char lowercaseLetter; //Letra minscula. Por exemplo, 'a' //... switch(lowercaseLetter) { case 'a': case 'e': case 'i':

80

case 'o': case 'u': Console::Write(S"Vogal"); break; default: Console::Write(S"Consoante"); break; }

No h declarao break nos primeiros quatro nveis case. O Fluxo de controle passa atravs da prxima declarao executvel para exibir a mensagem Vogal. A ramificao default lida com todas as outras letras e exibe a mensagem Consoante.

5.2.2.1.

EXERCCIO: PASSAGEM DIRETA EM UMA DECLARAO switch

Neste exerccio, voc melhorar seu aplicativo CalendarAssistant para exibir a estao que contm a data digitada pelo usurio. PASSO 1: Abra o CalendarAssistant. PASSO 2: Modifique a funo DisplayDate. Aps a exibio do ano, ms e dia, adicione o seguinte cdigo para mostrar a estao:
switch(month) { case 12: case 1: case 2: Console::Write(S"Inverno"); break; case 3: case 4: case 5: Console::Write(S"Primavera"); break; case 6: case 7: case 8: Console::Write(S"Vero"); break; case 9: case 10: case 11: Console::Write(S"Outono"); break; }

PASSO 3: Construa o programa. PASSO 4: Execute o programa algumas vezes e entre com diferentes meses. Verifique se o programa est exibindo corretamente a estao do ano.

5.3.

EXECUTANDO LAOS

No restante deste captulo, voc ver como executar laos no Visual C++. Voc tambm ver como realizar saltos incondicionais em um lao usando as declaraes break e continue. O Visual C++ tem trs laos construdos: 81

O lao while; o for; o lao do-while.

Primeiramente, vamos dar uma olhada no lao while.

5.3.1.

USANDO OS LAOS while

A Figura 5.11 ilustra um lao while simples.

Figura 5.11: Um lao while simples. O cdigo abaixo exemplifica o modo de escrever um lao while simples no Visual C++:
int count = 1; while(count <= 5) { Console::WriteLine(count * count); count++; } Console::WriteLine(S"Fim");

A palavra-chave while seguida por uma expresso condicional entre parnteses (os parnteses so obrigatrios). Se o resultado da avaliao da expresso condicional for true, o corpo de while executado. Aps o corpo do lao ser executado, o controle retorna para a declarao de while e a expresso condicional testada novamente. Esta seqncia continua at que o resultado da avaliao do teste seja false. DICA: Lembre-se sempre de incluir algum tipo de declarao de atualizao no lao de modo que ele eventualmente termine. A declarao de atualizao no exemplo acima count++;, que incrementa o contador do lao. Se voc no providenciar uma declarao de atualizao, o lao se repetir para sempre. A Figura 5.12 mostra a sada para o exemplo acima (programa LacoWhile).

82

Figura 5.12: Sada do programa LacoWhile.

5.3.1.1.

EXERCCIO: USANDO O LAO while NO PROJETO CalendarAssistant

Neste exerccio, voc melhorar o aplicativo CalendarAssistant de modo que o usurio possa entre com cinco datas. PASSO 1: Abra o projeto CalendarAssistant. PASSO 2: Modifique o cdigo da funo _tmain para permitir que o usurio entre com cinco datas.
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Bem-vindo ao Assistente de Calendrio"); int count = 1;//Declare e inicializa o contador do lao while(count <= 5)//Testa o contador do lao { Console::Write(S"\nPor favor, digite uma data "); Console::WriteLine(count); int year = GetYear(); int month = GetMonth(); int day = GetDay(year, month); if(month >= 1 && month <= 12 && day >= 1 && day <= 31) { DisplayDate(year, month, day); } else { Console::WriteLine(S"Data invlida"); } count++; } Console::WriteLine(S"\nFim\n"); return 0; }//************************************************************************

PASSO 3: Construa e execute o programa. Ele pedir que voc entre com a primeira data. Aps voc digitar esta data, o programa pedir a segunda e este processo continua at que voc digite a quinta data, quando o programa termina. A Figura 5.13 mostra o resultado de uma execuo do CalendarAssistant atual.

83

Figura 5.13: Sada do programa CalendarAssistant usando o lao while.

5.3.2.

USANDO LAOS for

O lao for uma alternativa ao while. A Figura 5.14 ilustra este tipo de lao.

Figura 5.14: Lao for. O exemplo abaixo mostra como escrever um lao for simples no Visual C++. Este exemplo realiza exatamente a mesma tarefa do lao while do exemplo da Figura 5.12.
for(int count = 1; count <= 5; count++) { Console::WriteLine(count * count); } Console::WriteLine(S"Fim");

Os parnteses aps a palavra-chave for contm trs expresses separadas por ponto-e-vrgula. A primeira expresso a inicializao do lao, i.e., fixa o offset dos contadores de ciclos (a expresso de inicializao executada apenas uma vez, no incio do lao). 84

NOTA: Voc pode declarar as variveis do lao na primeira expresso da declarao for. O exemplo acima ilustra esta tcnica. A varivel count local declarao for e perde a validade quando o lao termina. A segunda expresso na declarao for define um teste. Se o teste for avaliado como true, o corpo do lao executado. Aps o corpo do lao ter sido executado, a expresso final na declarao for executada. Esta expresso realiza as operaes de atualizao do lao, tais como incrementar os contadores de ciclos. NOTA: A declarao for muito flexvel. Voc pode omitir qualquer uma das trs expresses na construo for contanto que retenha os pontos-e-vrgulas separadores. Voc pode inclusive omitir as trs expresses, como em for(;;), o que representa um lao infinito. A Figura 5.15 mostra a sada do exemplo acima.

Figura 5.15: Sada do exemplo para o lao for.

5.3.2.1.

EXERCCIO: USADO O LAO for NO PROJETO CalendarAssistant

Neste exerccio, voc modificar o aplicativo CalendarAssistant de modo que ele use um lao for no lugar do lao while para obter as cinco datas do usurio. PASSO 1: Abra o CalendarAssistant. PASSO 2: Modifique a funo _tmain para trocar o lao while pelo for, usando o cdigo abaixo:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Bem-vindo ao Assistente de Calendrio"); for(int count = 1; count <= 5; count++) { Console::Write(S"\nPor favor, digite uma data "); Console::WriteLine(count); int year = GetYear(); int month = GetMonth(); int day = GetDay(year, month); if(month >= 1 && month <= 12 && day >= 1 && day <= 31) { DisplayDate(year, month, day); } else { Console::WriteLine(S"Data invlida"); }

85

} Console::WriteLine(S"\nFim\n"); return 0; }//************************************************************************

Note que a declarao count++; no mais necessria aps a exibio dos dados porque isto j feito na prpria declarao for. PASSO 3: Construa e execute o programa. Ele funcionar como antes.

5.3.3.

USANDO LAOS do-while

A terceira e ltima construo cclica no Visual C++ o lao do-while. Ele , fundamentalmente, diferente do while e do for, porque o teste vem no final do corpo do lao, o que significa que o seu corpo sempre executado pelo menos uma vez. A Figura 5.16 mostra o lao do-while.

Figura 5.16: Lao do-while. O exemplo abaixo mostra como escrever um lao do-while simples no Visual C++. Este exemplo gera nmeros aleatrios entre 1 e 6, inclusive, para simular um dado, e conta quanto lanamentos so necessrios para obtermos um 6.
//FUNO PRINCIPAL******************************************************** int _tmain() { Random *r = new Random(); int randomNumber; int throws = 0; do { randomNumber = r->Next(1, 7); Console::WriteLine(randomNumber); throws++; }while(randomNumber != 6); Console::Write(S"Voc realizou "); Console::Write(throws); Console::WriteLine(S" tentativas para obter um 6");

86

return 0; }//***********************************************************************

O lao comea com a palavra-chave do, seguida pelo corpo do lao, seguido pela palavra-chave while e uma condio de teste. Um ponto-e-vrgula requerido aps o parntese de fechamento da condio de teste. A Figura 5.17 mostra uma sada para o exemplo acima (projeto LacoDoWhile).

Figura 5.17: Sada do programa LacoDoWhile.

5.3.3.1.

EXERCCIO: USANDO O LAO do-while NO PROJETO CalendarAssistant

PASSO 1: Abra o projeto CalendarAssistant. PASSO 2: Modifique a funo GetMonth com o cdigo abaixo para que ela force o usurio a entra com um ms vlido:
int GetMonth()//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { int month = 0; do { Console::Write(S"Ms [1 a 12]? "); String *input = Console::ReadLine(); month = input->ToInt32(0); }while(month < 1 || month > 12); return month; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 3: Modifique a funo GetDay com o cdigo abaixo para que ela force o usurio a entrar com um dia vlido:
int GetDay(int year, int month)//+++++++++++++++++++++++++++++++++++++++++ { int maxDay, day = 0; if(month == 4 || month == 6 || month == 9 || month == 11) { maxDay = 30; } else if(month == 2) {

87

bool isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); if(isLeapYear) { maxDay = 29; } else { maxDay = 28; } } else { maxDay = 31; } do { Console::Write(S"Dia [1 a "); Console::Write(maxDay); Console::Write(S"]? "); String *input = Console::ReadLine(); day = input->ToInt32(0); }while(day < 1 || day > maxDay); return day; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 4: Construa e execute o programa. PASSO 5: Experimente entrar com um ms invlido. O programa mantm a pergunta para voc entrar com outro ms at que voc digite um valor entre 1 e 12, inclusive. PASSO 6: Experimente entrar com um dia invlido. O programa mantm a pergunta para voc entrar com outro dia at que voc digite um nmero vlido (que depende da sua escolha do ano e do ms).

5.3.4.

REALIZANDO SALTOS INCONDICIONAIS

O Visual C++ fornece duas palavras-chaves break e continue que lhe permitem realizar saltos incondicionais dentro de um lao. A declarao break faz com que voc saia imediatamente do lao. A declarao continue abandona o ciclo atual e volta ao topo do lao pronto para a prxima iterao. NOTA: As declaraes break e continue podem dificultar a compreenso do fluxo lgico atravs do lao. Use economicamente estas declaraes para evitar complicaes desnecessrias no seu cdigo.

5.3.4.1.

EXERCCIO: USANDO SALTOS INCONDICIONAIS NO CalendarAssistant

Neste exerccio, voc modificar o lao principal no seu aplicativo CalendarAssistant. Voc dar ao usurio a chance de finalizar prematuramente o lao, saltar a data atual e continuar na prxima, ou mostrar a data atual quando normal. PASSO 1: Abra o CalendarAssistant.

88

PASSO 2: Modifique a _tmain com o cdigo abaixo para permitir ao usurio saltar fora do lao ou do ciclo atual se desejar:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Bem-vindo ao Assistente de Calendrio"); for(int count = 1; count <= 5; count++) { Console::Write(S"\nPor favor, digite uma data "); Console::WriteLine(count); int year = GetYear(); int month = GetMonth(); int day = GetDay(year, month); Console::WriteLine(S"\nPressione B (break),"); Console::WriteLine(S"C (continue), ou"); Console::Write(S"qualquer outra tecla para mostrar a data: "); String *input = Console::ReadLine(); if(input->Equals(S"b") || input->Equals(S"B")) { break; } else if(input->Equals(S"c") || input->Equals(S"C")) { continue; } DisplayDate(year, month, day); } Console::WriteLine(S"\nFim\n"); return 0; }//************************************************************************

PASSO 3: Construa e execute o programa. PASSO 4: Depois que voc digita a primeira data, o programa pergunta se voc que sair do lao (b ou B), sair do ciclo (c ou C) ou continuar normalmente o fluxo (qualquer outra tecla). Faa com que esta primeira data seja exibida normalmente. PASSO 5: Entre com a segunda data e, ento, pressione c. Isto faz com que a declarao continue seja executada. A declarao continue abandona o ciclo atual sem mostrar sua data. Ao invs disto, o programa pede que voc digite a terceira data. PASSO 6: Entre com a terceira data e, ento, pressione B. Isto faz com que a declarao break seja executada, o que provoca o fim do lao.

5.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 5

A Tabela 5.2 mostra um resumo deste captulo.

89

Tabela 5.2: Resumo do captulo 5.


PARA Executar um teste simples FAA ISTO if(teste){} Ex.: if(n < 0) { Console::Write(S"O nmero "); Console::Write(n); Console::WriteLine(S" negativo"); } Use uma construo if-else. Ex.: if(n < 0) { Console::Write(S"Negativo"); } else { Console::Write(S"No Negativo"); } Use uma construo if-else-if. Ex.: if(n < 0) { Console::Write(S"Negativo"); } else if(n == 0) { Console::Write(S"Zero"); } else { Console::Write(S"Positivo"); } Use a palavra-chave switch seguida por uma expresso inteira entre parnteses. Defina ramificaes case para cada valor que quiser testar e defina uma ramificao padro para todos os outros valores. User a declarao break para encerrar uma ramificao. Ex.: int dayNumber; //0 = Domingo, 1 = Segundafeira, etc. //.. switch(dayNumber) { case 0: case 6: Console::Write(S"Fim de semana"); break; default: Console::Write(S"Dias teis"); break; } Use a palavra-chave while seguida por um teste entre parntese. Ex.: int n = 10; while(n >= 0) { Console::WriteLine(n);

Executar um teste duplo

Executar um mltiplo teste

Teste uma expresso simples em um conjunto finito de valores constantes

Executar ciclos usando um lao while

90

n--; } Executar ciclos usando um lao for Use a palavra-chave for seguida por um par de parnteses (expresso de inicializao; expresso de teste; expresso de atualizao). Ex.: for(int n = 10; n >= 0; n--) { Console::WriteLine(n); } Executar ciclos usando o lao do-while Use a palavra-chave do, seguida pelo corpo do lao, seguido pela palavra-chave while e a condio de teste. Termine o lao com um ponto-e-vrgula. Ex.: int n; do { String *input = Console::ReadLine(); n = input->ToInt32(0); }while(n > 100); Finalizar, prematuramente, um lao Use a declarao break dentro de qualquer lao. Ex.: for(int n = 0; n < 1000; n++) { int square = n * n; if(square > 3500) { break; } Console::WriteLine(square); } Abandonar um ciclo do lao e continuar Use a declarao continue dentro de com a prxima iterao qualquer lao. Ex.: for(int n = 0; n < 1000; n++) { int square = n * n; if(square % 2 == 0) { continue; } Console::WriteLine(square); }

91

PARTE 2: MAIS SOBRE PROGRAMAO ORIENTADA PARA OBJETOS 6. CAPTULO 6: MAIS SOBRE CLASSES E OBJETOS

Neste captulo, voc aprender como Organizar classes em arquivos de cabealho e arquivos-fontes; Criar e destruir objetos; Definir construtores para inicializar um objeto e um destrutor para fazer a limpeza quando um objeto destrudo; Definir membros amplos de classe usando a palavra-chave static; Definir relaes entre diferentes objetos em um aplicativo.

Como voc viu no captulo 2, C++ uma linguagem de programao orientada para objetos. Voc define classes para representar os tipos importantes de entidades no seu aplicativo e cria objetos como instncias destas classes. Por exemplo, um aplicativo de recursos humanos pode definir classes tais como Employee e Contract. Quando o aplicativo estiver executando, ele poder criar um novo objeto Employee toda vez que um novo empregado entrar na companhia e um novo objeto Contract para descrever os termos empregatcios do atual contrato. Este captulo retoma a introduo a classes e objetos do captulo 2. Aqui, voc ver como organizar classes em arquivos de cabealho e arquivos-fontes, o que lhe permite manter uma ntida separao entre uma definio de classe e sua implementao. Voc tambm aprender como fornecer construtores para inicializar novos objetos quando eles so criados. De modo similar, voc fornecer destrutores para limpar objetos antes que eles sejam destrudos. A maioria dos membros de dados e das funes-membros em uma classe so membros de instncia porque eles pertencem a instncias especficas da classe. Tambm possvel definir membros de classe, que pertencem classe como um todo. Voc ver como definir membros de classe neste captulo usando a palavra-chave static. Finalmente voc ver como criar relaes entre objetos em C++. Este conceito importante em programao orientada para objetos porque ele permite que os objetos se comuniquem entre si em uma execuo do aplicativo.

6.1.

ORGANIZANDO CLASSES EM ARQUIVOS DE CABEALHO E ARQUIVOS-FONTES

No captulo 2, voc viu como definir uma classe simples e como implementar as suas funesmembros inline. Considere a seguinte classe, que representa uma conta de carto de crdito:
class CreditCardAccount//************************************************* { public: void PrintStatement()//+++++++++++++++++++++++++++++++++++++++++++++++ { Console::Write(S"Balano do carto de crdito: "); Console::WriteLine(currentBalance); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ private: double currentBalance; };//**********************************************************************

92

CLASSES GERENCIADAS VERSUS CLASSES NO GERENCIADAS No captulo 2, usamos a palavra-chave __gc nas definies de classe. Esta palavra-chave cria uma classe gerenciada, o que significa que o coletor de lixo do .NET Framework automaticamente destri objetos no usados no seu programa. Voc aprender mais sobre classes gerenciadas no captulo 7. Porm, antes de aprender sobre classes gerenciadas, importante compreender como funcionam as classes no gerenciadas. Com uma classe no gerenciada, voc tem que deletar explicitamente os objetos no seu prprio cdigo. Este captulo descreve como usar classes no gerenciadas. A classe CreditCardAccount do exemplo acima contm uma nica funo-membro chamada PrintStatement. Esta funo foi declarada public. Assim ela pode ser acessada por outras partes do programa. A classe tambm contm um nico membro de dados chamado currentBalance, que foi declarada private para preservar o encapsulamento. Note que a definio da classe contm o corpo completo da funo PrintStatement, no apenas o prottipo. Este procedimento conhecido como funo inline. As funes inline so apropriadas s classes simples e podem causar muita desordem em classes maiores. Imagine uma classe contendo 30 funes. A definio da classe seria muito longa e seria muito difcil checar as assinaturas das funes na classe. A soluo dividir a definio da classe em duas partes: um arquivo de cabealho e um arquivo-fonte, como ilustrado na Figura 6.1.

Figura 6.1: Diviso da definio da classe em um arquivo de cabealho e um arquivo-fonte. NOTA: Voc pode usar quaisquer nomes e extenses de arquivo que desejar para o arquivo de cabealho e o fonte. A maioria dos desenvolvedores usa o mesmo nome da classe com as extenses .h e .cpp. 93

O arquivo de cabealho, CreditCardAccount.h, contm a definio da classe. Note que a definio da classe, agora, contm prottipos de funo ao invs de corpos de funo. Estes prottipos tornam mais legvel o arquivo de cabealho porque as assinaturas das funes esto mais proeminentes. O arquivo-fonte, CreditCardAccount.cpp, contm todos os corpos de funo para a classe. Cada funo deve ser prefixada pelo nome da classe qual pertence, seguido por dois sinais doispontos:
void CreditCardAccount::PrintStatement() { //Corpo da funo }

A sintaxe :: o operador de resoluo de escopo C++. Neste exemplo, o operador de resoluo de escopo no informa que a funo PrintStatement pertence classe CreditCardAccount. NOTA: Voc deve colocar uma declarao #include no incio do arquivo-fonte para incluir o arquivo de cabealho para a classe. Por exemplo, o CreditCardAccount.cpp tem a declarao #include CreditCardAccount.h. O compilador precisa das informaes contidas neste arquivo de cabealho para compilar os corpos de funo no arquivo-fonte.

6.1.1.
6.1.1.1.

DEFININDO UMA CLASSE EM UM ARQUIVO DE CABEALHO


EXERCCIO: CRIANDO O APLICATIVO CreditOrganizer E A CLASSE CreditCardAccount NO ARQUIVO DE CABEALHO CreditCardAccount.h

Neste exerccio, voc criar um novo aplicativo e definir uma classe chamada CreditCardAccount em um arquivo de cabealho (a implementao da classe fica para o exerccio seguinte). PASSO 1: Abra o Visual Studio .NET e crie um novo projeto Visual C++ Console Application (.NET) com o nome CreditOrganizer. PASSO 2: Selecione o menu Project Add New Item. PASSO 3: Na caixa de dilogo Add New Item, selecione o modelo Header File (.h). No campo Name, digite CreditCardAccount e clique no boto Open (Figura 6.2). O Visual Studio .NET cria um arquivo de cabealho vazio. PASSO 4: Digite o cdigo abaixo no arquivo de cabealho para definir a classe CreditCardAccount:
class CreditCardAccount//+++++++++++++++++++++++++++++++++++++++++++++++++ { public: bool MakePurchase(double amount); void MakeRepayment(double amount); void PrintStatement(); private: long accountNumber; double currentBalance; double creditLimit; };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Toda conta de carto tem um nmero de conta nico, um balano atual e um limite de crdito. A funo-membro MakePurchase lhe permitir fazer uma compra com o carto de crdito. Ela 94

retornar true se a compra for permitida e false se o valor da compra superar o limite do carto de crdito. A funo-membro MakeRepayment retirar do balano atual a quantia a ser paga. PASSO 5: Construa o programa e corrija eventuais erros de compilao.

Figura 6.2: Criando um arquivo de cabealho no Visual C++ .NET.

6.1.2.
6.1.2.1.

IMPLEMENTANDO UMA CLASSE EM UM ARQUIVO-FONTE


EXERCCIO: IMPLEMENTANDO A CLASSE CreditCardAccount NO ARQUIVOFONTE CreditCardAccount.cpp

Neste exerccio, voc implementar a classe CreditCardAccount em um arquivo-fonte. PASSO 1: Abra o projeto CreditOrganizer. PASSO 2: Selecione o menu Project Add New Item. PASSO 3: Na caixa de dilogo Add New Item, selecione o modelo C++ File (.cpp). No campo Name, digite CreditCardAccount.cpp e clique no boto Open (Figura 6.3). O Visual Studio .NET cria um arquivo-fonte vazio. PASSO 4: No incio do CreditCardAccount.cpp, adicione as duas declaraes #include abaixo:
#include "stdafx.h" #include "CreditCardAccount.h"

stdafx.h um arquivo de cabealho que pode incluir outros arquivos de cabealho padres. Voc deve incluir stdafx.h no incio de todo arquivo-fonte no seu projeto. CreditCardAccount.h contm a definio da classe CreditCardAccount. Voc deve incluir este arquivo aqui para que o compilador possa checar a implementao da classe CreditCardAccount. 95

PASSO 5: Adicione as declaraes abaixo para que voc possa usar classes e tipos de dados definidos no namespace System:
#using <mscorlib.dll> using namespace System;

A diretiva de pr-processador <mscorlib.dll> importa o arquivo da linguagem intermediria da Microsoft (MSIL, Microsoft Intermediate Language) mscorlib.dll para que voc possa usar dados gerenciados e construes gerenciadas definidas neste arquivo DLL.

Figura 6.3: Criando um arquivo-fonte no Visual C++. O uso da declarao using namespace System; lhe permite usar classes e tipos de dados definidos no namespace System. Especificamente, voc usar a classe Console para mostrar mensagens no console. PASSO 6: Implemente a funo-membro CreditCardAccount::MakePurchase com o cdigo abaixo:
bool CreditCardAccount::MakePurchase(double amount)//+++++++++++++++++++++ { if(currentBalance + amount > creditLimit) { return false; } else { currentBalance += amount; return true; } }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Esta funo chamada quando o proprietrio do carto tentar fazer uma compra. O parmetro amount indica o valor da compra. A funo verifica se a compra mais o dbito atual no carto excedem o membro de dados creditLimit e retorna false neste caso. Caso contrrio, a funo adiciona amount ao membro de dados currentBalance e retorna true. 96

NOTA: As funes-membros tm acesso irrestrito a todos os membros na classe, incluindo membros private. PASSO 7: Implemente a funo-membro CreditCardAccount::MakeRepayment com o cdigo abaixo:
void CreditCardAccount::MakeRepayment(double amount)//++++++++++++++++++++ { currentBalance -= amount; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Esta funo permite que o usurio pague a quantia amount ao gerenciador do carto. O valor pago subtrado do currentBalance. PASSO 8: Implemente a funo-membro CreditCardAccount::PrintStatement com o cdigo abaixo:
void CreditCardAccount::PrintStatement()//++++++++++++++++++++++++++++++++ { Console::Write(S"Nmero da conta: "); Console::WriteLine(accountNumber); Console::Write(S"Balano atual: "); Console::WriteLine(currentBalance); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Esta funo mostra informaes sobre o estado atual da conta. PASSO 9: Construa o programa e corrija eventuais erros de compilao.

6.2.

CRIANDO E DESTRUINDO OBJETOS

Tendo definido e implementado uma classe, voc est pronto para aprender a criar e destruir objetos. O modo como voc cria e destri um objeto no Microsoft Visual C++ depende da classe ser ou no gerenciada. Neste captulo, j falamos, consideraremos apenas classes no gerenciadas as classes gerenciadas so discutidas com mais detalhes no captulo 7. O cdigo abaixo mostra como criar um objeto, chamar suas funes-membros public e deletar o objeto quando ele no for mais necessrio:
CreditCardAccount *myAccount; //Declara um ponteiro myAccount = new CreditCardAccount; //Cria um novo objeto CreditCardAccount myAccount->MakePurchase(100); //Usa o operador -> para invocar //funes-membros. myAccount->MakeRepayment(70); myAccount->PrintStatement(); //... delete myAccount; //Deleta explicitamente o objeto quando no mais necessrio

O operador new cria um novo objeto da classe CreditCardAccount e retorna um ponteiro para este objeto. O ponteiro usado com o operador -> para invocar vrias funes-membros no novo objeto. Quando o objeto no mais necessrio, voc pode destru-lo explicitamente usando o operador delete. NOTA: Se voc esquecer de deletar um objeto de uma classe no gerenciada, o coletor de lixo no o ajuda. O objeto permanece alocado na memria, o que constitui um escape de memria. Este um dos problemas mais comuns nos aplicativos C++ tradicionais. Outro erro comum deletar um objeto muito cedo. Se voc tentar usar o objeto aps a destruio, seu programa causar uma exceo em tempo de execuo. 97

6.2.1.

EXERCCIO: CRIANDO E DELETANDO UM OBJETO CreditCardAccount

Neste exerccio, voc criar um novo objeto CreditCardAccount, invocar suas funesmembros e deletar o objeto quando ele no for mais necessrio. PASSO 1: Abra o projeto CreditOrganizer. PASSO 2: Se o Solution Explorer no estiver visvel, selecione o menu View Solution Explorer. PASSO 3: Na janela do Solution Explorer, d um clique duplo no arquivo-fonte CreditOrganizer.cpp para visualiz-lo no editor. PASSO 4: Aps a linha #include stdafx.h, adicione a diretiva #include abaixo:
#include "CreditCardAccount.h"

Este linha lhe permite criar e usar objetos CreditCardAccount no arquivo-fonte CreditOrganizer.cpp. PASSO 5: Adicione o cdigo abaixo na funo _tmain:
//FUNO PRINCIPAL******************************************************** int _tmain() { CreditCardAccount *myAccount; //Declara um ponteiro myAccount = new CreditCardAccount; //Cria um novo objeto CreditCardAccount myAccount->MakePurchase(1000); //Usa o operador -> para invocar //funes-membros. myAccount->MakeRepayment(700); myAccount->PrintStatement(); //Deleta explicitamente o objeto quando no mais necessrio delete myAccount; return 0; }//***********************************************************************

PASSO 6: Construa o programa e corrija os eventuais erros de compilao. PASSO 7: Execute o programa. O programa cria um objeto CreditCardAccount, faz uma compra e um pagamento de dbitos do carto, e imprime uma declarao. Porm, a declarao mostra nmeros aparentemente aleatrios para o nmero da conta e o balano atual, como vemos na Figura 6.4.

Figura 6.4: Sada atual do programa CreditOrganizer. A razo para este resultado que o objeto CreditCardAccount no foi inicializado quando criado. Os membros de dados no objeto tomam quaisquer valores que estiverem na memria onde o objeto foi localizado. 98

Para resolver este problema, voc pode definir um construtor na classe CreditCardAccount. O construtor ir inicializar novos objetos quando eles forem criados. Voc tambm pode definir um destrutor na classe para preparar os objetos antes que eles sejam destrudos.

6.3.

DEFININDO CONSTRUTORES E DESTRUTORES

Nesta seo, voc ver como definir as funes construtor e destrutor para uma classe. Vamos comear pelo construtor.

6.3.1.

DEFININDO CONSTRUTORES

Um construtor uma funo-membro especial que chamada automaticamente quando um objeto criado. O propsito do construtor inicializar o objeto para traz-lo para um estado operacional. Voc declara o prottipo para o construtor na definio da classe. O exemplo abaixo declara um construtor simples para a classe CreditCardAccount:
class CreditCardAccount//+++++++++++++++++++++++++++++++++++++++++++++++++ { public: CreditCardAccount(); //... Outros membros };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Aqui h vrios pontos importantes a serem destacados. Primeiro, um construtor deve ter o mesmo nome da classe. deste modo que o compilador o reconhece como um construtor. Segundo, um construtor no pode especificar um tipo de retorno, nem mesmo void. Se voc especificar um tipo de retorno para um construtor, obter um erro de compilao. Voc pode implementar o construtor no arquivo-fonte do seguinte modo:
//Construtor da classe CreditCardAccount++++++++++++++++++++++++++++++++++ CreditCardAccount::CreditCardAccount() { accountNumber = 1234; currentBalance = 0; creditLimit = 3000; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Este construtor simples inicializar todo novo objeto CreditCardAccount com os mesmos valores. Uma abordagem mais realstica consiste em definir um construtor que receba parmetros que permitam a inicializao de cada objeto com diferentes valores. NOTA: Voc pode fornecer um nmero qualquer de construtores em uma classe, desde que cada construtor tenha uma diferente lista de parmetros. Este um exemplo de funo sobrecarregada.

6.3.1.1.

EXERCCIO: UM CONSTRUTOR PARA A CLASSE CreditCardAccount

Neste exerccio, voc adicionar um construtor sua classe CreditCardAccount. O construtor receber dois parmetros especificando o nmero da conta e o limite do crdito para a nova conta. O balano atual sempre ser inicializado com 0 para cada nova conta. Assim, no necessrio fornecer um parmetro para este membro de dados. PASSO 1: Abre o projeto CreditOrganizer. PASSO 2: Abra o CreditCardAccount.h e declare um construtor public: 99

CreditCardAccount(long number, double limit);

DICA: Assegure-se sempre de que o construtor seja public. Se voc por engano declar-lo private, no o habilitar para criar objetos CreditCardAccount no seu programa. PASSO 3: Abra o CreditCardAccount.cpp e implemente o construtor com o cdigo abaixo:
//Construtor da classe CreditCardAccount++++++++++++++++++++++++++++++++++ CreditCardAccount::CreditCardAccount(long number, double limit) { accountNumber = number; creditLimit = limit; currentBalance = 0.0; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 4: Em CreditOrganizer.cpp, modifique a declarao que cria o objeto CreditCardAccount, usando o cdigo abaixo:
myAccount = new CreditCardAccount(12345, 2500);

Esta declarao cria um novo objeto CreditCardAccount e passa os valores 12345 e 2500 para o construtor de CreditCardAccount. O construtor usa estes valores de parmetro para inicializar os membros de dados accountNumber e creditLimit, respectivamente. PASSO 5: Construa o programa e corrija eventuais erros de compilao. PASSO 6: Execute o programa. Agora, o programa mostra informaes significantes para o objeto CreditCardAccount, como vemos na Figura 6.5.

Figura 6.5: Sada do programa CreditOrganizer atual. LISTAS DE INICIALIZAO DE MEMBROS H uma sintaxe alternativa para inicializao de membros de dados em um construtor usando uma lista de inicializao de membros como abaixo:
CreditCardAccount::CreditCardAccount(long number, double limit) :accountNumber(number), creditLimit(limit), currentBalance(0.0) { }

Os dois pontos na segunda linha so seguidos por uma lista de membros de dados separados por vrgula. Para cada membro de dados fornecido, entre parnteses, um valor inicial. Para inicializao simples, no importa se voc usa uma lista de inicializao de membros ou simplesmente inicializa os membros no corpo do construtor. Porm, h situaes onde voc tem que usar uma lista de inicializao de membros. No captulo 8, voc ver um exemplo quando estiver estudando sobre herana.

100

6.3.2.

DEFININDO DESTRUTORES

Um destrutor uma funo-membro especial que chamada automaticamente quando um objeto est para ser destrudo. O propsito do destrutor preparar o objeto. Por exemplo, o destrutor pode liberar a memria adicional alocada pelo objeto, liberando recursos que ele utilizou, fechando conexes com bancos de dados por ele aberto e assim por diante. NOTA: Somente as classes no gerenciadas tm destrutores. Nas classes gerenciadas, o coletor de lixo .NET cuida da limpeza dos objetos no mais utilizados: voc no toma parte na destruio dos objetos. Portanto, no precisa fornecer um destrutor. Declare o prottipo para o destrutor na definio da classe. O exemplo abaixo declara o destrutor para a classe CreditCardAccount:
class CreditCardAccount//+++++++++++++++++++++++++++++++++++++++++++++++++ { public: ~CreditCardAccount(); //... Outros membros. };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

O destrutor comea com um til (~) e tem o mesmo nome da classe. Ele no tem tipo de retorno e no pode receber parmetros. Isto implica que voc s pode ter um destrutor em uma classe. Voc pode implementar o destrutor no arquivo-fonte com o cdigo abaixo:
//Destrutor da classe CreditCardAccount+++++++++++++++++++++++++++++++++++ CreditCardAccount::~CreditCardAccount() { Console::Write(S"\nNmero da conta que sendo destruda: "); Console::WriteLine(accountNumber); Console::Write(S"Dbito no fechamento: "); Console::WriteLine(currentBalance); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Este destrutor simples mostra informaes finais sobre um objeto CreditCardAccount um pouco antes da sua destruio.

6.3.2.1.

EXERCCIO: UM DESTRUTOR PARA A CLASSE CreditCardAccount

Neste exerccio, voc adicionar um destrutor sua classe CreditCardAccount. O destrutor ir mostrar uma mensagem de status descrevendo o objeto que estiver sendo destrudo. PASSO 1: Abra o projeto CreditOrganizer. PASSO 2: Em CreditCardAccount.h, declare um destrutor public como indicado abaixo:
class CreditCardAccount//+++++++++++++++++++++++++++++++++++++++++++++++++ { public: ~CreditCardAccount(); //... Outros membros. };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

DICA: Como no caso do construtor assegure-se de que declarou um destrutor public. PASSO 3: Em CreditCardAccount.cpp, implemente o destrutor com o cdigo abaixo:
//Destrutor da classe CreditCardAccount+++++++++++++++++++++++++++++++++++ CreditCardAccount::~CreditCardAccount() { Console::Write(S"Nmero da conta a ser destruda: ");

101

Console::WriteLine(accountNumber); Console::Write(S"Dbito no fechamento: "); Console::WriteLine(currentBalance); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 4: Construa o programa e corrija eventuais erros de compilao. PASSO 5: Execute o programa. Como antes, o programa cria um objeto CreditCardAccount, invoca suas funes-membros e, ento, deleta o objeto. Quando o objeto CreditCardAccount deletado, o destrutor chamado implicitamente na declarao que contm o operador delete para mostrar o status final do objeto (Figura 6.6).

Figura 6.6: Sada do programa CreditOrganizer atual.

6.4.

DEFININDO MEMBROS static DE UMA CLASSE

Os membros de dados e as funes-membros atualmente definidas na classe CreditCardAccount so membros de instncia. Cada instncia de CreditCardAccount tem seus accountNumber, currentBalance e creditLimit particulares. Do mesmo modo, quando invoca as funesmembros MakePurchase, MakeRepayment e PrintStatement, voc deve especificar que instncia de CreditCardAccount voc est usando, como mostra a Figura 6.7.

Figura 6.7: Duas instncias de CreditCardAccount. 102

O C++ tambm lhe permite definir membros abrangentes de uma classe, i.e., membros pertencentes classe inteira e no apenas a uma instncia especfica. Por exemplo, voc pode definir um membro de dados abrangente chamado numberOfAccounts para contar quantas instncias de CreditCardAccount foram criadas. Similarmente, voc pode fornecer uma funo-membro abrangente da classe chamada GetNumberOfAccounts para receber esta contagem, como ilustrado na Figura 6.8.

Figura 6.8: Membros abrangentes da classe CreditCardAccount. Vamos ver como definir membros de dados e funes-membros abrangentes de uma classe.

6.4.1.

DEFININDO MEMBROS DE DADOS static

Para definir um membro de dados abrangente de uma classe, use a palavra-chave static como abaixo:
class CreditCardAccount//+++++++++++++++++++++++++++++++++++++++++++++++++ { private: static int numberOfAccounts;//Declara membro de dados abrangente //... Outros membros };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Esta declarao diz ao compilador que haver um membro de dados abrangente chamado numberOfAccounts. Porm, a declarao no aloca qualquer armazenamento para numberOfAccounts. Voc mesmo deve fazer isto adicionando a seguinte declarao no arquivofonte CreditCardAccount.cpp:
//MEMBROS DE DADOS ABRANGENTES******************************************** int CreditCardAccount::numberOfAccounts = 0; //************************************************************************

Esta declarao aloca armazenamento permanente para a varivel numberOfAccounts. Esta varivel estar disponvel por toda a vida do programa, mesmo antes que quaisquer objetos CreditCardAccount tenham sido criados.

103

NOTA: Se voc no inicializar explicitamente numberOfAccounts, o valor padro inicial 0. Se esquecer de definir completamente numberOfAccounts, voc obter um erro de vinculao quando tentar construir o programa. O erro de vinculao lhe informar que numberOfAccounts no foi definido.

6.4.1.1.

EXERCCIO: ADICIONANDO UM MEMBRO DE DADOS static CLASSE CreditCardAccount

Neste exerccio, voc adicionar um membro de dados static chamado numberOfAccounts classe CreditCardAccount. Voc incrementar este membro de dados toda vez que um novo objeto CreditCardAccount for criado. PASSO 1: Abra o projeto CreditOrganizer. PASSO 2: Em CreditCardAccount.h, numberOfAccounts como abaixo: declare o membro de dados static

class CreditCardAccount//+++++++++++++++++++++++++++++++++++++++++++++++++ { private: static int numberOfAccounts;//Declara membro de dados abrangente //... Outros membros };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 3: Em CreditCardAccount.cpp, defina o membro de dados numberOfAccounts como abaixo:


int CreditCardAccount::numberOfAccounts = 0;

NOTA: Adicione esta declarao aps todas as diretivas #include mas fora de qualquer corpo de funo. PASSO 4: Modifique o construtor CreditCardAccount de modo que ele incremente numberOfAccounts sempre que um novo objeto CreditCardAccount for criado.
CreditCardAccount::CreditCardAccount(long number, double limit) { accountNumber = number; creditLimit = limit; currentBalance = 0.0; numberOfAccounts++; Console::Write(S"Nmero atual de contas: "); Console::WriteLine(numberOfAccounts); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 5: Em CreditOrganizer.cpp, modifique a funo _tmain de modo que ela crie e use vrios objetos CreditCardAccount.
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Criando o 1 objeto"); CreditCardAccount *account1; account1 = new CreditCardAccount(12345, 2000); account1->MakePurchase(300); account1->PrintStatement(); Console::WriteLine(S"\nCriando o 2 objeto"); CreditCardAccount *account2; account2 = new CreditCardAccount(67890, 5000);

104

account2->MakePurchase(750); account2->PrintStatement(); Console::WriteLine(S"\nDestruindo os objetos"); delete account1; delete account2; return 0; }//***********************************************************************

PASSO 6: Construa o programa e corrija eventuais erros de compilao. PASSO 7: Execute o programa. Toda vez que um novo objeto CreditCardAccount for criado, o programa incrementa numberOfAccounts e exibe o seu valor atualizado como na Figura 6.9.

Figura 6.9: Sada do programa CreditOrganizer atual.

6.4.2.

DEFININDO FUNES-MEMBROS static

Para definir uma funo-membro static, use esta palavra-chave na declarao da funo:
class CreditCardAccount//+++++++++++++++++++++++++++++++++++++++++++++++++ { public: static int GetNumberOfAccounts(); //...Outros membros. };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Implemente a funo no arquivo-fonte com o cdigo abaixo. Note que voc no usa a palavra-chave static na implementao, mas somente na declarao dentro da definio da classe.
int CreditCardAccount::GetNumberOfAccounts()//++++++++++++++++++++++++++++ { return numberOfAccounts; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

NOTA: Uma funo-membro static s pode acessar membros de classe static. Por exemplo, GetNumberOfAccounts pode acessar numberOfAccounts, mas no pode acessar accountNumber, currentBalance ou creditLimit. Para chamar uma funo-membro static, use o nome da classe ao invs de uma instncia particular, como no exemplo abaixo: 105

int n = CreditCardAccount::GetNumberOfAccounts();

NOTA: Voc j viu a sintaxe Nome da Classe::Nome da Funo antes. Toda vez que exibe uma mensagem no console, voc usa uma declarao do tipo Console::WriteLine(SHello world). Esta declarao chama a funo-membro static WriteLine na classe Console.

6.4.2.1.

EXERCCIO: FUNO-MEMBRO static NA CLASSE CreditCardAccount

Neste exerccio, voc definir uma funo-membro static GetNumberOfAccounts na classe CreditCardAccount. Ento, voc chamar esta funo algumas vezes em _tmain. PASSO 1: Abra o projeto CreditOrganizer. PASSO 2: Em CreditCardAccount.h, declare a funo GetNumberOfAccounts como abaixo:
class CreditCardAccount//+++++++++++++++++++++++++++++++++++++++++++++++++ { public: static int GetNumberOfAccounts(); //...Outros membros. };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 3: Em CreditCardAccount.cpp, implemente a funo GetNumberOfAccounts como abaixo:


int CreditCardAccount::GetNumberOfAccounts()//++++++++++++++++++++++++++++ { return numberOfAccounts; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 4: Em CreditOrganizer.cpp, modifique a funo _tmain de modo que ela chame GetNumberOfAccounts em vrios momentos da execuo.
//FUNO PRINCIPAL******************************************************** int _tmain() { int n = CreditCardAccount::GetNumberOfAccounts(); Console::Write(S"N inicial de contas: "); Console::WriteLine(n); Console::WriteLine(S"Criando o 1 objeto"); CreditCardAccount *account1; account1 = new CreditCardAccount(12345, 2000); account1->MakePurchase(300); account1->PrintStatement(); Console::WriteLine(S"\nCriando o 2 objeto"); CreditCardAccount *account2; account2 = new CreditCardAccount(67890, 5000); account2->MakePurchase(750); account2->PrintStatement(); n = CreditCardAccount::GetNumberOfAccounts(); Console::Write(S"\nN atual de contas: "); Console::WriteLine(n); Console::WriteLine(S"\nDestruindo os objetos"); delete account1; delete account2; return 0;

106

}//***********************************************************************

PASSO 5: Construa o programa e corrija eventuais erros de compilao. PASSO 6: Execute o programa. A Figura 6.10 mostra a sada atual do CreditOrganizer.

Figura 6.10: Sada do programa CreditOrganizer atual.

6.5.

DEFININDO RELAES ENTRE OBJETOS

No restante deste captulo, voc ver como definir relaes entre objetos em um aplicativo do Visual C++. Os aplicativos do Visual C++ tipicamente contm muitos objetos. Estes objetos se comunicam entre si para alcanar a funcionalidade global necessria no aplicativo. Para ilustrar as relaes entre os objetos, voc adicionar uma nova classe chamada LoyaltyScheme ao seu aplicativo CreditOrganizer. A classe LoyaltyScheme permitir que o proprietrio do carto de crdito ganhe pontos de bnus quando usa seu carto. Os pontos de bnus funcionam como uma recompensa para o usurio leal do carto de crdito. Quando um objeto CreditCardAccount criado, ele ainda no tem um objeto LoyaltyScheme. O objeto LoyaltyScheme ser criado quando o usurio atingir 50% do seu limite de crdito. Subseqentemente, cada R$ 10 gasto com o carto de crdito acumular um ponto de bnus no objeto LoyaltyScheme, contanto que a conta esteja acima de 50% do limite de crdito. Quando o objeto CreditCardAccount for finalmente destrudo, o objeto LoyaltyScheme tambm dever s-lo. A Figura 6.11 mostra os tempos de vida dos objetos CreditCardAccount e LoyaltyScheme. Para alcanar esta funcionalidade, voc completar os exerccios seguintes: Definir a classe LoyaltyScheme; Implementar a classe LoyaltyScheme; Criar, usar e destruir objetos LoyaltyScheme; Testar o aplicativo.

107

Figura 6.11: Tempos de vida dos objetos CreditCardAccount e LoyaltyScheme.

6.5.1.
6.5.1.1.

DEFININDO A CLASSE LoyaltyScheme


EXERCCIO: DEFININDO A CLASSE LoyaltyScheme EM UM NOVO ARQUIVO DE CABEALHO

Neste exerccio, voc definir a classe LoyaltyScheme em um novo arquivo de cabealho chamado LoyaltyScheme.h. PASSO 1: Abra o projeto CreditOrganizer. PASSO 2: Selecione o menu Project Add New Item. PASSO 3: Na caixa de dilogo Add New Item, selecione o modelo Header File (.h). No campo Name, digite LoyaltyScheme e clique Open. PASSO 4: Digite o cdigo abaixo no arquivo de cabealho para definir a classe LoyaltyScheme:
//DEFINIO DA CLASSE LoyaltyScheme*************************************** class LoyaltyScheme { public: LoyaltyScheme(); //Construtor ~LoyaltyScheme(); //Destrutor void EarnPointsOnAmount(double amountSpent);//Ganha 1 ponto/R$10 gasto void RedeemPoints(int points); //Resgata pontos int GetPoints(); //Retorna o valor de totalPoints private: int totalPoints; //Total atualizado de pontos };//**********************************************************************

108

PASSO 5: Construa o programa e corrija eventuais erros de compilao.

6.5.2.
6.5.2.1.

IMPLEMENTANDO A CLASSE LoyaltyScheme


EXERCCIO: IMPLEMENTANDO A CLASSE LoyaltyScheme EM UM NOVO ARQUIVO DE CABEALHO

PASSO 1: Abra o projeto CreditOrganizer. PASSO 2: Selecione o menu Project Add New Item. PASSO 3: Na caixa de dilogo Add New Item, selecione o modelo C++ File (.cpp). No campo Name, digite LoyaltyScheme.cpp e clique em Open. O Visual Studio .NET cria um arquivo-fonte vazio. PASSO 4: Adicione as declaraes #include e de exposio do namespace System no incio do arquivo, como mostrado abaixo:
//BIBLIOTECAS E ARQUIVOS DE CABEALHO************************************* #include "stdafx.h" #include "LoyaltyScheme.h" #using <mscorlib.dll> using namespace System; //************************************************************************

PASSO 5: Implemente os cdigos do construtor e do destrutor da classe LoyaltyScheme como abaixo:


LoyaltyScheme::LoyaltyScheme()//++++++++++++++++++++++++++++++++++++++++++ { Console::WriteLine(S"Parabns, voc agora se qualificou" S" para obter pontos de bnus!"); totalPoints = 0; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ LoyaltyScheme::~LoyaltyScheme(void)//+++++++++++++++++++++++++++++++++++++ { Console::WriteLine(S"O esquema de lealdade finaliza agora"); }//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 6: Implemente a funo-membro EarnPointsOnAmount como abaixo:


void LoyaltyScheme::EarnPointsOnAmount(double amountSpent)//++++++++++++++ { int points = (int)(amountSpent / 10); totalPoints += points; Console::Write(S"Novos pontos de bnus ganhos: "); Console::WriteLine(points); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

A sintaxe (int)(amountSpent / 10) divide a quantidade gasta por 10 e converte o valor resultante para um tipo de dados int. PASSO 7: Implemente a funo-membro RedeemPoints com o cdigo abaixo:
void LoyaltyScheme::RedeemPoints(int points)//++++++++++++++++++++++++++++ { if(points <= totalPoints) { totalPoints -= points;

109

} else { totalPoints = 0; } }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 8: Implemente a funo-membro GetPoints como abaixo:


int LoyaltyScheme::GetPoints()//++++++++++++++++++++++++++++++++++++++++++ { return totalPoints; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 9: Construa o programa e corrija eventuais erros de compilao.

6.5.3.
6.5.3.1.

CRIANDO, USANDO E DESTRUINDO OBJETOS LoyaltyScheme


EXERCCIO: ESTENDENDO A CLASSE CreditCardAccount PARA SUPORTAR A FUNCIONALIDADE DO ESQUEMA DE LEALDADE

Neste exerccio, voc estender a classe CreditCardAccount para suportar a funcionalidade do esquema de lealdade. PASSO 1: Abra o projeto CreditOrganizer. PASSO 2: No incio do arquivo CreditCardAccount.h, adicione a diretiva #include abaixo:
#include "LoyaltyScheme.h"

Isto lhe permitir usar a classe LoyaltyScheme neste arquivo de cabealho. PASSO 3: Adicione o membro de dados private abaixo classe CreditCardAccount:
LoyaltyScheme *ptrLoyaltyScheme;//Ponteiro para um objeto LoyaltyScheme

Este ponteiro define uma associao entre um objeto CreditCardAccount e um objeto LoyaltyScheme. PASSO 4: Adicione a funo-membro public abaixo classe CreditCardAccount:
void RedeemLoyaltyPoints();

Esta funo atua como um envoltrio para a funo RedeemPoints na classe LoyaltyScheme. Quando quiser resgatar pontos de lealdade, voc chama RedeemLoyaltyPoints no seu objeto CreditCardAccount. Esta funo chamar RedeemPoints no objeto LoyaltyScheme subjacente para realizar a tarefa. NOTA: A confiana em uma existncia de funcionalidade do membro um exemplo de delegao. O objeto CreditCardAccount delega a operao RedeemPoints ao objeto LoyaltyScheme. PASSO 5: Em CreditCardAccount.cpp, localize o construtor CreditCardAccount e lhe adicione a seguinte declarao:
ptrLoyaltyScheme = 0;

Esta declarao fixa inicialmente o ponteiro ptrLoyaltyScheme para 0. Zero um valor especial para um ponteiro porque ele indica que o ponteiro ainda no aponta para um objeto real (O objeto LoyaltyScheme no pode ser criado at que o balano do carto de crdito alcance 50% do limite de crdito). 110

PASSO 6: Modifique a funo MakePurchase como indicado abaixo para contabilizar pontos de bnus quando o balano do carto de crdito alcanar 50% do limite de crdito:
bool CreditCardAccount::MakePurchase(double amount)//+++++++++++++++++++++ { if(currentBalance + amount > creditLimit) { return false; } else { currentBalance += amount; //Se o balano atual for 50% (ou mais) do limite de crdito... if(currentBalance >= creditLimit / 2) { //Se o objeto LoyaltyScheme ainda no existir... if(ptrLoyaltyScheme == 0) { //Cri-lo ptrLoyaltyScheme = new LoyaltyScheme(); } else { //LoyaltyScheme j existe. //Portanto, contabilizar os pontos de bnus ptrLoyaltyScheme->EarnPointsOnAmount(amount); } } return true; } }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 7: Implemente a funo RedeemLoyaltyPoints como abaixo. RedeemLoyaltyPoints uma nova funo-membro que permite ao usurio resgatar alguns ou todos os pontos de lealdade no objeto LoyaltyScheme associado.
void CreditCardAccount::RedeemLoyaltyPoints()//+++++++++++++++++++++++++++ { //Se o objeto LoyaltyScheme ainda no foi criado... if(ptrLoyaltyScheme == 0) { //Mostrar uma mensagem de erro Console::WriteLine(S"Sinto muito! Voc ainda no tem " S"um esquema de lealdade."); } else { //Informa ao usurio quantos pontos esto atualmente disponveis Console::Write(S"N de pontos disponveis: "); Console::Write(ptrLoyaltyScheme->GetPoints()); //Pergunta ao usurio quantos pontos ele quer resgatar Console::Write(S". Quantos pontos voc quer resgatar? "); String *input = Console::ReadLine(); int points = input->ToInt32(0); //Resgata os pontos ptrLoyaltyScheme->RedeemPoints(points); //Informa ao usurio quantos pontos restaram Console::Write(S"Pontos restantes: "); Console::WriteLine(ptrLoyaltyScheme->GetPoints()); } }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

111

NOTA: importante testar o ponteiro ptrLoyaltyScheme antes de us-lo. Se voc esqueceu de testar o ponteiro e ele ainda for 0, seu programa provocar uma exceo de ponteiro null em tempo de execuo. Este um tipo de erro muito comum em aplicativos C++. PASSO 8: Adicione a seguinte declarao ao destrutor CreditCardAccount:
delete ptrLoyaltyScheme;

Esta declarao deleta o objeto LoyaltyScheme porque ele no mais necessrio. NOTA: Quando usar delete, voc no precisa testar se um ponteiro null. O operador delete tem, internamente, um teste para ponteiro null. PASSO 9: Construa o programa e corrija eventuais erros de compilao.

6.5.4.
6.5.4.1.

TESTANDO O APLICATIVO
EXERCCIO: TESTANDO A FUNCIONALIDADE DO ESQUEMA DE LEALDADE

Neste exerccio, voc modificar o cdigo em CreditOrganizer.cpp para testar a funcionalidade do esquema de lealdade. PASSO 1: Abra o projeto CreditOrganizer. PASSO 2: Em CreditOrganizer.cpp, modifique a funo __main como indicado abaixo:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Criando o objeto da conta"); CreditCardAccount *account1; account1 = new CreditCardAccount(12345, 2000); Console::WriteLine(S"\nFazendo uma compra de R$ 300"); account1->MakePurchase(300); Console::WriteLine(S"\nFazendo outra compra de R$ 700"); account1->MakePurchase(700); Console::WriteLine(S"\nFazendo mais uma compra de R$ 500"); account1->MakePurchase(500); Console::WriteLine(S"\nResgatando pontos"); account1->RedeemLoyaltyPoints(); Console::WriteLine(S"\nDestruindo o objeto da conta"); delete account1; return 0; }//***********************************************************************

PASSO 3: Construa o programa e corrija eventuais erros de compilao. PASSO 4: Execute o programa. Ele cria um objeto CreditCardAccount e faz algumas compras. Quando o balano do carto de crdito atinge R$ 1000, um objeto LoyaltyScheme criado. As compras subseqentes produzem um ponto de lealdade para cada R$ 10 gasto. Quando voc tentar resgatar os pontos de lealdade, o programa lhe informa quantos pontos esto disponveis e lhe pergunta quantos voc quer resgatar. Entre com um valor, por exemplo, 36. O programa lhe informa quantos pontos restaram. 112

No final do programa, o objeto CreditCardAccount destrudo. O objeto associado LoyaltyScheme destrudo ao mesmo tempo. A Figura 6.12 mostra as mensagens que aparecem no console durante a execuo do programa.

Figura 6.12: Sada da verso final do CreditOrganizer.

6.6.

RPIDAS REFERNCIAS SOBRE O CAPTULO 6

A Tabela 6.1 mostra um resumo deste captulo.

113

Tabela 6.1: Resumo do captulo 6.


PARA Definir uma classe FAA ISTO Adicione um arquivo de cabealho ao projeto. Defina a classe neste arquivo. Ex.: class MyClass { public: void MyFunction(); seu

Implementar uma classe

private: int myData; }; Adicione um arquivo-fonte ao seu projeto. No arquivo-fonte, use uma declarao #include para o arquivo de cabealho que contm a definio da classe. Ento, implemente as funes-membros no arquivo-fonte. Ex.: #include "MyHeader.h" void MyClass::MyFunction() { myData = myData * 2; } Declare o construtor no arquivo de cabealho O e o implemente no arquivo-fonte. construtor deve ter o mesmo nome da classe e no retorna valor. Porm, um construtor pode receber parmetros. Ex.: //Arquivo de cabealho class MyClass { public: MyClass(int n); //... }; //Arquivo-fonte MyClass::MyClass(int n) { myData = n; } Declare o destrutor no arquivo de cabealho e o implemente no arquivo-fonte. O destrutor deve ter o mesmo nome da classe, precedido pelo til (~). O destrutor no pode retornar um valor ou receber quaisquer parmetros. Ex.: //Arquivo de cabealho class MyClass { public: ~MyClass(); //... }; //Arquivo-fonte MyClass::~MyClass() { Console::WriteLine(S"Adeus");

Colocar um construtor em uma classe

Colocar um destrutor em uma classe

114

Criar e destruir objetos de uma classe no gerenciada

Definir membros de dados static

} Crie um objeto usando a palavra-chave new, passando parmetros para o construtor se necessrio. Delete o objeto usando a palavra-chave delete. Ex.: myClass *ptrToObject; ptrToObject = new myClass(100); ptrToObject->MyFunction(); delete ptrToObject; Declare o membro de dados usando a palavrachave static. Defina o membro de dados no arquivo-fonte. Ex.: //Arquivo de cabealho class MyClass { private: static int myClassData; //... };

//Arquivo-fonte int myClass::myClassData = 0; Definir e usar funes-membros static Declare a funo-membro usando a palavrachave static. Implemente a funo-membro no arquivo-fonte. Chame a funo usando a sintaxe ClassName::FunctionName. Ex.: //Arquivo de cabealho class MyClass { public: static void MyClassFunction(); //... }; //Arquivo-fonte void MyClass::MyClassFunction() { myClassData++; } //Cdigo cliente MyClass::MyClassFunction(); Defina todas as classes requeridas e use ponteiros para denotar relaes entre objetos. Por exemplo, se uma instncia de classe A precisa apontar para uma instncia de classe B, use o seguinte: class B { //... }; class A { //... private: B *pointerToB; };

Definir relaes entre objetos

115

7.

CAPTULO 7: CONTROLANDO OS TEMPOS DE VIDA DOS OBJETOS

Neste captulo, voc aprender como diferir o modo de gerenciamento de memria do Microsoft .NET do modo tradicional C++; fornecer finalizadores para suas classes; implementar um mtodo Dispose para suas classes.

Agora que voc j sabe como criar objetos em C++ usando o operador new, ir aprender como os tempos de vida do objeto so controlados no C++ gerenciado e ver como a abordagem de gerenciamento difere da tradicional new e delete.

7.1.

O GERENCIAMENTO DE MEMRIA TRADICIONAL C++

Voc j viu como criar e deletar objetos dinamicamente em C++ usando os operadores new e delete, mas vamos rever como o sistema funciona.

7.1.1.

CRIANDO OBJETOS

Os objetos so criados dinamicamente usando o operador new, que realiza as seguintes tarefas: Aloca memria para o objeto; Chama um construtor para inicializar o objeto; Retorna um ponteiro para o objeto.

O fragmento de cdigo abaixo mostra como criar um objeto pertencente classe Account:
//Cria um objeto para representar a conta n 1234567 Account *pa = new Account(1234567);

Os objetos criados dinamicamente tm sua memria alocada da heap o agrupamento de memria livre alocado para o processo enquanto as variveis locais tm sua memria alocada na stack do programa. Um objeto criado dinamicamente na verdade consiste de duas partes: o objeto e o ponteiro que voc usa para acess-lo.

7.1.2.

DELETANDO OBJETOS

As variveis locais so criadas na stack e sero destrudas quando sarem do escopo. Na verdade, as variveis locais so tambm conhecidas como variveis automticas, um nome que reflete o fato de que elas so automaticamente criadas e destrudas quando necessrio. Em contraste, o C++ no gerenciado requer que voc gerencie o tempo de vida dos seus objetos criados dinamicamente. Considere o fragmento de cdigo abaixo:
void SomeFunction() { //Declara um inteiro para representar o n da conta long num = 1234567; //Cria um objeto para representar o n Account 1234567 Account *pa = new Account(num); }

116

As duas variveis saem do escopo aps a chave fechando e sero destrudas: o long num e o ponteiro pa. O atual objeto Account no automaticamente destrudo e existir at ser deletado ou quando o programa terminar. Voc destri objetos usando o operador delete, como abaixo:
//Cria um objeto para representar Account n 1234567 Account *pa = new Account(num); //Usa o objeto Account //Destri o objeto Account delete pa;

O operador delete recebe um ponteiro para o objeto e realiza duas operaes: Executa o destrutor da classe de modo que voc pode preparar o estado do objeto para destru-lo; Repe a memria que o objeto ocupava para o sistema operacional.

7.1.3.

VANTAGENS E DESVANTAGENS DA ALOCAO MANUAL DE MEMRIA

H vantagens e desvantagens na alocao manual de memria. A principal algumas pessoas chegam a afirmar que nica vantagem da alocao manual de memria que voc tem um controle bastante preciso sobre quando um objeto destrudo. Assim, voc no usa memria mais do que o tempo necessrio. Uma segunda vantagem o uso de destrutores: quando voc destri um objeto, o cdigo automaticamente executado para preparar o objeto. Este processo assegura que os objetos sempre so apropriadamente preparados sem interveno do programador. H duas desvantagens decorrentes da chamada manual ao operador delete para liberar objetos no mais necessrios: cham-lo muito tarde (ou no) e cham-lo cedo demais. Negligenciar a chamada a delete usualmente no um descuido fatal para seu programa, mas isto pode levar a conseqncias indesejveis. A mais comum que seu programa se conservar na memria mais tempo do que o necessrio, uma condio conhecida como vazamento de memria. Em alguns casos, o vazamento de memria pode ser fatal. Considere o fragmento de cdigo abaixo:
void lineDraw(int x1, int y1, int x2, int y2) { //Cria uma caneta para desenhar a linha... Pen *p = new Pen(Black, OnePixelWide, Dashed); //Chama alguma funo da biblioteca de grficos para desenhar a linha drawTheLine(p, x1, y1, x2, y2); //Esquece de deletar a caneta //delete p; }

Suponha que cada objeto Pen tem 10 bytes e que a funo lineDraw seja chamada 10000 vezes. Se o programador esquece de deletar o objeto Pen, esta rotina ir vazar 100000 bytes de memria. Nos tempos da verso 3 do Microsoft Windows, os computadores no dispunham de muita memria e um programa mal comportado poderia consumir toda a memria livre travar o sistema. A destruio prematura de um objeto ou no momento errado uma questo muito mais sria. Se um objeto for deletado antes do tempo, algum pode tentar us-lo posteriormente. Isto usualmente 117

resultaria numa falha do programa. Porm, s vezes difcil saber a hora certa de liberar um objeto. Eis um exemplo:
//Cria um objeto SomeObject *pObj = new SomeObject(); //Passa o objeto para um funo aFunction(pObj); //Usa o objeto pObj->doSomething(); //BANG! //... void aFunction(SomeObject *pp) { //Usa pp pp->doThis(); //Deleta o objeto - ou no? delete pp; }

O cdigo cria um objeto e, ento, passa o ponteiro para uma funo. A funo usa o objeto atravs do ponteiro, acha que o objeto no mais necessrio e chama delete. Quando a funo retorna, o cdigo de chamada tenta usar o objeto com resultados previsveis. O problema decidir quem tem a responsabilidade de destruir o objeto em outras palavras, quem possui o objeto. Esta propriedade pode ser surpreendentemente difcil de determinar em projetos grandes ou complexos.

7.2.

A ABORDAGEM .NET

Do mesmo modo que a Sun Microsystems fez com Java, a Microsoft decidiu que as desvantagens da alocao manual de memria superavam em muito as vantagens. Ento, o C++ gerenciado usa gerenciamento automtico de memria, assim como todas as linguagens .NET. Usando o mecanismo .NET, voc ainda criar objetos dinamicamente usando o operador new, mas o sistema responsvel pela destruio dos objetos, no voc. O sistema mantm rastros de referncias aos objetos e, quando um objeto j no estiver sendo referenciado por ningum, ele se torna um candidato para o coletor de lixo. O gerenciamento automtico de memria tem mais conseqncias para o programador do que voc poderia imaginar em princpio: Os objetos so sempre criados usando new. Cdigos no gerenciados lhe permitem criar objetos na heap ou dinamicamente na stack (como variveis automticas). Para que o coletor de lixo funcione, os objetos tm que ser acessados atravs de algum tipo de referncia, que significa um ponteiro em C++. Os membros de classe so sempre acessados usando ponteiros. Voc no pode determinar quando um objeto vai para o coletor de lixo.

Voc ainda pode usar o operador delete para destruir um objeto manualmente, porque isto uma parte inerente ao modo como o C++ funciona, mas melhor que voc deixe o coletor de lixo manipular esta liberao de memria. A COLETA DE LIXO NO .NET 118

O mecanismo de coleta de lixo no .NET Framework muito sofisticado, mas voc no precisa saber muito sobre ele para usar o C++ gerenciado. Na verdade este mecanismo projetado para funcionar bem sem que voc intervenha em nada. Porm, se voc estiver interessado em saber um pouco mais sobre isto, prossiga a leitura. A memria para objetos alocada da heap gerenciada, o bloco de memria que o processo usa para armazenar objetos alocados dinamicamente. Toda alocao ocupa algum espao da heap e possvel que em algum momento a memria heap ser totalmente preenchida. Em teoria, se isto acontecer, o coletor de lixo ser invocado para verificar se h objetos no referenciados cuja memria pode ser liberada para minimizar a sobrecarga da heap. Na realidade, isto no to simples assim. Todo objeto .NET framework criado dinamicamente pertence a uma gerao. Os objetos so colocados na gerao 0 quando so criados, com geraes de nveis mais alto sendo preenchidas por objetos mais velhos. A diviso de objetos em geraes significa que voc no tem que executar o coletor de lixo em todos os objetos na heap; basta considerar a idade de um objeto particular. A coleta do lixo ocorre quando a gerao 0 estiver cheia. Os objetos mortos so recolhidos, quaisquer objetos sobreviventes coleta so promovidos para a gerao 1 e novos objetos so adicionados gerao 0 novamente. O coletor de lixo melhora a eficincia porque sempre, inicialmente, executa uma coleta na gerao 0. Se no liberar memria suficientemente, o coletor pode deslocar a execuo da coleta para a prxima gerao. At o presente, apenas trs geraes (0, 1 e 2) so suportadas pelo .NET. Usualmente voc deixa o coletor de lixo decidir quando realizar uma coleta, mas voc pode usar o mtodo esttico Collect da classe System::GC para forar a coleta se souber que ter muitos objetos recuperveis no seu cdigo. Collect permite que voc execute uma coleta padro ou especifica uma gerao particular. Se estiver interessado em descobrir a qual gerao pertence o dado objeto, voc pode usar o mtodo System::GC::GetGeneration, passando-lhe uma referncia de objeto.

7.2.1.

FINALIZADORES

Todos os tipos de referncia .NET (classes __gc em C++ gerenciado) herdam um mtodo chamado Finalize da ltima classe-base, System::Object. Finalize permite que um objeto libere recursos e realize outras operaes de limpeza antes que o objeto seja coletado para o lixo. Qualquer classe gerenciada pode sobrecarregar Finalize se precisar realizar estas tarefas. importante notar que Finalize no equivale ao destrutor tradicional C++. Os destrutores so determinsticos, i.e., voc sempre pode dizer quando o destrutor para um objeto vai ser chamado. Pode ser difcil decidir quando um destrutor dever ser chamado em um cdigo complexo com muitos ponteiros e referncias circulando, mas sempre possvel porque o destrutor invocado explicitamente em algum ponto. Por outro lado, os finalizadores no so determinstico, i.e., voc no pode dizer quando o mtodo Finalize ser chamado para um objeto porque isto depende de quando o objeto coletado para o lixo. E embora voc possa forar uma coleta de lixo, o sistema que decide quando faz-la. Isto significa que no deve colocar em Finalize qualquer cdigo que voc sabe que est sendo chamado em um ponto particular, porque no h garantia de que a finalizao acontecer. Assim para que usar Finalize? Voc deve usar este mtodo para liberar quaisquer recursos no gerenciados que seus objetos suportem, tais como manipuladores de arquivo, manipuladores de janela ou conexes de banco de dados. Voc no precisa implementar Finalize para lidar com recursos gerenciados porque o coletor de lixo tomar conta deles. 119

Embora finalizadores no sejam a mesma coisa que destrutores, no C++ gerenciado use a sintaxe normal C++ para escrever um destrutor que o compilador escrever para voc um mtodo Finalize .NET. Assim, um destrutor como este
~MyClass() { Console::WriteLine(S"Finalizando..."); }

expande-se para
MyClass::Finalize() { Console::WriteLine(S"Finalizando..."); MyBaseClass::Finalize(); } virtual ~MyClass() { System::GC::SuppressFinalize(this); MyClass::Finalize(); }

O destrutor chama o mtodo Finalize para a classe, que contm o mesmo cdigo que voc fornece ao destrutor. Ento, Finalize chama o finalizador para sua classe-base, se houver. A chamada a SuppressFinalize impede que o coletor de lixo empreenda qualquer outra ao de finalizao. Se o destrutor for chamado no cdigo, o objeto explicitamente destrudo e voc deseja que o coletor de lixo no tente fazer mais nada. Voc tem que implementar finalizadores usando a sintaxe do destrutor C++. Voc no pode implementar Finalize diretamente para uma classe. Se tentar fazer isto, provocar um erro de compilao. NOTA: Implemente Finalize apenas em classes que precisem deste mtodo porque a recuperao do objeto durante a coleta de lixo gasta mais tempo quando os finalizadores so chamados.

7.2.2.
7.2.2.1.

IMPLEMENTANDO UM FINALIZADOR
EXERCCIO: IMPLEMENTANDO UM FINALIZADOR PARA UMA CLASSE GERENCIADA

O pequeno exerccio abaixo mostra como implementar um finalizador para uma classe gerenciada. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) com o nome TestFinalize. PASSO 2: Em TestFinalize.cpp, adicione a definio de uma classe gerenciada antes da funo _tmain, com o cdigo abaixo:
//DEFINIO DA CLASSE GERENCIADA Tester*********************************** __gc class Tester { public: Tester() { Console::WriteLine(S"Construtor da classe Tester"); } ~Tester() {

120

Console::WriteLine(S"Finalizador da classe Tester"); } };//**********************************************************************

A classe contm cdigo para um construtor e um destrutor. Assim, o compilador gerar um mtodo Finalize automaticamente. PASSO 3: Crie um objeto Tester na funo _tmain, como abaixo:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Teste de finalizao"); //Cria um objeto Tester *pt = new Tester(); Console::WriteLine(S"Fim do teste"); return 0; }//***********************************************************************

PASSO 4: Compile e execute o cdigo. Voc poder ver mensagens de ambas as funes no console, indicando que tanto o construtor quanto o destrutor foram chamados (Figura 7.1). Note que a mensagem do destrutor impressa aps a mensagem Fim do teste, mostrando que o objeto est sendo destrudo como parte da limpeza do programa.

Figura 7.1: Sada do programa TestFinalize. PASSO 5: Mude o cdigo de modo que o objeto seja deletado imediatamente antes da mensagem Fim do teste.
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Teste de finalizao"); //Cria um objeto Tester *pt = new Tester(); //Deleta o objeto delete pt; Console::WriteLine(S"Fim do teste"); return 0; }//***********************************************************************

PASSO 6: Construa e execute o cdigo. Voc ver que a mensagem do destrutor impressa antes da mensagem Fim de teste, o que mostra que o objeto foi destrudo neste ponto. 121

7.2.3.

ALGUNS PONTOS SOBRE FINALIZAO

H trs pontos que voc precisa estar atento quando utilizar finalizadores no senso .NET ao invs do senso C++. Primeiro, os objetos com finalizadores gastam mais tempo para alocar e mais tempo para destruir porque o finalizador tem que ser chamado. Segundo, nenhuma garantia feita sobre qual a ordem em que os finalizadores sero chamados, o que pode ser problemtico quando usamos objetos aninhados. Suponha que a classe A contm uma referncia a um objeto da classe B. Ambos os finalizadores sero chamados quando um objeto A for destrudo, mas voc diz qual ser chamado primeiro, o que pode causar problemas se o finalizador da classe B for chamado em primeiro lugar e se a classe A referencia o objeto B embutido no seu finalizador. Por esta razo, voc no deve se referir a objetos embutidos em finalizadores. Finalmente, os finalizadores no so chamados em fechamento de aplicativo para objetos que ainda esto sendo usados, tais como os usados por linhas do fundo ou objetos que so criados como parte do processo de finalizao. Embora todos os recursos de sistema sejam liberados quando os aplicativos fecharem, os objetos que no tiverem seus finalizadores chamados podem no obter uma chance de limpeza apropriada.

7.2.4.

USANDO UM MTODO Dispose

O .NET Framework usa Finalize para limpeza quando um objeto coletado para a lixeira e chamado atravs do destrutor quando objetos C++ so destrudos. A maioria das outras linguagens .NET especialmente C# e Microsoft Visual Basic no lhe permitem qualquer controle sobre o tempo de vida dos objetos. Estas linguagens no oferecem qualquer equivalente do destrutor C++. Assim, Finalize ser chamado apenas quando o coletor de lixo reformar o objeto. Como voc pode ter certeza de que um objeto libera os recursos que suporta? Colocar o cdigo em Finalize uma m idia porque voc no sabe quando este mtodo ser chamado. No passado, os programadores tinham que codificar mtodos improvisados nas suas classes que os clientes tinham que chamar para forar a limpeza. No h eficincia quando todos os programadores tm que inventar sua prpria verso do mesmo mecanismo. Assim, a Microsoft formalizou o processo e introduziu a interface IDisposable na primeira verso do .NET Framework. NOTA: A interfaces esto relacionadas com herana e so abordadas com detalhe no captulo 8. Esta seo lhe mostrar como implementar um mtodo Dispose para sua classe, mas veja mais detalhes sobre o funcionamento das interfaces no captulo 8, se voc ainda no as conhece. As classes que implementam IDisposable tm que implementar um mtodo Dispose para liberar recursos no gerenciados. As clientes podem explicitamente chamar Dispose quando finalizarem o uso do objeto para liberar os recursos utilizados e deixar o tempo de execuo para a coleta de lixo referente ao objeto em um ponto apropriado. Voc pode implementar IDisposable na sua classe se ela utiliza recursos no gerenciados que a cliente possa querer liberar em um ponto definido no cdigo; voc quer usar uma classe C++ gerenciada de outras linguagens .NET.

7.2.4.1.

EXERCCIO: ADICIONANDO SUPORTE Dispose EM UMA CLASSE

O exerccio seguinte mostra como adicionar suporte Dispose para uma classe. PASSO 1: Abra o projeto TestFinalize. 122

PASSO 2: Edite a definio da classe Tester de modo que ela seja herdeira de IDisposable, como mostrado abaixo:
__gc class Tester : public IDisposable

Se voc no ainda no estudou sobre herana, esta sintaxe ser explicada no captulo 8. PASSO 3: Uma classe herdeira de IDisposable deve implementar um mtodo Dispose. Assim, adicione um novo mtodo public classe Tester:
void Dispose() { Console::WriteLine(S"Dispose da classe Tester"); }

O nome da funo deve ser Dispose, no deve ter argumentos e seu tipo de retorno void. PASSO 4: Chame Dispose na funo _tmain como abaixo:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Teste de finalizao"); //Cria um objeto Tester *pt = new Tester(); //Chama Dispose pt->Dispose(); Console::WriteLine(S"Fim do teste"); return 0; }//***********************************************************************

PASSO 5: Construa e execute o cdigo. Voc ver que o mtodo Dispose chamado antes que o objeto seja destrudo (7.2).

Figura 7.2: Sada do programa TestFinalize atual.

7.2.5.

INTEGRANDO Finalize E Dispose

H possibilidades de que ocorram problemas srios quando integramos Finalize e Dispose. O mtodo Dispose libera recursos em demanda, mas o objeto continua vivo, o que significa que algum pode tentar us-lo j com seus recursos liberados e no desejamos que isto ocorra. Outro problema que Dispose libera recursos em demanda, enquanto Finalize os libera quando o objeto coletado pela lixeira, o que significa que Finalize tem que saber se os recursos j foram liberados por uma chamada a Dispose de modo que ela no tente liber-los uma segunda vez. 123

A soluo adicionar um sinalizador para a classe que registre o estado dos recursos do objeto (j foi liberado? verdadeiro ou falso). Voc pode checar este sinalizador sempre que o objeto for acessado e programar um aviso se o objeto foi acessado aps a liberao dos seus recursos. O exerccio abaixo mostra como fazer isto.

7.2.5.1.

EXERCCIO: USANDO SINALIZADOR PARA LIBERAR RECURSOS

PASSO 1: Abra o projeto TestFinalize e adicione um membro booleano classe Tester:


//DEFINIO DA CLASSE GERENCIADA Tester*********************************** __gc class Tester : public IDisposable { //... private: bool bDisposed; };//**********************************************************************

PASSO 2: Fixe o sinalizador para false no construtor:


Tester() { Console::WriteLine(S"Construtor da classe Tester"); bDisposed = false; }

PASSO 3: Fixe o sinalizador para true no mtodo Dispose para mostrar que os recursos foram liberados:
void Dispose() { Console::WriteLine(S"Dispose da classe Tester"); //Liberando todos os recursos bDisposed = true; }

PASSO 4: Cheque o estado do sinalizador no destrutor e use o resultado para liberar recursos ou no:
~Tester() { Console::WriteLine(S"Finalizador da classe Tester"); if(bDisposed == false) { Console::WriteLine(S"Liberando recursos..."); } }

PASSO 5: Para mostrar como voc deve se proteger para no chamar um mtodo em um objeto que j tenha liberado seus recursos, adicione um mtodo public classe Tester:
void aMethod() { if(bDisposed) throw new ObjectDisposedException(S"Tester"); Console::WriteLine(S"aMethod da classe Tester"); }

124

O mtodo checa se o sinalizador true. Se for, o cdigo emite uma ObjectDisposedException, que uma das excees padronizadas fornecidas no namespace System para manipular este tipo de ocorrncia. PASSO 6: Adicione uma chamada a aMethod imediatamente aps a chamada a Dispose em _tmain:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Teste de finalizao"); //Cria um objeto Tester *pt = new Tester(); //Chama Dispose pt->Dispose(); //Tenta chamar um mtodo do objeto pt->aMethod(); Console::WriteLine(S"Fim do teste"); return 0; }//***********************************************************************

PASSO 7: Construa e execute o cdigo. A chamada ao mtodo provocar uma falha de programa quando a exceo gerada e a caixa de mensagens da Figura 7.3 mostrada. Na seqncia, a mensagem da Figura 7.4 exibida para prevenir ao chamador que ele est tentando usar um objeto cujos recursos j foram liberados na declarao pt->Dispose();.

Figura 7.3: Sada do programa TestFinalize mostrando a caixa de mensagens indicando um erro de depurao JIT.

125

Figura 7.4: Seqncia da sada do programa TestFinalize mostrada na Figura 7.3.

7.3.

RPIDAS REFERNCIAS SOBRE O CAPTULO 7

A Tabela 7.1 mostra um resumo deste captulo. Tabela 7.1: Resumo do captulo 7.
PARA Fornecer um finalizador a uma classe FAA ISTO Implemente um destrutor C++. Ex.: ~MyClass() { //Cdigo para limpeza } Fornecer um mtodo Dispose a uma Torne sua classe uma herdeira da interface classe IDisposable e implemente o mtodo Dispose. Ex.: __gc class MyClass : public IDisposable { public: void Dispose() { //Cdigo para limpeza } }; Integrar Finalize e Dispose Fornea um sinalizador booleano a ser fixado quando os recursos forem liberados. Fixe-o em Dispose e cheque-o no destrutor. Garantir que os mtodos no so Verifique o estado do sinalizador. Se os chamados em objetos que cujos recursos do objeto j foram reformados, ele enviar uma est morto e voc deve recursos foram reformados. ObjectDisposedException para informar ao chamador que o objeto j no existe. Forar uma coleta de lixo Chame o mtodo System::GC::Collect. Descobrir a que gerao de coletor de Chame o mtodo System::GC::GetGeneration, lixo um objeto pertence passando-lhe uma referncia para o objeto.

126

8.

CAPTULO 8: HERANA

Neste captulo, voc aprender a: Descrever a importncia da herana em programao orientada para objetos; Definir uma classe-base; Definir uma classe-derivada; Acessar membros da classe-base a partir da classe-derivada; Usar a palavra-chave virtual para conseguir polimorfismo; Definir classes abstratas e membros abstratos; Definir classes seladas; Usar interfaces.

Herana um importante conceito orientado para objeto, pois lhe permite definir uma classe-base comum que captura as similaridades entre vrias classes diferentes. A classe-base contm os membros de dados e as funes-membros comuns a todas as outras classes. Ento, voc pode derivar classes que herdam todos os membros da classe-base e adicionar novos membros de dados e funes-membros quando necessrio. As classes-derivadas tambm podem sobrepor alguns dos mtodos definidos na classe-base. Isto conhecido como polimorfismo e uma tcnica de programao extremamente til. Os benefcios da herana so bem documentados na comunidade orientada para objetos. A herana lhe ajuda a desenvolver aplicativos mais rapidamente porque voc pode reutilizar a funcionalidade definida na classe-base. Os testes e as manutenes so simplificados porque h menos cdigo no seu aplicativo. A herana tambm lhe ajuda a criar um modelo mais preciso e significante do seu sistema. Neste captulo, voc aprender como usar todos os aspectos da herana no Microsoft Visual C++. Tambm ver como definir classes-bases e classes-derivadas, e descobrir como usar efetivamente estas classes no seu aplicativo.

8.1.

PROJETANDO UMA HIERARQUIA HEREDITRIA

Antes de iniciar a escrever qualquer cdigo utilizando herana em C++, voc deve gastar algum tempo projetando a hierarquia hereditria. Identifique classes que tm comportamento comum e considere se estas classes se beneficiariam do uso da herana. Neste captulo, voc definir e implementar uma hierarquia hereditria representando tipos diferentes da contas bancrias. A Figura 8.1 ilustra como as classes sero organizadas na hierarquia hereditria.

Figura 8.1: Esquema da hierarquia hereditria para contas bancrias. 127

NOTA: A ilustrao da Figura 8.1 a notao UML (Unified Modeling Language, modelo de linguagem unificada) para representar a herana. Cada caixa no diagrama uma classe. A seta apontando para BankAccount denota herana em UML. BankAccount a classe-base (tambm conhecida como a superclasse). Ela define membros de dados e funes-membros comuns a todos os tipos de contas bancrias. CurrentAccount e SavingsAccount so classes-derivadas (tambm conhecidas como subclasses) e representam tipos especficos de contas bancrias. Estas classes-derivadas herdam todos os membros de dados e funes-membros de BankAccount, e, se necessrio, voc pode adicionar membros de dados e funes-membros extras para tipos diferentes de contas bancrias. CurrentAccount e SavingsAccount tambm podem sobrepor funes-membros definidas em BankAccount. Por exemplo, a classe BankAccount pode ter um mtodo chamado CanDebit para indicar se uma dada quantidade de dinheiro pode ser debitada da conta. As regras de aplices de seguro para permisso de dbito so diferentes para cada tipo de conta. Assim, CurrentAccount e SavingsAccount podem sobrepor o mtodo CanDebit para realizar o processamento requerido para cada tipo de conta.

Voc ir definir e implementar todas estas classes ao longo deste captulo. Vamos comear com a classe-base, BankAccount.

8.2.

DEFININDO UMA CLASSE-BASE

O C++ fornece vrias palavras-chaves que voc pode usar em uma classe-base para especificar como a classe-base exposta s classes-derivadas e ao programa cliente. Voc aprender sobre estas palavraschaves mais adiante, neste captulo. Por enquanto, voc criar uma classe-base simples que no usa nenhuma das caractersticas da linguagem relacionadas com herana. Quando voc define uma classe-base, o melhor modo de comear definindo as funes-membros comuns que sero requeridas por todas as classes-derivadas. Uma vez definidas estas funesmembros, adicione membros de dados para lhes darem suporte. Ento, fornea um ou mais construtores para inicializar estes membros de dados. HERANA EM C++ GERENCIADO Herana faz parte do C++ desde que BJARNE STROUSTRUP criou a linguagem, mas o C++ gerenciado introduziu vrias palavras-chaves adicionais para herana. Estas novas palavras-chaves facilitaram o uso da herana em seu aplicativo Visual C++ e tambm fornece conformidade com a CLS (Common Language Specification, especificao de linguagem comum) no .NET Framework. Alm disso, no possvel a uma classe-derivada em C++ gerenciado ter mais de uma classe-base. Mltiplas classes-bases so possveis em C++ no gerenciado. As classes em C++ gerenciado so restritas a uma nica classe-me para compatibilidade com outras linguagens .NET.

8.2.1.

EXERCCIO: CRIANDO UM NOVO IMPLEMENTANDO UMA CLASSE-BASE

APLICATIVO,

DEFININDO

Neste exerccio, voc criar um novo aplicativo e definir a classe BankAccount. Esta classe ser a me de todos os tipos de contas bancrias no aplicativo.

128

Em BankAccount, voc definir as funes-membros e os membros de dados comuns a todos os tipos de contas bancrias. Voc tambm definir um construtor e um destrutor para esta classe-me. PASSO 1: Abra o Microsoft Visual Studio .NET e crie um novo projeto Visual C++ Console Application (.NET) chamado BigBank. PASSO 2: Clique no menu Project Add New Item. Na caixa de dilogo Add New Item, selecione o modelo Header File (.h). No campo Name, digite BankAccount e clique em Open. O Visual Studio .NET cria um arquivo de cabealho vazio. PASSO 3: No arquivo de cabealho, digite o cdigo abaixo para definir a classe BankAccount:
//DIRETIVAS DE COMPILAO************************************************* #pragma once #using <mscorlib.dll> using namespace System; //************************************************************************ //DEFINIO DA CLASSE-BASE GERENCIADA BankAccount************************* __gc class BankAccount { public: BankAccount(String *holder); ~BankAccount(); void Credit(double amount); void Debit(double amount); private: String *accountHolder; double balance; };//**********************************************************************

DICA: A diretiva de compilao #pragma once especifica que este arquivo de cabealho ser processado apenas uma vez pelo compilador durante uma construo. Esta diretiva particularmente til para arquivos de cabealho freqentemente includos, tais como os que contm definies de classes-bases. Se omitir a diretiva #pragma once, quase certo que voc ir se deparar com erros de compilao quando tentar construir o aplicativo, porque BankAccount.h ser includo em vrios locais diferentes no aplicativo e o compilador gerar um erro se perceber a definio da classe BankAccount mais de uma vez. PASSO 4: Selecione o menu Project Add New Item. Na caixa de dilogo Add New Item, selecione o modelo C++ File (.cpp). No campo Name, digite BankAccount e, ento, clique no boto Open. O Visual Studio .NET cria um arquivo-fonte vazio. PASSO 5: No arquivo-fonte digite o cdigo abaixo para implementar a classe BankAccount:
#include "stdafx.h" #include "BankAccount.h" //DEFINIES DAS FUNES-MEMBROS DA CLASSE-BASE BankAccount*************** BankAccount::BankAccount(String *holder)//++++++++++++++++++++++++++++++++ : accountHolder(holder), balance(0.0) { }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BankAccount::~BankAccount()//+++++++++++++++++++++++++++++++++++++++++++++ { }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

129

void BankAccount::Credit(double amount)//+++++++++++++++++++++++++++++++++ { balance += amount; Console::Write(S"Aps o crdito, o novo balano : "); Console::WriteLine(balance); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void BankAccount::Debit(double amount)//++++++++++++++++++++++++++++++++++ { balance -= amount; Console::Write(S"Aps o dbito, o novo balano : "); Console::WriteLine(balance); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //************************************************************************

NOTA: O construtor usa uma lista de inicializao de membros para inicializar os membros de dados de BankAccount, que a sintaxe apropriada para inicializao de membros de dados em um construtor. Alm disso, a nica maneira de invocar construtores de classe-base, o que se tornar aparente quando, em breve, voc definir as classes-derivadas CurrentAccount e SavingsAccount. PASSO 6: Construa o programa para verificar se h erros de compilao.

8.3.

DEFININDO UMA CLASSE-DERIVADA

Para definir uma classe-derivada em C++ gerenciado, use a seguinte sintaxe:


__gc class MyDerivedClass : public MyBaseClass { //... };

Os dois pontos na definio da classe indicam herana. Aps os dois pontos, voc deve especificar a palavra-chave public seguida pelo nome da classe-base. A palavra-chave public indica que membros public na classe-base permanecero public quando forem herdados por sua classederivada. DICA: Se voc omitir a palavra-chave public aps os dois pontos, o nvel de acesso padro para herana private. Herana private uma tcnica especializada usada em C++ no gerenciado, mas no suportada em C++ gerenciado. Portanto, haver um erro de compilao se voc omitir a palavra-chave public na definio da classe-derivada.

8.3.1.

EXERCCIO: DEFININDO E IMPLEMENTANDO UMA CLASSE-DERIVADA

Neste exerccio, voc definir a classe CurrentAccount. CurrentAccount herdar de BankAccount, o que significa que no necessrio re-implementar funes-membros herdadas tais como Credit e Debit. Tambm no h necessidade de redefinir membros de dados herdados, tais como accountHolder e balance. Tudo o que voc precisa definir em CurrentAccount so as funes-membros e os membros de dados adicionais, que so aplicados especificamente a contas correntes. PASSO 1: Abre o projeto BigBank. 130

PASSO 2: Selecione o menu Project Add New Item. Na caixa de dilogo Add New Item, selecione o modelo Header File (.h). No campo Name, digite CurrentAccount e, ento, clique no boto Open. O Visual Studio .NET cria um arquivo de cabealho vazio. PASSO 3: No arquivo de cabealho, digite o cdigo abaixo para definir a classe CurrentAccount:
//DIRETIVAS DE COMPILAO************************************************* #pragma once #include "BankAccount.h" //************************************************************************ //DEFINIO DA CLASSE-DERIVADA CurrentAccount***************************** __gc class CurrentAccount : public BankAccount { public: CurrentAccount(String *holder, double limit); ~CurrentAccount(); void ChangeOverdraftLimit(double newLimit); private: double overdraftLimit; };//**********************************************************************

Note que a diretiva #include BankAccount.h requerida porque BankAccount a classebase de CurrentAccount. O compilador precisa saber como BankAccount definida para compilar a classe CurrentAccount. Note tambm que o construtor recebe dois parmetros. O primeiro inicializar o nome do proprietrio da conta (definido em BankAccount) e o segundo parmetro inicializar a varivel overdraftLimit (limite de saque a descoberto, definida em CurrentAccount). PASSO 4: Selecione Project Add New Item. Na caixa de dilogo Add New Item, selecione o modelo C++ File (.cpp). No campo Name, digite CurrentAccount e, ento, clique no boto Open. O Visual Studio .NET cria um arquivo-fonte vazio. PASSO 5: No arquivo-fonte, digite o cdigo abaixo para implementar a classe CurrentAccount:
#include "stdafx.h" #include "ContaCorrente.h" //DEFINIES DAS FUNES-MEMBROS DA CLASSE-DERIVADA CurrentAccount******** CurrentAccount::CurrentAccount(String *holder, double limit)//++++++++++++ : BankAccount(holder), overdraftLimit(limit) { }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CurrentAccount::~CurrentAccount()//+++++++++++++++++++++++++++++++++++++++ { }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void CurrentAccount::ChangeOverdraftLimit(double newLimit)//++++++++++++++ { overdraftLimit = newLimit; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //************************************************************************

O ponto mais importante a se observar aqui o construtor CurrentAccount. A lista de inicializao de membros inclui a sintaxe BankAccount(holder). Esta sintaxe chama o construtor na classe-base, BankAccount, para inicializar membros de dados herdados. Se voc der 131

uma olhada em BankAccount.cpp, ver que o construtor de BankAccount requer um parmetro String* para fixar o nome do proprietrio da conta. O balano sempre inicializado com 0. PASSO 6: Construa o programa. NOTA: O construtor da classe-derivada deve chamar o construtor da classe-base, usando a sintaxe da lista de inicializao de membros. Se voc esquecer de chamar o construtor da classe-base, o compilador tentar lhe auxiliar chamando um construtor sem argumentos na classe-base. Se no houver tal construtor na classe-base, voc ver um erro de compilao.

8.4.

ACESSANDO MEMBROS DA CLASSE-BASE

Quando define uma classe-derivada, voc pode acessar alguns membros na classe-base. Por exemplo, voc pode chamar uma funo-membro da classe-base para realizar alguma operao requerida por voc. Similarmente, voc pode acessar um membro de dados da classe-base de modo que possa realizar um clculo na sua classe-derivada. Se o membro da classe-base public, voc pode acess-lo em sua classe-derivada (ou em qualquer outro lugar do aplicativo). Porm, se o membro da classe-base for private, voc no pode acesslo na classe-derivada. Os membros private s podem ser acessados por funes-membros da sua prpria classe. Classes-derivadas no tm acesso a eles. Para contornar esta restrio, o C++ fornece um terceiro especificador de acesso protected. Se voc declarar um membro da classe-base como protected, ele se torna acessvel a esta classe e a todas as classes-derivadas. O exemplo abaixo ilustra os trs nveis de especificadores de acesso public, protected e private:
__gc class BankAccount { //Membros public, visveis em qualquer lugar. public: BankAccount(String *holder); ~BankAccount(); void Credit(double amount); void Debit(double amount); //Membros protected, visveis nesta classe e nas suas subclasses. protected: double balance; //Membros private, visveis somente nesta classe. private: String *accountHolder; };

DICA: Use o especificador de acesso protected com cuidado. Uma vez definido um membro de dados como protected, voc introduz uma dependncia entre sua classe e todas as classesderivadas. Se mudar a definio do membro de dados protected (por exemplo, mudando seu tipo de dados de int para String*), voc ter que modificar todas as classes-derivadas que usam este membro de dados.

132

8.4.1.

EXERCCIO: DEFININDO E IMPLEMENTANDO A CLASSE-DERIVADA SavingsAccount

Neste exerccio, voc definir e implementar a classe-derivada SavingsAccount. Esta classe ter uma funo-membro ApplyInterest, que adicionar o lucro conta-poupana. O lucro ser calculado como uma percentagem do balano atual. Porm, o balano atualmente declarado como um membro de dados private em BankAccount. Voc mudar isto para protected de modo que as classes-derivadas possam acessar o membro de dados balance. PASSO 1: Abra o projeto BigBank. PASSO 2: Em BankAccount.h, mude a definio da classe BankAccount. Especificamente, mude o especificador de acesso do membro de dados balance para protected. PASSO 3: Selecione o menu Project Add New Item. Na caixa de dilogo Add New Item, selecione o modelo Header File (.h). No campo Name, digite SavingsAccount e, ento, clique no boto Open. O Visual Studio .NET cria um arquivo de cabealho vazio. PASSO 4: No arquivo de cabealho, digite o cdigo abaixo para definir a classe SavingsAccount:
//DIRETIVAS E ARQUIVOS DE CABEALHOS************************************** #pragma once #include "BankAccount.h" //************************************************************************ //DEFINIO DA CLASSE-DERIVADA SavingsAccount***************************** __gc class SavingsAccount: public BankAccount { public: SavingsAccount(String *holder); ~SavingsAccount(); void ApplyInterest(); private: //5% de taxa de juros para todas as contas static double interestRate = 0.05; };//**********************************************************************

Note o membro de dados static interestRate. Este membro de dados permite que todas as contas-poupana compartilhem uma taxa de juros comum. NOTA: Os membros de dados static de classes gerenciadas C++ devem ser definidos dentro da classe. Em outras palavras, voc deve fornecer uma definio como static double interestRate = 0.05; ao invs de uma declarao como static double interestRate;. Contraste isto com as classes no gerenciadas onde os membros de dados static so definidos fora da definio da classe. PASSO 5: Selecione Project Add New Item. Na caixa de dilogo Add New Item, selecione o modelo C++ File (.cpp). No campo Name, digite SavingsAccount e, ento, clique no boto Open. O Visual Studio .NET cria um arquivo-fonte vazio. PASSO 6: No arquivo-fonte, digite o cdigo abaixo para implementar a classe SavingsAccount:
//ARQUIVOS DE CABEALHO*************************************************** #include "stdafx.h" #include "SavingsAccount.h" //************************************************************************ //DEFINIES DAS FUNES-MEMBROS DA CLASSE-DERIVADA SavingsAccount******** SavingsAccount::SavingsAccount(String *holder)//++++++++++++++++++++++++++ : BankAccount(holder)

133

{ }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SavingsAccount::~SavingsAccount()//+++++++++++++++++++++++++++++++++++++++ { }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void SavingsAccount::ApplyInterest()//++++++++++++++++++++++++++++++++++++ { Credit(balance * interestRate); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //************************************************************************

Note que a funo-membro ApplyInterest chama a funo-membro Credit, que definida na classe-base. bastante comum chamar funes herdadas da classe-base para reutilizar a funcionalidade definida nestas funes. Note tambm que ApplyInterest usa o membro de dados balance, que definido na classebase. A classe-base define balance como protected para permitir este acesso. PASSO 7: Construa o programa.

8.5.

CRIANDO OBJETOS

Quando voc define a hierarquia hereditria, a classe-base atua como um repositrio para as funesmembros e membros de dados comuns suportados por todas as classes-derivadas. Porm, a classebase usualmente no contm informaes suficientes para representar objetos reais. Considere o exemplo da conta bancria que estamos desenvolvendo neste captulo. Quando entra em um banco para abrir uma conta bancria, voc tem que saber qual o tipo de conta deseja abrir (corrente ou poupana). Voc no abre uma conta bancria genrica. Em termos de programao, voc deve se prevenir contra a criao de objetos BankAccount genricos. Voc s deve permitir instancializaes das classes-derivadas como CurrentAccount e SavingsAccount. Para obter este efeito em C++ gerenciado, declare a classe BankAccount como uma classe abstrata do seguinte modo:
__gc __abstract class BankAccount { //Corpo da classe };

8.5.1.

EXERCCIO: CRIANDO OBJETOS CurrentAccount E SavingsAccount

Neste exerccio, voc modificar a classe BankAccount como descrito acima para transform-la em uma classe abstrata. Ento, voc escrever algum cdigo na funo _tmain no aplicativo para criar e usar objetos CurrentAccount e SavingsAccount. PASSO 1: Abra o projeto BigBank. PASSO 2: Em BankAccount.h, mude a definio da classe BankAccount adicionando a palavrachave __abstract para transform-la numa classe abstrata. PASSO 3: No incio do arquivo-fonte BigBank.cpp, aps a diretiva #include "stdafx.h, adicione as seguintes diretivas #include: 134

#include "CurrentAccount.h" #include "SavingsAccount.h"

NOTA: No h necessidade de escrever explicitamente #include BankAccount.h porque este arquivo de cabealho j foi includo em CurrentAccount.h e SavingsAccount.h. PASSO 4: Na funo _tmain, tente criar objetos BankAccount como sugerido abaixo:
BankAccount *genericAccount = new BankAccount(S"Emlia");

PASSO 5: Construa o programa. Voc obter o erro de compilao mostrado na Figura 8.2, que confirma o fato de que BankAccount uma classe abstrata.

Figura 8.2: Janela Task List mostrando erro de compilao por causa da tentativa de criar um objeto da classe abstrata BankAccount. PASSO 6: Delete a declarao criada no PASSO 4 e adicione o cdigo abaixo em _tmain para criar e usar um objeto CurrentAccount:
//FUNO PRINCIPAL******************************************************** int _tmain() { CurrentAccount *current = new CurrentAccount(S"Emlia", 100); current->Credit(500); current->ChangeOverdraftLimit(300); current->Debit(750); return 0; }//***********************************************************************

PASSO 7: Construa e execute o programa. A Figura 8.3 mostra sua sada atual.

Figura 8.3: Sada do programa BigBank atual. PASSO 8: Para criar e usar um objeto SavingsAccount, mude o cdigo de _tmain para o cdigo abaixo:
//FUNO PRINCIPAL******************************************************** int _tmain() {

135

SavingsAccount *savings = new SavingsAccount(S"Thomas"); savings->Credit(500); savings->Debit(100); savings->ApplyInterest(); return 0; }//***********************************************************************

PASSO 9: Construa e execute o programa. A Figura 8.4 mostra a sada atual.

Figura 8.4: Atual sada do programa BigBank.

8.6.

SOBREPONDO FUNES-MEMBROS

Quando define uma classe-base, voc deve considerar se as classes-derivadas precisaro sobrepor algumas das funes-membros da sua classe-base. Para cada funo-membro na classe-base, h trs possibilidades: A funo da classe-base satisfatria para todas as classes-derivadas. As classes-derivadas nunca precisaro sobrepor a funo-membro com comportamento personalizado. As funes-membros Credit e Debit em BankAccount se ajustam a esta categoria. Elas simplesmente adicionam ou subtraem dinheiro do balano a classe-derivada no precisa sobrepor estas funes-membros. Eis um exemplo

__gc __abstract class BankAccount { public: void Credit(double amount);//Esta funo no pode ser sobreposta. void Debit(double amount);//Nem esta. //... };

A funo da classe-base realiza algumas tarefas, mas as classes-derivadas podem precisar sobrecarreg-la para obter comportamento personalizado. Para habilitar uma funo da classe-base a ser sobrecarregada, voc deve declar-la usando a palavra-chave virtual na definio da classe-base, como mostrado abaixo:

__gc __abstract class BankAccount { public: virtual String *ToString(); //Esta funo pode ser sobreposta. //... };

A funo da classe-base especifica alguma operao que requerida por todas as classes-derivadas, mas cada classe-derivada precisa realizar a operao de um modo significativamente diferente. No h comportamento comum sensvel que voc possa definir na classe-base. Para alcanar esse efeito, declare a funo-membro da classe-base usando a palavra-chave virtual. No final do

136

prottipo da funo, use a sintaxe = 0, como no exemplo abaixo. Esta sintaxe indica que a funo no implementada na classe-base as classes-derivadas devem sobrep-la. Eis um exemplo:
__gc __abstract class BankAccount { public://Esta funo deve ser sobreposta. virtual bool CanDebit(double amount) = 0; //... };

NOTA: EM C++, usamos o termo funo virtual pura para descrever uma funo que deve ser sobreposta pelas classes-derivadas. Outras linguagens de programao usam diferentes terminologias e palavras-chaves. Por exemplo, C# permite que voc use a palavra-chave abstract para este tipo de mtodo. O Microsoft Visual Basic usa a palavra-chave MustOverride.

8.6.1.

EXERCCIO: FUNO VIRTUAL E FUNO VIRTUAL PURA

Neste exerccio, voc definir a funo-membro ToString na classe BankAccount. Voc declarar esta funo como virtual para permitir que as classes-derivadas a sobreponham se necessrio. Voc tambm definir uma funo virtual pura chamada CanDebit na classe BankAccount. Isto forar todas as classes-derivadas a implementar CanDebit. PASSO 1: Abre o projeto BigBank. PASSO 2: Em BankAccount.h, adicione as seguintes declaraes de funo public classe BankAccount:
virtual String* ToString();//As classes derivadas podem sobrep-la. virtual bool CanDebit(double amount) = 0;//As classes-derivadas devem sobrep-la.

PASSO 3: Em BankAccount.cpp, implemente a funo ToString com o cdigo abaixo:


String* BankAccount::ToString()//+++++++++++++++++++++++++++++++++++++++++ { String *result = new String(S"Proprietrio da conta: "); result = String::Concat(result, accountHolder); result = String::Concat(result, S", Balano: "); result = String::Concat(result, balance.ToString()); return result; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 4: Modifique a funo-membro Debit com o cdigo abaixo:


void BankAccount::Debit(double amount)//++++++++++++++++++++++++++++++++++ { if(CanDebit(amount)) { balance -= amount; Console::Write(S"Dbito bem sucedido. O novo balano : "); Console::WriteLine(balance); } else { Console::Write(S"Dbito recusado. O balano permanece: "); Console::WriteLine(balance); }

137

}//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Note que Debit, agora, chama CanDebit para verificar se o dbito permitido. CanDebit no implementada em BankAccount, mas todas as classes-derivadas so obrigadas a fornecer esta funo virtual pura. Em tempo de execuo, a verso correta de CanDebit ser chamada dependendo do tipo de conta bancria que estiver sendo usada para a operao de dbito polimorfismo em ao! PASSO 5: Em CurrentAccount.h, adicione as seguintes declaraes de funo public classe CurrentAccount:
virtual String* ToString();//Escolhida para sobrepor ToString. virtual bool CanDebit(double amount);//Obrigada a sobrepor CanDebit.

PASSO 6: Em CurrentAccount.cpp, implemente a funo ToString com o cdigo abaixo:


String* CurrentAccount::ToString()//++++++++++++++++++++++++++++++++++++++ { String *result = __super::ToString(); result = String::Concat(result, S", Limite de saque a descoberto: "); result = String::Concat(result, overdraftLimit.ToString()); return result; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

A sintaxe __super::ToString chama a funo ToString na superclasse (BankAccount). Esta chamada retorna uma string contendo o nome do proprietrio da conta e o balano. Concatenamos a isto o valor do limite de saque sem fundo (overdraftLimit) e retornamos a string resultante. PASSO 7: Ainda em CurrentAccount.cpp, implemente a funo CanDebit com o cdigo abaixo:
bool CurrentAccount::CanDebit(double amount)//++++++++++++++++++++++++++++ { if(amount <= balance + overdraftLimit) { return true; } else { return false; } }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 8: Em SavingsAccount.h, adicione a seguinte declarao de funo public classe SavingsAccount:


virtual bool CanDebit(double amount);//Sobreposio obrigatria

Voc obrigado a sobrepor CanDebit porque ela uma funo virtual pura. Porm, no precisa sobrepor ToString porque a classe-base (BankAccount) fornece uma implementao padro desta funo. Fizemos a escolha de no sobrepor ToString na classe SavingsAccount. PASSO 9: Em SavingsAccount.cpp, implemente a funo CanDebit com o cdigo abaixo:
bool SavingsAccount::CanDebit(double amount)//++++++++++++++++++++++++++++ { if(amount <= balance / 10) { return true; } else { return false;

138

} }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Esta funo permite que o usurio retire at um dcimo do balano atual. PASSO 10: Em BigBank.cpp, troque o cdigo existente na funo _tmain por:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Testando a classe CurrentAccount"); CurrentAccount *current = new CurrentAccount(S"Emlia", 100); current->Credit(500); current->Debit(600);//Deve ser aceito current->Debit(1); //Deve ser recusado Console::WriteLine(current->ToString()); Console::WriteLine(S"\nTestando a classe SavingsAccount"); SavingsAccount *savings = new SavingsAccount(S"Thomas"); savings->Credit(500); savings->Debit(50); //Deve ser aceito savings->Debit(46); //Deve ser recusado Console::WriteLine(savings->ToString()); return 0; }//***********************************************************************

PASSO 11: Construa e execute o programa. A Figura 8.5 mostra a sua sada atual.

Figura 8.5: Sada da verso final do programa BigBank. PASSO 12: Crie um ponto de quebra na primeira declarao na funo _tmain e comece a depurar o programa. Percorra uma declarao de cada vez, entrando em cada funo de usurio para verificar qual a verso chamada durante a execuo.

8.7.

DEFININDO CLASSES SELADAS

Em C++ gerenciado, voc pode definir uma classe como sealed, o que significa que a classe no pode ser usada como uma classe-base. A definio de uma classe como selada uma til medida de segurana se sua classe contiver informaes sensveis ou se ela realiza operaes que voc no quer personalizar nas classes-derivadas. Para selar uma classe, use a palavra-chave __sealed na definio da classe como no cdigo abaixo: 139

__gc __sealed class MyClass { //... Corpo da classe. };

8.8.

DEFININDO E USANDO INTERFACES

Uma interface similar a uma classe, mas todas as funes-membros so virtuais puras. As interfaces so teis para definir capacidades para as diversas classes no seu aplicativo. O exemplo abaixo mostra como definir uma interface hipottica em C++ gerenciado:
__gc __interface IStorableAsXml { void ReadFromXmlFile(String *XmlFilename); void WriteToXmlFile(String *XmlFilename); };

DICA: Por conveno, os nomes das interfaces comeam com a letra I, o que facilita a distino entre interfaces e classes. Uma vez tendo definido a interface, voc pode implement-la em qualquer classe que suporte a funcionalidade prescrita na interface. A sintaxe para a implementao de uma interface a mesma usada em uma classe-base. O exemplo abaixo mostra como implementar a interface IStorableAsXml na classe SavingsAccount:
__gc class SavingsAccount : public BankAccount, public IStorableAsXml { public://Sobrepe funes definidas na interface void ReadFromXmlFile(String *XmlFilename) { } void WriteToXmlFile(String *XmlFilename) { } //Outros membros. };

Neste exemplo, SavingsAccount herda de BankAccount e implementa a interface IStorableAsXml. A classe SavingsAccount obrigada a implementar os mtodos ReadFromXmlFile e WriteToXmlFile.

8.9.

RPIDAS REFERNCIAS SOBRE O CAPTULO 8

A Tabela 8.1 mostra um resumo deste captulo. Tabela 8.1: Resumo do captulo 8.
PARA Definir uma classe-base abstrata FAA ISTO Use a palavra-chave __abstract na definio da classe. Ex.: __gc __abstract class MyBase { //... }; Na definio de classe-derivada, use dois pontos, seguidos por public, seguida pelo nome da classe-base.

Definir uma classederivada

140

Ex.: __gc class MyDerived : public MyBase { //... }; Construir objetos No construtor da classe-derivada, use uma lista de derivados inicializao de membros para chamar o construtor da classe-base. Ex.: MyDerived::MyDerived(int bdata, int ddata) : MyBase(bdata), derivedData(ddata) { //... } Habilitar classesDeclare os membros como protected na classe-base. derivadas a acessar Ex.: membros na classe-base __gc __abstract class MyBase enquanto nega acesso s { classes no relacionadas protected: int dataVisibleToDerivedClass; //... }; Definir funes-membros Declare as funes-membros como virtuais na classe. que possam ser Ex.: sobrepostas na classe__gc __abstract class MyBase base { protected: virtual void myOverridableFunction(); //... }; Especificar funesDeclare as funes-membros como virtuais na classe-base. membros da classe-base Aps o parntese de fechamento, anexe = 0. que devem ser Ex.: sobrepostas pelas __gc __abstract class MyBase classes-derivadas { protected: virtual void myOverridableFunction() = 0; //... }; Definir uma classe Use a palavra-chave __sealed na definio da classe. selada Ex.: __gc __sealed class MySealedClass { //... }; Definir uma interface Use a palavra-chave __interface. Ex.: __gc __interface IMyInterface { void function1(int n); int function2(double d); }; Implementar uma Use a mesma sintaxe utilizada em herana. Implemente interface todas as funes requeridas na sua classe. Ex.: __gc class MyImplementingClass : public IMyInterface { public: void function1(int n); int function2(double d); //... Outros membros.

141

};

PARTE 3: FUNDAMENTOS DE PROGRAMAO NO MICROSOFT .NET 9. CAPTULO 9: TIPOS DE VALOR

Neste captulo, voc aprender como Distinguir entre tipo de referncia e tipo de valor; Trabalhar com estruturas; Trabalhar com enumeraes.

Na PARTE 2, voc aprendeu sobre programao orientada para objetos e como aplic-la no .NET. Voc viu como muitos tipos de dados dentro do .NET so representados por classes e aprendeu como criar e usar suas prprias classes. Porm, nem todo tipo de dados no .NET uma classe e, agora, voc vai conhecer o outro bloco de construo fundamental dos tipos .NET o tipo de valor. Neste captulo, voc descobrir o que so tipos de valor e como eles diferem dos tipos de referncia que voc j encontrou neste texto. Voc tambm conhecer dois importantes tipos de valor estruturas e enumeraes que sero teis no desenvolvimento dos seus cdigos.

9.1.

TIPOS DE REFERNCIA E TIPOS DE VALOR

Vamos resumir o que voc aprendeu sobre classes at aqui. As classes so conhecidas como tipos de referncia porque voc sempre acessa objetos usando variveis de referncia. Considere a linha de cdigo abaixo:
MyClass *pc = new MyClass();

Neste exemplo, pc uma varivel de referncia que nos permite fazer referncia ao objeto MyClass criado pelo operador new. Acessar objetos usando referncias deste modo permite que o mecanismo de coleta de lixo do .NET recolha os recursos usados por um objeto quando no mais houver qualquer referncia a ele. Esta caracterstica do .NET torna eficiente o uso da memria, o que significa que voc no precisa mais se preocupar com um dos maiores problemas de programas em C++ tradicional o vazamento de memria. A segunda coisa que voc aprendeu sobre classes foi que elas contm membros de dados e funesmembros. Os membros de dados representam o estado do objeto e boa prtica faz-los private para a classe. As funes-membros fornecem o comportamento do objeto e elas usam os membros de dados para determinar como responder. Todas as operaes no objeto so feitas chamando as funesmembros, usando o operador ->, como na linha de cdigo abaixo:
result = pc->DoOperation();

9.1.1.

A NECESSIDADE DE TIPOS DE VALOR

Por que precisamos dos tipos de valor? Como o prprio nome indica, eles foram designados para suportar valores, tais como inteiros, nmeros ponto-flutuante, booleanas e caracteres. Qualquer coisa que seja basicamente uma envoltura em torno de um valor simples e seja menor do que cerca de 16 bytes uma boa candidata a um tipo de valor. Precisamos de tipos de valor porque queremos usar valores simples to eficientemente quanto possvel, mas tambm queremos que eles sejam utilizveis como objetos. O uso de valores como objetos um 142

problema com linguagens orientadas para objetos porque se os tipos bsicos so representados como objetos, todas as operaes (tais como adio e multiplicao de inteiros) tm que ser feitas chamando funes, o que no nada eficiente. Por outro lado, se os tipos bsicos no so representados como objetos, as operaes sobre eles podem ser muito eficientes, mas no podemos us-los onde so necessrios objetos. O .NET resolve este problema com tipos de valores que so representados e usados to eficientemente quanto os tipos embutidos, mas que tambm podem ser usados como objetos quando necessrio. Voc no precisa saber que isto est acontecendo na maioria das vezes que usa o .NET. Este processo, chamado boxing, discutido no captulo 25. A Tabela 9.1 traz um resumo dos tipos de valor fornecidos pelo .NET Framework. Tabela 9.1: Tipos de valor fornecidos pelo .NET Framework.
TIPO DE VALOR Byte SByte Int16 Int32 Int64 UInt16 UInt32 UInt64 Single Double Boolean Char Decimal IntPtr UIntPtr DESCRIO Inteiro sem sinal de 8 bits Inteiro com sinal de 8 bits Inteiro com sinal de 16 bits Inteiro com sinal de 32 bits Inteiro com sinal de 64 bits Inteiro sem sinal de 16 bits Inteiro sem sinal de 32 bits Inteiro sem sinal de 64 bits Nmero ponto-flutuante de preciso simples de 32 bits Nmero ponto-flutuante de dupla preciso de 64 bits Valor booleano Caractere Unicode de 16 bits valor decimal de 96 bits Inteiro com sinal cujo tamanho depende da plataforma Inteiro sem sinal cujo tamanho depende da plataforma TIPO EQUIVALENTE EM C++ GERENCIADO char signed char short int ou long __int64 unsigned short unsigned int ou unsigned long unsigned __int64 float double bool wchar_t Decimal Nenhum tipo embutido Nenhum tipo embutido

Note que os equivalentes C++ so simplesmente nomes para os tipos apelidos, se voc prefere que soam com mais naturalidade em C++ bsico. Embora seja mais natural usar os equivalentes da linguagem nativa, voc pode usar as duas maneiras. Por exemplo, as duas linhas de cdigo abaixo significam exatamente a mesma coisa:
int n = 0; System::Int32 n = 0; //Usa o tipo C++ gerenciado //Usa o tipo nativo .NET

9.1.2.

PROPRIEDADES DOS TIPOS DE VALOR

Um tipo de valor um tipo herdeiro da classe System::ValueType. Os tipos de valor tm vrias propriedades especiais: Tipos de valor so armazenados na stack (ao contrrio dos tipos de referncia, que so armazenados na heap em tempo de execuo). As instncias dos tipos de valor sempre so acessadas diretamente (ao contrrio dos tipos de referncia, que so acessados atravs de referncias). Acesso direto significa que voc no usa o operador new quando cria instncias. 143

Ao copiar tipos de valor, copiamos o valor, no a referncia.

Como voc pode ver, os tipos de valor se comportam exatamente como os tipos embutidos padres, como int e char, e so to eficientes quanto estes para uso. Como mencionamos na seo anterior, a principal diferena entre os tipos de valor e os tipos embutidos que os tipos de valor tambm podem ser tratados como objetos quando necessrio.

9.2.

ESTRUTURAS

As estruturas (struct) fornecem um modo para criar os dados compostos ou tipos de registro com os quais voc pode se deparar em outras linguagens de programao. Como nas classes, as estruturas podem conter funes-membros, membros de dados, propriedades, delegados e eventos, mas h uma importante diferena: as estruturas so tipos de valor, no tipos de referncia. Portanto, se tiver um tipo de valor que precise ter alguma estrutura interna, por exemplo, um ponto de coordenadas X e Y, voc pode implement-lo usando uma struct. O exerccio a seguir mostra como criar uma estrutura representando um ponto, como criar instncias da estrutura e como usar as instncias no cdigo. NOTA: Tanto no C++ tradicional quanto no gerenciado use a palavra-chave struct para definir estruturas. Este captulo discute o uso das estruturas .NET (gerenciadas) ao invs da struct tradicional. A declarao estruturas .NET tem a vantagem de torn-las funcionveis dentro do mundo .NET e tambm nos permite a troca de estruturas com outras linguagens .NET.

9.2.1.

EXERCCIO: CRIANDO E USANDO UMA ESTRUTURA SIMPLES

PASSO 1: Entre no Microsoft Visual Studio .NET e crie um novo projeto Visual C++ Console Application (.NET) com o nome Structs. PASSO 2: No topo do arquivo-fonte Structs.cpp, imediatamente aps a declarao using namespace System;, adicione a seguinte diferena de estrutura:
//DEFINIO DA ESTRUTURA Point******************************************** __value struct Point { public: int x, y; };//**********************************************************************

As palavras-chaves __value e struct iniciam uma definio de estrutura e voc notar que as estruturas so muito similares s classes no modo como so definidas. O corpo da estrutura limitado por chaves e termina em ponto-e-vrgula, e as palavras-chaves public e private so usadas para fixar o nvel de acesso para os membros da estrutura. Note o uso da palavra-chave __value aqui. Esta palavra-chave informa ao compilador que Point um tipo de valor e no uma estrutura C++ tradicional. importante que voc se lembre de usar __value quando definir suas estruturas. Esta estrutura simples representa um ponto em um grfico. Assim, ela tem dois membros de dados inteiros representando as coordenadas X e Y. PASSO 3: Para criar e inicializar um objeto Point, adicione as seguintes linhas funo _tmain do seu aplicativo:
//Cria um objeto Point Point p1;

144

//Inicializa seus membros p1.x = 10; p1.y = 20;

Note que o cdigo no usa o operador new. O operador new usado para criar referncias aos objetos e tipos de valor no so acessados por referncia. Ao invs disto, um objeto Point foi criado na stack do programa e voc o acessa diretamente como p1. Como os membros de dados so public neste ponto, voc pode acess-los usando o familiar operador ponto (.). PASSO 4: Adicione duas linhas abaixo para exibir no console o valor de um dos membros da struct:
Console::Write(S"p1.x "); Console::WriteLine(p1.x);

Se compilar e executar o programa neste ponto, voc ver a sada p1.x 10.

9.2.2.

EXERCCIO: INVESTIGANDO A ESTRUTURA

Neste exerccio, voc executar o programa sob controle do depurador de modo que poder percorrer a estrutura de tipo de valor que voc criou. PASSO 1: Abra o projeto Structs. PASSO 2: No incio da funo _tmain, insira um ponto de quebra para depurao (Figura 9.1).

Figura 9.1: Programa Structs pronto para depurao. PASSO 3: Inicie a depurao, pressione F5. Uma vez o programa carregado, ele executar e ir parar no ponto de quebra. Ento, voc pode usar a janela Locals na parte de baixo da tela para observar a estrutura do tipo Point. PASSO 4: Se a janela Locals no estiver na tela no modo de depurao, clique no menu Debug Windows Locals. Voc deve ver uma entrada para a varivel p1. Qualquer tipo que tenha estrutura interna por exemplo Point ter o sinal mais (+) esquerda do nome da varivel. 145

PASSO 5: Clique no sinal mais para expandir a estrutura. Voc ver alga similar Figura 9.2.

Figura 9.2: Janela Locals no modo de depurao do programa Structs. Voc pode ver que p1 tem trs entradas que convergem para ele. A primeira mostra p1 derivado de System::ValueType, que por ser turno se deriva de System::Object. As outras duas entradas so os membros x e y, ambos inteiros de 32 bits. Neste ponto da depurao, eles ainda no receberam um valor inicial. Assim, ambos so iguais a 0. PASSO 6: Pressione F10 at inicializar p1 (Figura 9.3).

Figura 9.3: Janela Locals mostrando a evoluo da depurao. 146

Com esta ao, p1 inicializado e, como voc ver na Figura 9.3, x recebe 10 e y 20. Note que o valor de y mudou de preto para vermelho na janela Locals, mostrando que este foi o passo anterior da execuo (ver a posio da seta amarela na funo _tmain). PASSO 7: Termine a depurao e pressione Shift + F5 (ou clique no boto Stop Debugging na barra de utilitrios) para fechar o depurador.

9.2.3.

DIFERENAS ENTRE ESTRUTURAS E CLASSES

As estruturas e as classes tm vrias diferenas fundamentais: Voc no pode inicializar membros em uma definio de estrutura. Se precisar fornecer inicializao para um tipo de estrutura, voc tem que providenciar um construtor As estruturas no tm finalizadores porque elas no so coletadas pela lixeira. Herana no aplicvel s estruturas. Assim, elas no podem herdar nada de ningum e no podem ser usadas como classe-base. Estruturas podem implementar interfaces.

9.2.4.

EXERCCIO: IMPLEMENTANDO CONSTRUTORES PARA UMA ESTRUTURA

Neste exerccio, voc adicionar um construtor estrutura Point de modo que as instncias podem ser inicializadas na criao. PASSO 1: Abra o projeto Structs. PASSO 2: Adicione as definies abaixo imediatamente aps a declarao public na estrutura Point:
Point()//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { x = 0; y = 0; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Point(int xVal, int yVal)//+++++++++++++++++++++++++++++++++++++++++++ { x = xVal; y = yVal; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

O primeiro construtor no recebe argumentos e simplesmente fixa ambos os membros de dados para 0. Um construtor sem argumentos chamado de construtor padro. O segundo recebe dois valores int e os utiliza para inicializar os membros de dados x e y. Neste caso, os argumentos so simplesmente copiados para os membros de dados, mas seria simples adicionar algum cheque para garantir que os dados so passados corretamente. NOTA: Quem j usou o C++ antes estar familiarizado com o uso de argumentos padres nos construtores. Voc no pode usar argumentos nos tipos gerenciados no Visual C++. Assim, precisa providenciar explicitamente um construtor padro. PASSO 3: Adicione o cdigo extra abaixo na funo _tmain para criar objetos Point inicializados:
Point p1; //Usa o construtor padro

147

Point p2(10, 20); //Usa o segundo construtor para fazer x = 10 e y = 20

9.2.5.

USANDO UMA ESTRUTURA DENTRO DE OUTRA

possvel e freqentemente til usar uma estrutura dentro de outra. Imagine que voc tem uma estrutura chamada Person para descrever uma pessoa. A estrutura contm o nome e a data de nascimento, entre outros dados. Voc pode usar campos separados para cada item, mas tambm pode fazer as entradas de dados em outra estrutura e se referir a ela dentro de Person. Eis um exemplo:
//Uma estrutura de dados contendo dia, ms e ano __value struct Date { int dd, mm, yyyy; }; //Uma estrutura Person contendo um membro Date __value struct Person { String *name; Date DOB; };

Voc pode ver que a estrutura Date contm trs membros representando o dia, o ms e o ano. Esta estrutura bastante geral. Assim, voc pode us-la em outros programas. A estrutura Person contm uma referncia String para reter o nome e um objeto Date para reter a data de nascimento.

9.2.5.1.

EXERCCIO: INVESTIGANDO COMO FUNCIONAM MEMBROS DE DADOS DE ESTRUTURA

Neste exerccio, voc usar as estruturas Person e Date para investigar como funcionam os membros de dados de uma estrutura. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Person. PASSO 2: No incio do arquivo-fonte Person.cpp, aps a declarao using namespace System;, adicione as definies das estruturas Date e Person dadas acima. PASSO 3: Na funo _tmain, crie um objeto Person. Relembre que voc no usa new porque as estruturas so tipos de valor:
//Cria um objeto Person Person p1;

PASSO 4: Preencha os valores para os campos:


//Inicializa os campos p1.name = "Fred"; p1.DOB.dd = 10; p1.DOB.mm = 3; p1.DOB.yyyy = 1960;

Note como os membros de dados da estrutura so acessados. Como o membro DOB tem seus prprios membros, voc simplesmente usa outro operador ponto (.) para acessar estes membros. Voc pode continuar este aninhamento tantos nveis quantos deseje, embora no seja muito usual se aprofundar mais do que fizemos no exemplo. 148

PASSO 5: Voc tambm pode inicializar todos os membros de Person em uma linha. Remova as quatro linhas de inicializao do PASSO 4 e digite a linha abaixo onde voc criou o objeto Person:
//Cria e inicializa um objeto Person Person p1 = {"Fred", {10, 3, 1960}};

Voc pode ver o que est acontecendo aqui? O contedo das chaves mais externas chamado inicializador agregado so os dados para a inicializao da estrutura como um todo. Person contm dois itens, uma String e uma estrutura Date. Portanto, h dois itens na lista. Como Date tem seus prprios membros, suas entradas so colocas entre chaves. NOTA: O uso de um inicializador agregado uma alternativa ao uso de um construtor e pode ser til onde no necessrio fazer checagem nos dados. PASSO 6: Se achar que a data de nascimento est errada, voc pode simplesmente criar um novo objeto Date e copi-lo no objeto Person. Experimente isto:
//Cria e inicializa um novo objeto Date Date newDOB = {1, 4, 1955}; p1.DOB = newDOB;

O novo objeto Date recebe os valores especificados no inicializador e, ento, copiado no objeto Person, sobrescrevendo os valores antigos. PASSO 7: Voc pode ver a organizao da estrutura Person executando o programa sob o controle do depurador. Coloque um ponto de quebra na linha onde p1 criado. PASSO 8: Inicie o programa pressionando F5 e aguarde at ele parar no ponto de quebra. Ento, use a janela Locals para observar a estrutura do tipo Person. PASSO 9: Clique no sinal + de p1 na janela Locals para expandir a estrutura Person. Clique tambm no sinal + de DOB. A Figura 9.4 mostra o ponto de quebra na funo _tmain e o objeto p1 expandido. Note que nem o membro de dados name nem a estrutura DOB foram inicializadas.

149

Figura 9.4: Situao no incio da depurao do programa Structs atual. PASSO 10: Pressione F10 at que os membros sejam inicializados. A Figura 9.5 mostra este instante.

Figura 9.5: Situao atual da depurao. PASSO 11: Quando terminar, pressione Shift + F5 para sair do depurador. Finalmente, vamos considerar definies de estruturas aninhadas. Se no quiser usar a estrutura Date fora da Person, voc pode defini-la internamente, como abaixo:
//Estrutura Person contendo um membro Date++++++++++++++++++++++++++++++++ __value struct Person { String *name; //Estrutura de dados contendo dia, ms e ano-------------------------__value struct Date { int dd, mm, yyyy; };//-----------------------------------------------------------------Date DOB; };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Voc cria variveis Person e acessa seus membros exatamente do mesmo modo que antes. A grande diferena agora que a estrutura Date uma parte de Person. Assim, voc no pode criar variveis Date independentes.

150

9.2.6.

COPIANDO ESTRUTURAS

Como as estruturas so tipos de valor, copi-las equivalente a copiar os valores que elas contm. Contraste este comportamento com o das classes, onde copiar objetos resulta em cpias de referncias.
Person p1, p2; //... p2 = p1 //Dados de p1 so copiados em p2 MyClass m1, m2; //... m2 = m1; //m2 e m1, agora, se referem ao mesmo objeto. //Nenhum dado copiado.

NOTA: Voc no pode usar um tipo de referncia __gc como um membro de uma estrutura porque as estruturas no so coletadas pela lixeira. Um membro de referncia teria que fazer parte de um coletor de lixo.

9.3.

ENUMERAES

Uma enum um conjunto de constantes inteiras nomeadas. Enumeraes so especialmente teis para representar tipos que recebem um elemento de um conjunto de valores fixos, como por exemplo, os dias da semana ou os meses do ano. Enumeraes so tipos de valor que se derivam da classe abstrata System::Enum, que por sua vez se deriva de System::ValueType.

9.3.1.

EXERCCIO: CRIANDO E USANDO UMA ENUMERAO

No exerccio abaixo, voc criar uma enum para suportar valores representando os dias da semana e, ento, os usar em um programa. PASSO 1: Crie um projeto Visual C++ Console Application (.NET) chamado Enums. PASSO 2: No incio do arquivo-fonte Enums.cpp, imediatamente aps a declarao using namespace System;, adicione a definio de enumerao abaixo:
__value enum WeekDay//++++++++++++++++++++++++++++++++++++++++++++++++++++ { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

As palavras-chaves __value e enum iniciam uma definio de enumerao e voc notar que, mais uma vez, as enumeraes so definidas similarmente s classes. O corpo da enumerao est entre chaves e termina com um ponto-e-vrgula. Note o uso da palavra-chave __value aqui. J sabemos que esta palavra-chave que informa ao compilador que este um tipo de valor e no uma enumerao C++ tradicional. muito importante que voc se lembre de usar __value quando definir suas enumeraes. A enumerao em si consiste de um conjunto de nomes separados por vrgula, cada um dos quais representando uma constante inteira. PASSO 3: Crie variveis enum de modo similar aos outros tipos de valor j estudos. Para criar e inicializar um objeto WeekDay, adicione as linhas abaixo funo _tmain do seu aplicativo:
//Cria e inicializa um objeto WeekDay WeekDay w = Monday;

151

Como no caso das estruturas, o cdigo no usa o operador new. Uma enumerao chamada WeekDay foi criada na stack do programa e voc pode acess-la diretamente como w. Note como a varivel enum inicializada com um dos membros da enumerao. Esta sintaxe como voc inicializa variveis enum e como pode mudar seus valores posteriormente. PASSO 4: Use as linhas abaixo para imprimir no console:
Console::Write(S"O valor de w "); Console::WriteLine(w);

O valor 0 deve ser impresso. Cada uma das constantes nomeadas que compem a enumerao representa um valor inteiro. Por padro, estes valores comeam por 0 e crescem de 1 a cada membro subseqente da enumerao. Voc pode testar esta sada mudando o valor com o qual voc inicializa w, por exemplo, para Saturday. Quando voc executar o cdigo novamente, o valor 5 ser impresso. Embora o valor dado para uma enumerao seja um inteiro, no h nenhuma converso implcita entre os tipos enum e int. Se voc considerar as linhas de cdigo abaixo, compreender por qu:
//Este cdigo no compila! //'1' significaria Tuesday w = 1; //O que '8' significa? w = 8;

Se for permitida converso entre enum e int, seria possvel colocar valores invlidos na enumerao. No outro sentido, tudo ok, porque qualquer valor enum um inteiro vlido. Voc no tem que se prender aos valores numricos padres que so nomeados para membros enum. Suponha que deseje os equivalentes inteiros dos dias da semana no intervalo de 1 a 7 ao invs de entre 0 e 6. simplesmente nomeia 1 para o membro Monday, como mostrado abaixo:
__value enum WeekDay//++++++++++++++++++++++++++++++++++++++++++++++++++++ { Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

A enumerao, agora, comear com 1 e, como voc no forneceu nenhum valor para os membros restantes, eles sero numerados de 2 a 7. Se quiser, voc pode fornecer uma srie completamente descontnua de valores para os membros da enumerao, como no exemplo abaixo:
__value enum StatusCodes { OK = 0, FileNotFound = 2, AccessDenied = 5, InvalidHandle = 6, OutOfMemory = 8 };

9.3.2.

EXERCCIO: USANDO ENUMERAES EM PROGRAMAS

Neste exerccio, voc ver como usar uma enumerao para controlar a execuo do programa utilizando a declarao switch. PASSO 1: Abra o projeto Enums. PASSO 2: Na funo _tmain, aps a declarao contendo WriteLine, adicione o cdigo abaixo:
switch(w)//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { case Monday://-------------------------------------------------------Console::WriteLine(S"Segunda-Feira!");

152

break;//---------------------------------------------------------case Tuesday://------------------------------------------------------Console::WriteLine(S"Tera-Feira!"); break;//---------------------------------------------------------case Wednesday://----------------------------------------------------Console::WriteLine(S"Quarta-Feira!"); break;//---------------------------------------------------------default://-----------------------------------------------------------Console::WriteLine(S"Quinta, sexta, sbado ou domingo!"); break;//---------------------------------------------------------}//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Voc pode usar uma varivel enum como uma varivel de controle de switch porque ela , basicamente, um inteiro. Igualmente, voc pode usar os nomes dos membros da enumerao como os nveis case de switch porque eles tambm so inteiros. O exemplo acima tem nveis case para Monday, Tuesday e Wednesday; o resto manipulado pelo default. Lembre-se de colocar as declaraes break aps o cdigo de cada ramificao ou o programa no ter o comportamento esperado.

9.3.3.

EVITANDO AMBIGIDADES

Os nomes dos membros de uma enumerao no tm que ser unvocos, mas se no forem, voc pode precisar qualific-los com o nome da enumerao qual pertencem para evitar ambigidades. Suponha que voc tenha duas enumeraes declaradas no seu programa Direction e Mood ambas possuindo um membro chamado Down. Eis como voc deve us-las:
int myMood = Down;//Ambguo int myMood = Mood::Down;//Ok

9.3.4.

USANDO MEMRIA EFICIENTEMENTE

Por padro, um enum um int e, portanto, tem tamanho de 32 bits, o que lhe fornece nmeros que variam de 2147483648 a 2147483647. Se voc precisa apenas de valores pequenos para membros enum, haver desperdcio de memria se cada varivel ocupar 32 bits. Por esta razo, possvel basear uma enum em qualquer tipo de inteiro. No caso do nosso exemplo WeekDay, todos os valores se ajustam sem problemas em 1 byte. Voc poderia basear a enum em char, como no exemplo abaixo:
__value enum WeekDay : char//(Usando 1 byte)++++++++++++++++++++++++++++++ { Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

153

9.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 9

A Tabela 9.2 mostra um resumo deste captulo. Tabela 9.2: Resumo do captulo 9.
PARA Criar uma estrutura FAA ISTO Use __value struct + nome da estrutura + {}; Ex.: __value struct Point3D { int x, y, z; }; Inicializar Crie um construtor, que uma funo com o mesmo nome da estrutura. membros de Ex.: uma estrutura __value struct Point3D { int x, y, z; Point3D(int xVal, int yVal, int zVal) { x = xVal; y = yVal; z = zVal; } }; Voc tambm pode usar um inicializador agregado: Point3D p1 = {10, 20, 30}; Acessar Use a notao com o operador ponto (.). membros de Ex.: uma estrutura p1.x = 10; myPerson.DOB.dd = 20; Criar uma Use __value enum + nome da enumerao + {}; enumerao Ex.: __value enum Seasons { Spring, Summer, Autumn, Winter }; Controlar os Nomeie valores para os membros na definio da enumerao. valores Ex.: usados para __value enum Seasons membros de { uma Spring = 1, Summer, Autumn, Winter enumerao }; Basear Coloque dois pontos aps o nome da enumerao + o nome do tipo. enumeraes Ex.: em outros __value enum Seasons : char tipos de { inteiros Spring, Summer, Autumn, Winter };

154

10.

CAPTULO 10: SOBRECARREGANDO OPERADORES

Neste captulo, voc aprender O que sobrecarregar um operador; Quais as classes que devem suportar sobrecarga de operador; O que voc pode e o que no pode sobrecarregar; Como implementar sobrecargas de operador.

Voc j viu como construir classes e estruturas, como fornecer funes-membros nos seus tipos e como usar estas funes em programas. Neste captulo, voc vai descobrir uma categoria especial de funes-membros chamadas funes de operador sobrecarregado, que lhe permite adicionar funcionalidade extra de modo que seus tipos possam ser usados mais natural e intuitivamente. NOTA: Se j conhece sobrecarga de operador em C++, voc precisa ficar atento porque a sobrecarga em C++ gerenciado manipulada de modo completamente diferente do C++ tradicional. Na realidade, os tipos C++ gerenciado no permitem a implementao de operadores sobrecarregados do C++ tradicional. Assim, voc precisar prestar ateno redobrada a este captulo para descobrir como a sobrecarga de operadores feita agora.

10.1.

O QUE SOBRECARREGAR OPERADOR?

No captulo 3, voc conheceu os operadores fornecidos pela linguagem C++. O problema que aqueles operadores funcionam somente com os tipos embutidos da linguagem e voc est comeando a usar classes e estruturas para definir seus prprios tipos de dados. Isto significa que, se quiser fazer uma operao de adio ou uma operao de comparao entre tipos criados por voc, no poder usar os operadores + e == porque o compilador no sabe como aplic-los aos seus tipos. A sobrecarga de operadores uma caracterstica C++ que lhe permitir definir operadores para funcionar com seus tipos, o que freqentemente leva a um estilo mais natural de programao. Assim, ao invs de escrever isto:
object3 = object1.Add(object2);

voc pode escrever isto:


object3 = object1 + object2;

10.1.1.

QUAIS OS TIPOS QUE PRECISAM DE OPERADORES SOBRECARREGADOS?

Em geral, precisamos sobrecarregar operadores para as classes que so contineres de valores simples. Os tipos podem ser divididos em trs categorias gerais, como mostrado na Tabela 10.1. Os valores so as classes para as quais voc se achar freqentemente implementando operadores sobrecarregados. Voc pode imaginar implementaes de +, >, == e outros operadores para tipos como Date e String, mas mais sutil enxergar quando devemos implement-los para as outras classificaes da Tabela 10.1. Tipos de servio, que tm pouco ou nenhum estado, no tendem a necessitar de operadores: o que significa a comparao de dois objetos AddressLookup? Tipos de entidade podem ter alguns operadores, mas seus significados podem no ser intuitivos. Voc pode usar == para checar duas BankAccount para igualdade, mas o que significaria isto? Falaremos mais sobre igualdade mais adiante, neste captulo. Agora vamos ver como funciona a sobrecarga de operadores. 155

Tabela 10.1: Classificao de tipos em classes.


CLASSIFICAO Valores DEFININDO CARACTERSTICAS Dados internos de valores. Se dois objetos contm os mesmos dados, estes objetos so idnticos. Os servios tm pouco ou nenhum dado de estado. Eles fornecem servios atravs das suas funesmembros. As entidades tm uma identidade que nica para cada objeto. EXEMPLOS String, Matrix, Date e Time

Servios

CreditCardCheck e AddressLookup

Entidades

BankAccount (identificada pelo nmero da conta) e Person (identificada pelo nmero do seguro social)

10.1.2.

O QUE VOC PODE SOBRECARREGAR?

Do captulo 3, voc j conhece o rico conjunto de operadores que o C++ suporta. Muitos destes voc pode sobrecarregar, mas h algumas restries. O C++ tradicional no o deixa sobrecarregar vrios dos operadores mais esotricos, como sizeof e o operador ponto de membro (.). O C++ gerenciado estende a lista e adiciona alguns outros operadores C++ que no podiam ser sobrecarregados, incluindo ->, () e []. A principal razo para esta restrio que a CLS (Common Language Specification) projetada para usar linguagens cruzadas e por isso ela suportar um conjunto de operadores que seja til a todas as linguagens .NET, ao invs de suportar operadores especficos para C++. Voc ver mais tarde quais so todos os operadores .NET que podem ser sobrecarregados.

10.1.3.

REGRAS PARA SOBRECARGA

Vrias regras so aplicadas quando sobrecarregamos operadores. O problema que voc pode implementar operadores para significar tudo o que voc quiser. Assim, algumas regras so necessrias para impor alguns limites e evitar que voc passe para o compilador uma tarefa impossvel. Voc no pode definir novos operadores. Mesmo que pense que %% resultaria em um novo operador, voc no pode adicion-lo. Voc no pode mudar o nmero de operandos tomados por um operador. Voc pode at pensar que seria realmente til criar um operador / unrio, mas o operador diviso sempre tem dois operandos. Voc no pode mudar a precedncia ou associatividade dos operadores. Assim, * sempre ter precedncia sobre +, no importa o significado do que eles atualmente implementam para um tipo e (por exemplo) o operando esquerdo de um * sempre ser avaliado antes do operando direito.

10.2.

SOBRECARREGANDO OPERADORES EM TIPOS GERENCIADOS

A sobrecarga de operadores em tipos gerenciados bastante diferente da sobrecarga em C++ tradicional. A CLS define a lista de operadores aritmticos, lgicos e bit-a-bit que as linguagens .NET podem suportar e os vendedores de compilador usam os operadores nativos da linguagem para implementar estas funes. Assim, quando sobrepe um operador em um tipo C++ gerenciado, voc no sobrepe o operador C++, mas a funcionalidade CLS subjacente. 156

NOTA: Se voc atualmente um programador C++, verifique sempre se o compilador lhe permite implementar sobrecargas normais de operador C++ em tipos gerenciados. Voc tem que usar o mecanismo de sobrecarga que descreveremos.

10.2.1.

SOBRECARREGANDO TIPOS DE VALOR

Vamos comear adicionando sobrecarga de operador a tipos de valor e, ento, passaremos aos tipos de referncia. Voc j sabe que os tipos de valor so os que provavelmente precisem de sobrecarga de operadores.

10.2.1.1.

EXERCCIO: SOBRECARREGANDO OPERADORES ARITMTICOS

Neste exerccio, voc ver como implementar operadores em um tipo de valor. O exerccio tambm introduz muitas das tcnicas que voc precisar usar quando adicionar sobrecarga de operadores ao seus prprios tipos. PASSO 1: Crie um projeto Visual Overload. C++ Console Application (.NET) chamado

PASSO 2: No incio do arquivo-fonte Overload.cpp, imediatamente aps a declarao using namespace System;, adicione a definio de estrutura abaixo:
__value struct Dbl//++++++++++++++++++++++++++++++++++++++++++++++++++++++ { double val; public: Dbl(double v)//------------------------------------------------------{ val = v; }//------------------------------------------------------------------double getVal()//----------------------------------------------------{ return val; }//------------------------------------------------------------------};//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Voc usar esta estrutura simples ao longo destes exerccios. Ela simplesmente encapsula um double e, ento, fornece um construtor para criar e inicializar objetos Dbl e uma funo get para acessar o membro de dados. Como voc deve se lembrar do captulo 9, a palavra-chave __value torna Dbl um tipo de valor .NET ao invs da tradicional estrutura C++. PASSO 3: Na funo _tmain, crie trs objetos Dbl:
Dbl d1(10.0); Dbl d2(20.0); Dbl d3(0.0);

PASSO 4: Adicione uma chamada a Console::WriteLine para imprimir o valor de d3 no console:


Console::Write(S"O valor de d3 "); Console::WriteLine(d3.getVal());

Relembre que voc tem de usar o operador ponto para referenciar membros de um objeto gerenciado. PASSO 5: Tente adicionar d1 e d2 e passe o resultado para d3. Insira a linha abaixo no cdigo de _tmain, antes das declaraes do PASSO 4: 157

d3 = d1 + d2;

Quando tentar isto, ver que no funciona. O compilador retorna um erro C2676 (Figura 10.1).

Figura 10.1: Erro decorrente da tentativa de sobrecarregar o operador +. O compilador informa que ele no tem um operador + para usar que funcione com objetos Dbl. Portanto, ele no pode realizar a operao. PASSO 6: Implemente o operador + para a estrutura adicionando o cdigo abaixo na definio de Dbl, imediatamente aps a funo getVal:
//O operador + para a estrutura Dbl//+++++++++++++++++++++++++++++++++ static Dbl op_Addition(Dbl lhs, Dbl rhs) { Dbl result(lhs.val + rhs.val); return result; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Vamos analisar esta funo. A palavra-chave static, com a qual voc se encontrou no captulo 6, lhe informa que esta funo pertence estrutura Dbl como um todo, ao invs de pertencer a uma instncia particular da estrutura. O operador de adio implementado pela funo op_Addition, que uma das funes sobrecarregveis definidas na CLS. A Tabela 10.2 lista os operadores mais comuns suportados pela CLS, juntos com seus equivalentes C++. Para sobrecarregar um operador, voc escolhe a funo equivalente da lista e a implementa na sua classe. Os argumentos que uma funo recebe e o que ela retorna depende da funo. Neste caso, estamos implementando o operador binrio adio. Assim, os argumentos vo ser os dois valores Dbl e o tipo de retorno tambm ser um valor Dbl. Voc pode ver como a funo trabalha. O resultado da adio dos dois valores Dbl um terceiro Dbl. Assim, voc cria mais um Dbl, inicializa-o com os contedos dos dois operandos e o retorna. SOBRECARGA DE OPERADOR EM C++ NO GERENCIADO

158

Caso precise fornecer operadores sobrecarregados tradicionais (C++ no gerenciado), voc ver que a sobrecarga de operador feita de modo bem diferente. As sobrecargas de operador usam a palavrachave operator para nomear uma funo de sobrecarga. Assim, o operador de adio implementado por uma funo chamada operator+, o de igualdade pela funo operator==, o de incremento pela funo operator++, e assim por diante. Estas funes ou so funes-membros do tipo ou funes globais que no so membro de nenhum tipo. H muitos mais sobrecargas de operadores em C++ no gerenciado. Se precisar conhecer mais, consulte um livro de programao C++. The C++ Programming Language de Bjarne Stroustrup, Addison-Wesley, 1997, o texto clssico sobre C++. Tabela 10.2: Operadores mais comuns suportados pela CLS.
OPERAO Decremento Incremento Menos unrio Mais unrio Adio Subtrao Multiplicao Diviso Modulus Atribuio Igualdade Desigualdade Menor que Menor ou igual a Maior que Maior ou igual a AND lgico OR lgico NOT lgico Deslocamento para a esquerda Deslocamento para a direita AND bit-a-bit OR bit-a-bit OR exclusivo OPERADOR C++ -++ - (unrio) + (unrio) + (binrio) - (binrio) * / % = == != < <= > >= && || ! << >> & | ^ FUNO CLS op_Decrement op_Increment op_UnaryNegation op_UnaryPlus op_Addition op_Subtraction op_Multiply op_Division op_Modulus op_Assign op_Equality op_Inequality op_LessThan op_LessThanOrEqual op_GreaterThan op_GreaterThanOrEqual op_LogicalAnd op_LogicalOr op_LogicalNot op_LeftShitf op_RightShift op_BitwiseAnd op_BitwiseOr op_ExclusiveOr

PASSO 7: Tente compilar seu programa novamente. Voc deve obter sucesso na compilao desta vez porque o compilador reconhece que voc implementou o operador +. Execute o programa e voc obter a sada mostrada na Figura 10.2.

Figura 10.2: Sada atual do programa Overload. Se seu tipo implementar um operador, o compilador mapeia operadores C++ (tais como +) na funo op_ equivalente para voc. Voc tambm pode chamar a funo do operador explicitamente, embora 159

geralmente no haja motivos que justifiquem este procedimento. Mas, caso deseje, voc pode trocar a linha de adio por:
//d3 = d1 + d2; d3 = Dbl::op_Addition(d1, d2);

Um mtodo static acessado usando o nome do tipo seguido pelo operador :: e o nome da funo. Agora que viu como implementar o operador adio, ser mais simples para voc implementar outros operadores aritmticos para a estrutura (subtrao, diviso, multiplicao e mdulo).

10.2.2.

SOBRECARREGANDO FUNES DE OPERADOR

Voc tambm pode sobrecarregar as prprias funes de operador. Por exemplo, suponha que voc deseje manipular as seguintes operaes de adio:
//Adio de dois Dbl d3 = d1 + d2; //Adio de um Dbl e um int d3 = d1 + 5; //Adio de um int e um Dbl d3 = 5 + d2;

Voc pode sobrepor adies da funo op_Addition para manipular esta tarefa. Veja o exerccio a seguir.

10.2.2.1.

EXERCCIO: SOBRECARREGANDO FUNES DE OPERADOR

PASSO 1: Abra o projeto Overload. PASSO 2: Na estrutura Dbl, adicione as seguintes variaes para a funo op_Addition:
//Adicionando um Dbl e um int+++++++++++++++++++++++++++++++++++++++++ static Dbl op_Addition(Dbl lhs, int rhs) { Dbl result(lhs.val + rhs); return result; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //Adicionando um int e um Dbl+++++++++++++++++++++++++++++++++++++++++ static Dbl op_Addition(int lhs, Dbl rhs) { Dbl result(lhs + rhs.val); return result; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 3: Coloque o cdigo abaixo na funo _tmain para testar as novas funes de operador:
//Adiciona dois Dbl d3 = d1 + d2; Console::Write(S"O valor de d3 "); Console::WriteLine(d3.getVal()); //Adiciona um Dbl e um int d3 = d1 + 5; Console::Write(S"\nO valor de d3 agora "); Console::WriteLine(d3.getVal());

160

//Adiciona um int e um Dbl d3 = 5 + d2; Console::Write(S"\nO valor de d3 agora "); Console::WriteLine(d3.getVal());

PASSO 4: Agora um passo interessante: remova (ou ponha em comentrio) estas duas ltimas sobrecargas de operador adicionadas no PASSO 2, recompile e execute o cdigo. Voc deve achar que ele funciona exatamente do mesmo modo que funcionaria com as funes de operador que voc colocou em comentrios. Ento, o que est acontecendo? Voc pode descobrir usando o depurador. PASSO 5: Coloque um ponto de quebra em frente linha d3 = 5 + d2;, em _tmain (Figura 10.3).

Figura 10.3: Colocando um ponto de quebra em frente linha d3 = d1 + 5;. PASSO 6: Execute o programa pressionando F5. Quando o programa parar no ponto de quebra, pressione F11 para entrar na prxima funo a ser executada. Se espera que esta funo seja op_Addition, se enganar porque voc entrar no construtor Dbl (Figura 10.4). Pressione F10 at voltar para a linha d3 = 5 + d2;.

Figura 10.4: Resultado da depurao at o PASSO 6. Note que v = 5.0. 161

PASSO 7: Pressione novamente F11 e, agora sim, voc entrar na funo op_Addition. Por que voc teve uma chamada extra ao construtor Dbl? Porque o compilador v um inteiro (5) no cdigo, mas dispe apenas da verso op_Addition que recebe dois valores Dbl. Porm, como a estrutura Dbl tem um construtor que recebe um double, o compilador converte o inteiro para double e, ento, o construtor usado para criar um objeto Dbl temporrio que passado para op_Addition. Quando voc est na funo op_Addition, se examinar os argumentos na janela Locals (Figura 10.5), ver que eles so do tipo Dbl e que o argumento lhs tem valor igual a 5.0.

Figura 10.5: Depurador na funo op_Addition. Voc viu que, se fornecer para implementao dos seus tipos construtores apropriados, voc s vezes pode usar um operador sobrecarregado para realizar vrias converses diferentes.

10.2.3.

IMPLEMENTANDO OPERADORES LGICOS E A IGUALDADE

Lidamos at agora com operadores aritmticos. Na seqncia, vamos considerar os operadores lgicos e os de comparao. Como j vimos na Tabela 10.2, o C++ fornece um conjunto de operadores de comparao que so reapresentados na Tabela 10.3. Tabela 10.3: Operadores de comparao.
OPERADOR == != > >= < <= DESCRIO Igualdade Desigualdade Maior que Maior ou igual a Menor que Menor ou igual a

162

A implementao destes operadores simples e segue o modelo do operador adio do exerccio anterior.

10.2.3.1.

EXERCCIO: IMPLEMENTAO DO OPERADOR IGUALDADE

PASSO 1: Abra o projeto Overload e na estrutura Dbl, aps as implementaes das funes op_, coloque o cdigo abaixo:
//O operador == da estrutura Dbl-------------------------------------static bool op_Equality(Dbl lhs, Dbl rhs) { return lhs.val == rhs.val; }//-------------------------------------------------------------------

A funo segue o mesmo padro das que voc implementou para os operadores aritmticos. Ela um membro static do tipo Dbl, mas desta vez ela retorna um varivel booleana, exatamente como voc esperaria de um operador lgico, e toma sua deciso comparando a estrutura interna dos seus dois operandos. O QUE IGUALDADE? A deciso se voc deve implementar == ou != depende do tipo que estiver escrevendo e isto pode no ser uma deciso simples. Para algumas classes, a escolha bastante bvia. Por exemplo, um tipo Point tem como membro x e y. Neste caso, == compararia o membro x ou y de dois objetos Point e retornaria true se os valores fossem iguais. O que dizer sobre uma classe chamada Currency (moeda corrente) que tem um valor e um tipo de moeda? Voc pode ver que dois objetos Currency so iguais se ambos, valor e tipo de moeda, forem idnticos. Igualmente, voc pode dizer que dois objetos so idnticos se seus valores so o mesmo quando convertidos para a alguma moeda de referncia, por exemplo, dlar ou Euro. Os dois procedimentos so igualmente vlidos. Cabe a voc escolher um e document-lo. Adicionalmente, pode haver classes para as quais qualquer notao de igualdade artificial. Considere uma classe BankAccount. O que significaria igualdade para ela? Dois objetos BankAccount no podem ser idnticos porque eles devem ter nmeros de conta diferentes. Voc pode escolher algum critrio para testar a igualdade, ou decidir que a igualdade no pode ser aplicada a objetos BankAccount. Para finalizar, voc deve estar atento que o teste de igualdade pode causar problema para valores de pontos-flutuantes. Alguns valores podem requerer mais casas decimais do que tipos como float e double suportam. Assim, pode haver erros de arredondamento para mais ou para menos devidos escolha da ltima casa decimal. fortemente provvel que estes erros de arredondamento possam resultar em dois valores ligeiramente diferentes. Uma maneira de resolver este problema definir uma classe, como a Dbl, que faa compensaes para estas situaes, testando se a diferena entre dois valores cai dentro de uma dada tolerncia. Para fornecer um teste mais realstico, voc pode codificar o operador igualdade para Dbl do seguinte modo:
//O operador == da estrutura Dbl-------------------------------------static bool op_Equality(Dbl lhs, Dbl rhs) { if(Math::Abs(lhs.val - rhs.val) < 0.00001) return true; else

163

return false; }//-------------------------------------------------------------------

O mtodo Math::Abs um membro static da classe Math e retorna um valor absoluto. PASSO 2: Adicione o cdigo de teste abaixo funo _tmain para o operador igualdade.
if(d1 == d2) Console::WriteLine(S"\nd1 e d2 so iguais"); else Console::WriteLine(S"\nd1 e d2 so diferentes");

O resultado deste teste depende dos valores atribudos a d1 e d2. Ao compilar e executar seu programa, voc vai obter o resultado mostrado na Figura 10.6.

Figura 10.6: Sada atual do programa Overload. PASSO 3: Voc pode tomar um atalho para implementar o operador desigualdade (!=) usando a hiptese de que seu resultado deve ser o contrrio do obtido com == para os mesmos operandos. Assim, voc pode implementar o operador desigualdade em termos do de igualdade. Adicione o cdigo abaixo aps a sobrecarga para op_Equality:
//O operador != para a estrutura Dbl---------------------------------static bool op_Inequality(Dbl lhs, Dbl rhs) { return !(lhs == rhs);//Chama op_Equality }//-------------------------------------------------------------------

A funo compara seus dois argumentos usando o operador ==, que chama a funo op_Equality. Ento, op_Inequality aplica o operador NOT (!) para trocar um resultado true para false, e vice-versa. Neste caso, o uso do atalho no resulta em nenhuma economia de cdigo, mas se op_Equality tivesse que checar um nmero de membros de dados maior, o atalho j valeria a pena. Ele tambm ajuda se a estrutura muda porque voc s precisaria modificar a implementao de op_Equality. Os outros operadores lgicos (<, <=, > e >=) podem ser sobrecarregados de modo similar e voc pode fazer uso dos mesmos atalhos quando implementar pares opostos de operadores.

164

10.2.4.

EXERCCIO: IMPLEMENTANDO Equals

Voc j sabe que, no final das contas, todos os tipos no .NET so herdeiros da classe Object. Esta classe fornece vrias funes que todos os tipos .NET herdam e uma em particular relevante para nossa discusso sobre os operadores == e !=: Equals. O mtodo Object::Equals foi projetado com a inteno de fornecer aos tipos um modo de comparar contedo e no referncias. Esta a mesma tarefa que voc est realizando com a implementao do operador ==. Porm, uma das grandes atraes do .NET que os tipos gerenciados podem ser acessados sem mais ajustes por outras linguagens e as outras linguagens bem podem querer usar Equals para testar igualdade. O exerccio abaixo mostra como implementar Equals para a estrutura Dbl. PASSO 1: Abra o projeto Overload e, aps a funo-membro op_Inequality da estrutura Dbl, adicione o cdigo abaixo:
//O operador == para a estrutura Dbl---------------------------------bool Equals(Object *pOther) { Dbl *s = dynamic_cast<Dbl*>(pOther); if(s == 0) return false; return s->val == val; }//-------------------------------------------------------------------

Esta funo de operador mais complexa do que outras neste captulo. Assim, vamos observ-la linhaa-linha. A primeira linha declara a funo Equals para receber um Object* como nico argumento e retornar uma bool. importante que voc a declare exatamente deste modo; caso contrrio, ela no ir se sobrepor funo virtual herdada da classe-base Object. O uso de Object* para o tipo de argumento significa que voc pode passar um ponteiro para qualquer tipo gerenciado na funo. Assim, a primeira coisa a fazer assegurar-se de que o ponteiro passado de fato um Dbl*. Voc executa este teste usando um dynamic_cast, que um mecanismo introduzido pelo C++ que lhe permite realizar uma converso de tipos (cast) em tempo de execuo. Aqui a converso de Object* para Dbl*. Se a converso falhar se o ponteiro passado no for um Dbl* o resultado ser um ponteiro null. A funo checa esta possibilidade e retorna false se ela ocorrer. Se a dynamic_cast funcionar, voc sabe que tem um Dbl*; portanto, pode checar seu contedo para ver se ele o mesmo contedo do objeto. PASSO 2: A chamada do mtodo Equals no cdigo mais complicada do que usar os operadores que j encontramos por causa da necessidade de passar um ponteiro Object. Adicione o cdigo abaixo aps o seu teste do operador ==, na funo _tmain:
if(d1.Equals(__box(d2))) Console::WriteLine(S"\nd1 e d2 so iguais"); else Console::WriteLine(S"\nd1 e d2 so diferentes");

Este cdigo chama a funo Equals no objeto d1 para ver se d1 igual a d2. Equals precisa de um ponteiro para um Object. Assim, de alguma maneira, voc precisa obter um Object* representando d2. Voc consegue isto usando a palavra-chave __box. Voc aprendeu no captulo 9 como tipos de valor so to eficientes quanto tipos embutidos, mas eles tambm podem ser usados como tipos de referncia quando houver necessidade. O tratamento de um tipo de valor como um tipo de referncia feito por encaixotamento (boxing) do mesmo. Este 165

processo de encaixotamento discutido com mais detalhes no captulo 25, mas basicamente ele envolve o encapsulamento de um objeto em torno de um tipo de valor. A envoltura do objeto um tipo de referncia e atua como uma caixa contendo o valor. O processo pode parece complicado, mas voc realmente est fornecendo Equals para uso por outras linguagens .NET. Em cdigo C++ gerenciado, voc deve usar ==. DICA: Se voc implementar Equals para um tipo, fortemente recomendado que tambm implemente o operador ==.

10.2.5.

EXERCCIO: IMPLEMENTANDO ATRIBUIES

A funo op_Assign fornece um modo para sobrecarregar o operador de atribuio (=). Por que voc faria isto? Suponha que tenha uma classe que contm uma data e um tempo e que voc quer ter certeza de que estas variveis-membros suportam a data e o tempo quando o objeto criado ou atribudo a outro. Se voc no implementar op_Assign, os valores existentes sero copiados diretamente do lado direito sobre o do lado esquerdo. Implementando o operador, voc pode personalizar a operao de cpia e garantir que a data e o tempo sero atualizados quando a atribuio for realizada. O exerccio abaixo mostra como implementar op_Assign para a estrutura Dbl. PASSO 1: Abra o projeto Overload e adicione trs membros de dados para representar o tempo. Na estrutura Dbl, acrescente a linha de cdigo abaixo, aps a varivel val:
double hr, min, sec;//Criao ou atribuio de tempo

Alm disso, altere o construtor de modo que os campos do tempo sejam inicializados para o tempo atual. O novo construtor deve ser:
Dbl(double v)//------------------------------------------------------{ val = v; //Cria nova data e campos do tempo DateTime dt = DateTime::Now; hr = dt.get_Hour(); min = dt.get_Minute(); sec = dt.get_Second(); }//-------------------------------------------------------------------

O construtor usa o objeto System::DateTime para obter o tempo atual. Este objeto inicializado por uma chamada propriedade static DateTime::Now. Ento, os campos individuais so salvos nos membros apropriados do objeto. PASSO 2: Agora adicione a funo de atribuio para o objeto Dbl.
//O operador = para a estrutura Dbl----------------------------------static Dbl op_Assign(Dbl &lhs, const Dbl &rhs) { //Copia sobre o valor lhs.val = rhs.val; //Cria nova data e campos de tempo DateTime dt = DateTime::Now; lhs.hr = dt.get_Hour(); lhs.min = dt.get_Minute(); lhs.sec = dt.get_Second(); return lhs;

166

}//-------------------------------------------------------------------

Voc notar vrios detalhes sobre esta funo. O primeiro o fato de que ela usa referncias para os argumentos. Passando valores por referncia permitimos que se possam fazer cpias. Pelo contrrio, o nome que voc usa na funo um vnculo de volta para o argumento que foi passado. A passagem de valores atravs de referncias essencial quando voc pensa sobre como a atribuio tem que funcionar. Esperamos que o cdigo de atribuio deva ser algo como:
obj2 = obj1;

Voc deseja que obj2 contenha os valores do obj1 quando a funo retornar. Se voc passar os argumentos por valor sem o & quaisquer mudanas em lhs sero perdidas porque esta varivel local deixa de existir ao trmino da funo. Quando tomamos como referncias os argumentos para a funo, fornecemos um vnculo de volta para o valor que foi passado. Assim, as mudanas feitas em lhs persistiro. O argumento rhs passado como uma referncia const porque voc no quer mudanas no lado direito e a palavra-chave const garante isto. O membro val sobreposto e, ento, um novo conjunto de membros de tempo gerado de modo que o objeto refletir o tempo a ele atribudo, no o tempo original com o qual foi criado. PASSO 3: Para testar as mudanas, voc tem que organizar algum intervalo de tempo entre a criao do objeto e a atribuio que lhe feita de modo que possa ver que estes tempos so diferentes. O modo mais fcil de conseguir isto usar o mtodo static Sleep da classe System::Threading::Thread. Assim, adicione funo _tmain o cdigo abaixo:
//Cria dois objetos Dbl one(0.0); Dbl two(10.0); //5 segundos parado System::Threading::Thread::Sleep(5000); //Fazer uma atribuio one = two;

PASSO 4: O melhor modo de ver o resultado executar o cdigo pelo depurador. Coloque um ponto de quebra na linha onde o objeto one criado. PASSO 5: Execute o programa pressionado F5 e, quando a execuo alcanar o ponto de quebra, pressione F10 uma vez para criar o objeto one. Na janela Locals, clique no sinal + de one para expandi-lo (Figura 10.7). Voc ver os valores de tempo atribudos ao objeto. PASSO 6: Agora, pressione F10 at alcanar a chamada funo Sleep. Quando ultrapassar a atribuio, voc ver que os membros de dados no objeto one mudam: val recebe o valor do membro val de two, enquanto os tempos refletem o instante em que a atribuio foi feita (Figura 10.8).

167

Figura 10.7: Executando o programa Overload pelo depurador.

Figura 10.8: Momento em que o objeto two atribudo a one.

10.2.6.

EXERCCIO: IMPLEMENTANDO INCREMENTO E DECREMENTO

Como exemplo final, o exerccio abaixo mostra como sobrecarregar os operadores de incremento (++) e decremento ( ). Como voc viu no captulo 3, os operadores embutidos ++ e so usados para 168

incrementar e decrementar valores de variveis numricas. Voc pode sobrecarreg-los para seus prprios tipos numricos, mas tambm pode sobrecarreg-los para fornecer outra funcionalidade. Por exemplo, suponha que voc tenha um tipo Date que suporta valores de dia, ms e ano. Voc pode implementar ++ e para adicionar ou subtrair um dia da data atual, ajustando o ms e o ano apropriadamente. O exerccio abaixo mostra como implementar o operador ++ para o tipo Dbl. PASSO 1: Abra o projeto Overload e, na estrutura Dbl, logo aps a funo op_Assign, adicione o cdigo abaixo:
//O operador ++ para a estrutura Dbl---------------------------------static Dbl op_Increment(Dbl d) { d.val += 1; return d; }//-------------------------------------------------------------------

O operador ++ implementado pela funo op_Increment; o operador implementado por op_Decrement. O nico argumento representa o valor a ser incrementado. Assim, 1 adicionado ao seu valor e, ento, uma cpia retornada. PASSO 2: Use o operador no cdigo:
//Cria um objeto Dbl Dbl d5(1.0); Console::Write(S"\nO valor inicial de d5 "); Console::WriteLine(d5.getVal()); //Incrementa-o d5++; Console::Write(S"\nO valor atual de d5 "); Console::WriteLine(d5.getVal());

A Figura 10.9 mostra a sada atual do programa Overload. Note que, embora a funo op_Increment retorne um Dbl, o valor de retorno no est sendo usado neste exemplo e voc est executando a funo apenas para obter seu efeito colateral de incrementar o valor.

Figura 10.9: Sada atual do programa Overload. Uma vez implementado o operador incremento, voc facilmente implementar o operador decremento.

169

NOTA: Voc viu no captulo 3 que h duas formas de incrementar e decrementar operadores: a forma prefixada que coloca o operador antes do operando (++x) e a ps-fixada (x++). Quando sobrecarregar tipos gerenciados, no possvel distinguir entre operadores prefixados e ps-fixados. Assim, a mesma funo, op_Increment ou op_Decrement, chamada para ambos. Na sobrecarga de operador C++ tradicional, voc pode sobrecarregar separadamente prefixados e ps-fixados.

10.2.7.

SOBRECARREGANDO TIPOS DE REFERNCIA

Voc pode sobrecarregar operadores para tipos de referncia do mesmo modo que faz para tipos de valor, mas precisa ler com ateno os assuntos abordados nesta seo.

10.2.7.1.

IMPLEMENTANDO OPERADORES SOBRECARREGADOS PARA TIPOS DE REFERNCIA

Voc j sabe que os tipos de referncia so acessados usando ponteiros, o que significa que os argumentos para as funes de operador para tipos de referncia sempre tm que ser ponteiros. Assim, se voc for implementar op_Addition para um tipo de referncia, tem que codific-la como no exemplo abaixo:
//O operador + pp um tipo de referncia static MyRef* op_Addition(MyRef* pLhs, MyRef* pRhs) { MyRef *result = new MyRef(pLhs->val + pRhs->val); return result; }

10.2.8.

CHAMANDO REFERNCIA

OPERADORES

SOBRECARREGADOS

PARA

TIPOS

DE

Voc no pode chamar operadores sobrecarregados implicitamente para tipos de referncia. Assim, voc tem que chamar diretamente a funo sobrecarregada, como no exemplo:
//Isto no funciona MyRef *r3 = one + two; //Isto funciona MyRef *r3 = MyRef::op_Addition(one, two);

NOTA: Esta limitao pode ser removida em verses mais atuais do Visual C++ .NET.

10.3.

DIRETRIZES PARA FORNECIMENTO DE OPERADORES SOBRECARREGADOS

A diretriz mais importante : Os operadores sobrecarregados devem intuitivamente fazer sentido para uma classe. Por exemplo, se voc tem uma classe String, usar + para concatenar duas strings bastante intuitivo. Voc concorda que, se usarmos como em s2s1, estamos querendo dizer procure s1 dentro de s2 e, se encontrar, remova-a? Mas o que poderia significar o operador * quando aplicado entre duas strings? Nada de bvio, e voc somente causaria confuso aos usurios se fornecesse tal sobrecarga. Assim, esteja seguro de que os operadores que voc deseja fornecer para seus tipos so os que as pessoas esperam encontrar.

170

A segunda diretriz : O uso do operador deve ser consistente. Em outras palavras, se voc sobrecarregar ==, tambm cuide em sobrecarregar !=. O mesmo se aplica a < e >, ++ e --, e assim por diante. NOTA: O compilador C# obriga a sobrecarga consistente e ele no lhe permite sobrecarregar um operador de um par sem tambm implementar o outro. Porm, em C++, cabe a voc ser consistente. A terceira diretriz : No sobrecarregue operadores obscuros ou operadores que mudem a semntica da linguagem. Operadores como a vrgula so obscuros e poucas pessoas sabem como eles funcionam. Assim, no uma boa idia sobrecarreg-los. Outros operadores, como os lgicos AND e OR (&& e ||), podem causar problemas. Nos captulos anteriores, voc aprendeu sobre a declarao if e como expresses unidas por && e || s so avaliadas se necessrio. Como resultado, algumas expresses em uma declarao if podem nunca ser avaliadas. Se voc sobrecarregar os operadores AND e OR, a expresso inteira ter que ser avaliada, o que muda o modo como o if funciona.

10.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 10

A Tabela 10.4 mostra um resumo deste captulo. Tabela 10.4: Resumo do captulo 9.
PARA Sobrecarregar operadores para tipos de valor Implementar teste de igualdade Sobrecarregar operadores para tipos de referncia FAA ISTO Implemente a funo op_ apropriada como um membro static do tipo. Sobrecarregue == e !=, e fornea uma sobrecarga de Equals para o benefcio de outras linguagens .NET. Use ponteiros para argumento e retorne tipos.

171

11.

CAPTULO 11: MANIPULANDO EXCEES

Neste captulo, voc aprender O que so excees; Os diferentes tipos de excees que podem ser usados em C++ gerenciado; Como gerar excees; Como manipular excees; Como criar suas prprias classes de exceo.

Agora que voc sabe como construir classes e tipos de valor, e como utiliz-los em programao, este captulo lhe introduz em manipulao de excees, um modo poderoso de controlar erros dentro dos programas C++.

11.1.

O QUE SO EXCEES?

Excees so um mecanismo de manipulao de erros empregado extensivamente em C++ e em vrias outras modernas linguagens de programao. Tradicionalmente, erro e informao de estado so passados atravs do uso de valores de retorno e parmetros de funo, como no exemplo abaixo:
//Passa estado de volta como valor de retorno bool bOK = doSomething(); //Passa estado de volta em um parmetro int status; doSomething(arg1, arg2, &status);

Embora este seja um modo experimentado e testado de passar informaes de estado, ele carrega de vrias desvantagens: Voc no pode forar o programador a fazer alguma coisa a respeito do erro; O programador nem mesmo tem como checar o cdigo do erro; Caso se aprofunde em uma srie de chamadas aninhadas, voc tem que fixar e liberar cada sinalizador de estado manualmente; muito difcil obter de volta informaes de estado de alguma coisa que no receba argumentos ou retorne um valor.

As excees fornecem um mecanismo alternativo para manipulao de erros, que lhe dar trs vantagens principais sobre a manipulao tradicional: As excees no podem ser ignoradas. Se uma exceo no for manipulada em algum ponto, o programa terminar, o que tornam as excees apropriadas para controle de erros crticos. As excees no tm que ser manipuladas no ponto onde ocorrem. Um erro pode ocorrer em muitos nveis de profundidade numa chamada de funo em um programa e pode no haver maneira de resolver o problema no ponto onde ocorre o erro. As excees lhe permitem manipular o erro em qualquer lugar para cima na pilha de chamadas (veja a subseo A PILHA DE CHAMADAS E AS EXCEES abaixo). As excees fornecem um modo til para sinalizar erros onde um valor de retorno no pode ser usado. H dois lugares particulares em C++ onde os valores de retorno no podem ser usados: os construtores no os usam e os operadores sobrecarregados no podem ter seu valor de retorno

172

sobrecarregado para uso com erro e informaes de estado. As excees so particularmente teis nestas situaes porque elas lhe permitem evitar o mecanismo normal de valor retornado. A PILHA DE CHAMADAS E AS EXCEES Em qualquer ponto em um programa, a pilha de chamadas coleta informaes sobre as funes que foram chamadas para transport-las ao ponto atual. A pilha de chamadas usada em trs situaes principais: durante a execuo para controlar chamada e retorno de funes, no depurador e durante a manipulao de excees. O manipulador para uma exceo pode ocorrer na rotina na qual a exceo foi lanada na pilha. Tambm pode ocorrer em qualquer rotina acima na pilha de chamadas e, em tempo de execuo, cada rotina na pilha de chamadas checada para verificao se ela implementa um manipulador apropriado. Se um manipulador apropriado no for encontrado at o topo da pilha, o programa termina. No .NET, as excees tm uma outra vantagem significativa: elas podem ser usadas atravs das linguagens. Como as excees so parte do .NET Framework subjacente, possvel lanar uma exceo em cdigo C++ e peg-la em Microsoft Visual Basic .NET, algo que no possvel fora do ambiente .NET. Como acontece com qualquer outro mecanismo de erro, voc tender a ativar excees causando erros no seu cdigo. Porm, voc tambm pode gerar suas prprias excees, voc ver em breve.

11.1.1.

COMO AS EXCEES FUNCIONAM?

Quando uma condio de erro ocorre, o programador pode gerar uma exceo usando a palavra-chave throw e a exceo etiquetada com um pedao de dados que identifica exatamente o que aconteceu. Neste ponto, a exceo normal pra e o cdigo de manipulao de exceo construdo no programa comea a procurar um manipulador. Procura na rotina atualmente em execuo e, se encontrar um manipulador apropriado, este executado e o programa continua. Se nenhum manipulador for encontrado na rotina atual, o cdigo de manipulao de exceo se desloca um nvel para cima na pilha de chamadas e checa se h um manipulador apropriado. Este processo continua at que um manipulador seja encontrado ou que o nvel do topo na pilha de chamadas a funo _tmain seja alcanada. Se nada for encontrado at este nvel, o programa termina com uma mensagem de exceo no manipulada.

11.1.1.1.

EXERCCIO: ERRO DE DIVISO POR ZERO

Eis um exemplo de como uma exceo no manipulada lhe aparece. Provavelmente, voc j a viu diversas vezes! Implemente o cdigo abaixo:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine("Teste de exceo"); int top = 3; int bottom = 0; int result = top / bottom; Console::Write("O resultado "); Console::WriteLine(result);

173

return 0; }//***********************************************************************

fcil ver que este cdigo causar um erro de diviso por zero. Assim, quando executado, voc ver a sada mostrada na Figura 11.1.

Figura 11.1: Sada do programa DivisaoPorZero. Voc pode ver que o resultado da diviso por zero a gerao de uma exceo. Como no manipulamos esta exceo no cdigo, o programa termina e a sada final nunca faz esta manipulao na tela. Note a forma da mensagem padronizada: ela lhe informa o que aconteceu (um erro System.DivideByZero), apresenta uma mensagem de erro e, ento, lhe informa o nvel da pilha onde o erro ocorreu (na funo _tmain, na linha 17 do arquivo DivisaoPorZero.cpp). System.DivideByZero denota o tipo de objeto que foi passado na exceo. Muitas classes de exceo so fornecidas no namespace System e tambm provvel que voc queira fazer a sua prpria classe de exceo, tendo como classe-base System::Exception, como veremos mais adiante.

11.1.2.

TIPOS DE EXCEO

Manipular exceo um pouco complicado pois voc pode encontrar trs diferentes tipos de manipulao de exceo, quando usa C++ gerenciado: excees do C++ tradicional, excees do C++ gerenciado e manipulao de excees estruturadas do Microsoft Windows (SEH, Structured Exception Handling). As excees do C++ tradicional formam a base de toda a manipulao de excees C++. O C++ gerenciado adiciona a habilidade para usar tipos gerenciados (por exemplo, a classe __gc e os tipos __value) em excees e voc pode mistur-las com as excees tradicionais. O C++ gerenciado tambm estende a manipulao de excees adicionando o conceito de um bloco __finally, que discutiremos na seo O BLOCO __finally, ainda neste captulo. A terceira ordem de manipulao de excees que voc pode encontrar a SEH, uma forma de manipulao de excees construda nos sistemas operacionais Windows que independente do C++. No falaremos mais nada sobre SEH, exceto para informar que voc pode interagir com ela do C++.

11.2.

LANANDO EXCEES

Comearemos nossa explorao das excees discutindo como ger-las, ou lan-las (throw). Voc terminar gerando excees mais por acidente do que por planejamento, mas precisa saber como gerar suas prprias excees quando ocorrer erros no seu aplicativo.

174

O QUE VOC PODE LANAR? O C++ tradicional lhe permite anexar qualquer tipo de objeto a uma exceo. Assim, voc pode usar tipos embutidos (tais como int e double) bem como estruturas e objetos. Se voc lana objetos em C++, usualmente os lana e os captura por referncia. O C++ gerenciado estende esta habilidade para lhe permitir lanar e capturar ponteiros para manipular tipos e voc provavelmente estar usando tipos gerenciados quando escrever cdigo .NET. Este captulo lida quase que exclusivamente com lanamento e captura de tipos gerenciados, mas esteja atento a outros tipos de dados que so usados fora do mundo C++. Como voc saber o que lanar? H um grande nmero de classes de exceo como parte do namespace System, todas derivadas de Exception. Algumas das que voc encontrar com mais freqncia so listadas na Tabela 11.1. Voc deve se habilitar a encontrar a classe de exceo para atender aos seus propsitos e se no conseguir, sempre possvel derivar suas prprias classes de exceo de System::Exception. Tabela 11.1: Classes de exceo mais comuns do namespace System.
CLASSE DE EXCEO System::ApplicationException DESCRIO Lanada quando ocorre um erro de aplicativo nofatal. System::ArgumentException Lanada quando um dos argumentos para uma funo invlido. As subclasses incluem System::ArgumentNullException e System::ArgumentOutOfRangeException. System::ArithmeticException Lanada para indicar um erro em uma operao aritmtica, de casting ou de converso. As subclasses incluem System::DivideByZero e System::OverflowException. System::Exception A classe-base de todos os tipos de exceo. System::IndexOutOfRangeException Lanada quando um ndice de matriz est fora do intervalo. System::InvalidCastException Lanada quando uma converso invlida detectada. System::MemberAccessException Lanada quando feita uma tentativa para acessar dinamicamente um membro que no existe. As subclasses incluem System::MissingFieldException e System::MissingMethodException. System::NotSupportedException Lanada quando um mtodo que invocado no suportado. System::NullReferenceException Lanada quando feita uma tentativa de desreferenciar uma referncia null. System::OutOfMemoryException Lanada quando a memria no pode ser alocada. System::SystemException A classe-base para excees que se espera que o manipule. As subclasses incluem usurio ArgumentException ArithmeticException. System::TypeLoadException Lanada quando o CLR (Common Language Runtime, tempo de execuo da linguagem comum) no encontra um assembly ou um tipo dentro de um assembly, ou no pode carregar o tipo. As subclasses incluem System::DllNotFoundException.

175

11.2.1.

EXERCCIO: GERANDO UMA EXCEO

O exerccio abaixo mostrar como gerar uma exceo. Na prxima seo, voc ver como capturar e processar a exceo. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Throwing. PASSO 2: No arquivo-fonte Throwing.cpp, em local apropriado, defina a funo abaixo:
void func(int a)//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { if(a <= 0) throw new System::ArgumentException(S"Aaargh!"); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Esta funo simples recebe um argumento e, se seu valor for menor ou igual a 0, ela lana uma exceo. Neste caso, estamos criando um novo objeto System::ArgumentException, inicializando-o com uma string e, ento, lanando-o. PASSO 3: Adicione o cdigo abaixo na funo _tmain para testar a exceo:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Teste de lanamento de exceo"); Console::WriteLine(S"Chamando a funo com a = 3"); func(3); Console::WriteLine(S"Chamando a funo com a = 0"); func(0); Console::WriteLine(S"Fim"); return 0; }//***********************************************************************

O cdigo chama a funo duas vezes, uma com um valor vlido e outra com 0, o que deve ativar a exceo. PASSO 4: Se estiver usando o Visual C++ 7.1, voc precisa fazer uma mudana adicional antes de construir o programa (se estiver usando a verso 7, no precisar fazer esta mudana). Use o Solution Explorer para abaixo o arquivo AssemblyInfo.cpp e adicione as linhas de cdigo abaixo, imediatamente aps as duas linhas using namespace:
using namespace System::Diagnostics; [assembly:Debuggable(true, true)];

Estas linhas asseguram que todas as informaes so geradas quando a exceo capturada. NOTA: A Microsoft mudou a forma como as informaes de exceo so geradas entre as verses 7 e 7.1 do Visual C++. Por padro, a verso 7.1 no fornece informaes completas sobre onde ocorre a exceo, por razes de eficincia. Voc pode garantir que as informaes completas sejam geradas adicionando o atributo Debuggable ao arquivo-fonte AssemblyInfo.cpp, como mostrado neste PASSO 4. PASSO 5: Compile e execute o cdigo. Voc deve obter a sada mostrada na Figura 11.2. O programa chamou a funo uma vez sem incidente, mas a segunda chamada ativou uma exceo. Como antes, voc recebe uma mensagem e uma lista dos nveis da pilha. Desta vez, a mensagem a string usada para inicializar o objeto de exceo e h dois nveis da pilha, mostrando que a exceo foi ativada na linha 14 na funo func, que foi chamada da funo _tmain, na linha 25. 176

Figura 11.2: Sada atual do programa Throwing. NOTA: O nmero exato da linha que lhe informado na lista da pilha depender de voc ter ou no digitado o texto do cdigo exatamente como fizemos.

11.3.

MANIPULANDO EXCEES

Agora que voc j viu como gerar excees, vamos aprender a manipul-las. USANDO A CONSTRUO try E catch As excees so capturadas e processadas usando a construo try e catch, que tem a seguinte forma:
try//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { //O cdigo que pode falhar }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ catch(TypeOne *pOne)//++++++++++++++++++++++++++++++++++++++++++++++++++++ { //Manipule esta exceo }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ catch(TypeTwo *pTwo)//++++++++++++++++++++++++++++++++++++++++++++++++++++ { //Manipule esta exceo }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

O cdigo que voc desconfia que pode falhar colocado em um bloco try, seguido por uma ou mais manipuladores na forma de blocos catch. Cada bloco catch se parece um com a definio de uma pequena funo, com a palavra-chave catch seguida por um tipo entre parnteses, que representa o tipo que ser capturado e processado pelo bloco catch. No cdigo acima, o primeiro bloco catch manipular excees ativadas com um tipo TypeOne, enquanto o segundo bloco manipular as ativadas com um tipo TypeTwo. NOTA: Os blocos try e catch formam uma nica construo. Voc no ter um bloco try sem no mnimo um bloco catch; tambm no pode ter um bloco catch sem um bloco try. E tambm no pode colocar nada entre eles. Voc pode encadear tantos blocos catch juntos quantos forem os tipos de exceo a capturar, contanto que tenha pelo menos um.

177

11.3.1.

EXERCCIO: FUNDAMENTOS DA MANIPULAO DE EXCEES

Neste exerccio, voc ver os fundamentos da manipulao de excees, usando o exemplo acima como base. PASSO 1: Abre o projeto Throwing. PASSO 2: Modifique, como abaixo, a funo _tmain:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Teste de lanamento de exceo"); try { int n = 3; Console::WriteLine(S"Chamando a funo com n = 3"); func(n); Console::WriteLine(S"Chamando a funo com n = 0"); n = 0; func(n); } catch(System::ArgumentException *pex) { Console::WriteLine(S"A exceo foi {0}", pex); } Console::WriteLine(S"Fim"); return 0; }//***********************************************************************

A chamada funo func est em um bloco try, que seguido por um nico bloco catch. Quando a segunda chamada funo falha, o mecanismo de manipulao de excees entra em ao. Ele no pode achar um manipulador na funo onde se originou o erro. Assim, ele sobe um nvel na pilha de chamadas e sai do bloco try. Neste ponto, a rotina de execuo procura um manipulador. Como parte deste processo de busca, ela coloca a pilha do programa de volta ao ponto onde estava no incio do bloco try. Em outras palavras, desmancha a pilha, i.e., destri quaisquer variveis que tenham sido criadas na pilha dentro do bloco try. Assim, voc no as utiliza no bloco catch. Voc precisa ter em mente isto quando escrever manipuladores de exceo: declare quaisquer variveis necessrias em catch fora do bloco try correspondente. Quando a pilha desmanchada em try, a rotina de execuo entra nos blocos catch associados com este bloco try para verificar se h algum deles que tenha um tipo de argumento que case com o que foi lanado na pilha. Se no houver um bloco catch apropriado, a rotina de execuo tentar se deslocar para outro nvel mais acima na pilha de chamada e, ento, falharia e terminaria o programa. PASSO 3: Execute o cdigo. A Figura 11.3 mostra a atual sada do programa Throwing. A segunda chamada funo gerou uma exceo que foi capturada pelo bloco catch, que imprimiu no console a string A exceo foi: mais o detalhe da exceo. Em contraste com o que aconteceu no exerccio anterior, agora, a mensagem Fim impressa. Isto ilustra um importante ponto sobre manipulao de excees: Uma vez um bloco catch tenha sido executado, a execuo do programa continua aps o bloco catch como se nada tivesse acontecido. Se houver quaisquer outros blocos catch encadeados, eles sero ignorados. PASSO 4: Experimente mudar a segunda chamada de modo que ela passe um valor positivo. Voc ver que o bloco catch no executado. Se um bloco try finalizar sem ocorrer qualquer exceo, a execuo salta todos os blocos catch associados com o bloco try. 178

Figura 11.3: Sada atual do programa Throwing.

11.3.2.

PERSONALIZANDO A MANIPULAO DE EXCEES

A impresso padro do objeto de exceo consiste no tipo + mensagem + lista da pilha que voc percorreu quando a exceo no era manipulada. Voc pode usar as propriedades da classe Exception para controlar o que impresso, como indicado na Tabela 11.2. Tabela 11.2: Propriedades e descrio da classe Exception.
PROPRIEDADES DA System::Exception Message StackTrace Source DESCRIO Retorna uma string contendo a mensagem associada com a exceo. Retorna a string contendo detalhes da lista da pilha. Retorna uma string contendo o nome do objeto ou aplicativo que provocou o erro. Por padro, este o nome do assembly.

Se voc alterar a declarao de WriteLine no bloco catch para


catch(System::ArgumentException *pex) { Console::WriteLine(S"A exceo foi: {0}", pex->Message); }

ver uma sada como a mostrada na Figura 11.4.

Figura 11.4: Sada do programa Throwing para o bloco catch exibindo apenas a mensagem associada com a exceo. De modo similar, voc pode usar StackTrace para reter e imprimir as informaes da lista da pilha. 179

11.3.3.

USANDO A HIERARQUIA DE EXCEO

As classes de exceo formam uma hierarquia baseada em System::Exception e voc pode usar esta hierarquia para simplificar sua manipulao de excees. Como exemplo, considere System::ArithmeticException, que herdeira de System::Exception e tem subclasses que incluem System::DivideByZeroException e System::OverflowException. Observe o cdigo abaixo:
try { //Fazer alguma operao aritmtica } catch(System::ArithmeticException *pex) { //Manipula esta exceo } catch(System::DivideByZeroException *pex) { //Manipula esta exceo }

Suponha que uma DivideByZeroException seja lanada. Voc espera captur-la pelo segundo bloco catch, mas, na verdade, ela ser capturada pelo primeiro. Isto porque, de acordo com a hierarquia hereditria, uma DivideByZeroException uma ArithmeticException. Portanto, casa com o tipo do primeiro bloco catch. Para obter o comportamento que espera quando utiliza mais de um bloco catch, voc precisa classificar os blocos catch do mais especfico para o mais geral. DICA: O compilador lhe mostrar a advertncia C4286 se voc obtiver os blocos catch na ordem errada. Este alerta funciona tanto no cdigo gerenciado quanto no no gerenciado. Assim, se quiser capturar todas as excees aritmticas, voc pode colocar um manipulador para ArithmeticException e todas as excees das classes-derivadas sero capturadas. Em um caso mais geral, voc simplesmente adiciona um manipulador para Exception e todas as excees sero capturadas.

11.3.4.

EXERCCIO: USANDO EXCEES COM CONSTRUTORES

Na seo O QUE SO EXCEES?, neste captulo, mencionamos que uma das vantagens das excees que elas lhe permitem sinalizar um erro onde no h modo algum de retornar um valor. Elas so muito teis para informar sobre erros em construtores, que, como sabemos, no tem um valor de retorno. Neste exerccio, voc ver como definir uma classe simples que usa uma exceo para informar sobre erros do seu construtor e tambm ver como checar excees quando criar objetos deste tipo. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado CtorTest. PASSO 2: No arquivo-fonte CtorTest.cpp, aps a declarao using namespace System;, adicione a seguinte definio de classe:
//DEFINIO DA CLASSE Test************************************************ __gc class Test { String *pv; public:

180

Test(String *pval)//++++++++++++++++++++++++++++++++++++++++++++++++++ { if(pval == 0 || pval == S"") throw new System::ArgumentException(S"Argumento null ou vazio"); else pval = pv; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ };//**********************************************************************

A palavra-chave __gc torna a classe Test gerenciada e esta classe gerenciada tem um nico membro de dados, um ponteiro para uma String gerenciada. Em tempo de construo, este ponteiro no deve ser null ou apontar para uma string em branco. Assim, o construtor checa o ponteiro e lana uma exceo se o teste falhar. Se o ponteiro passar no teste, a construo continua. PASSO 3: Na funo _tmain, experimente criar um objeto como este:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Excees em construtores"); //Cria um ponteiro null para testar a manipulao de exceo. String *ps = 0; Test *pt = 0; //Tenta criar um objeto try { pt = new Test(ps); } catch(System::ArgumentException *pex) { Console::WriteLine(S"Exceo: {0}", pex->Message); } Console::WriteLine(S"Finalizada a construo do objeto"); return 0; }//***********************************************************************

Construa e execute o programa. Note que a chamada ao operador new feita dentro do bloco try. Se alguma coisa estiver errada com o ponteiro String (como no exemplo), o construtor Test lanar uma exceo que ser captada pelo bloco catch. Experimente modificar a declarao da string ps de modo que ela aponte para uma string em branco (inicialize-a com S). Depois, experimente uma string no-nula, para checar se a exceo est funcionando corretamente.

11.3.5.

ANINHANDO E RELANANDO EXCEES

Agora que voc viu como usar a construo try e catch, vamos utiliz-lo em aplicaes mais avanadas. A primeira destas consiste em aninhamento e relanamento de excees. Como o nome indica, aninhar excees significa incluir uma construo try e catch dentro de outra, o que pode fornecer um modo til para manipular condies de erro. A coisa funciona como no exemplo abaixo:
try//Bloco try externo { try//Bloco try interno

181

{ //Fazer algo } catch(SomeException *pex) { Console::WriteLine(S"Exceo: {0}", pex->Message); } } catch(OtherException *pex) { Console::WriteLine(S"Exceo: {0}", pex->Message); }

Se ocorrer uma exceo dentro de um bloco try interno que seja do tipo SomeException*, ela ser manipulada pelo bloco catch interno e a execuo continuar aps o fim do bloco catch interno como usual. O bloco catch externo no ser executado neste caso porque o erro j ter sido adequadamente manipulado. Se ocorrer uma exceo dentro do bloco try interno que seja do tipo OtherException*, ela no ser manipulada pelo bloco interno catch, mas passada para a construo try e catch externa, onde ser processada pelo bloco catch externo. NOTA: Voc pode aninhar construes try e catch at vrios nveis, mas no usual se aprofundar mais de dois nveis porque isto pode complicar a estrutura do cdigo. Relanar uma exceo significa manipular uma exceo em um bloco catch e, ento, lan-la novamente de modo que ela possa ser manipulada em algum outro lugar.

11.3.5.1.

EXERCCIO: RELANANDO EXCEES

Neste exerccio, voc ver como lanar uma exceo e relan-la. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Rethrow. PASSO 2: No arquivo-fonte Rethrow.cpp, aps a declarao using namespace System;, adicione a seguinte definio de funo:
void func(int a)//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { try { if(a <= 0) throw new System::ArgumentException(S"Aaargh!"); } catch(System::ArgumentException *pex) { Console::WriteLine(S"Exceo capturada em func()"); } }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Este funo basicamente a mesma funo simples que voc encontrou no comeo do captulo. Ela lana uma System::ArgumentException quando lhe passado um argumento negativo. A diferena aqui que a exceo est sendo capturada dentro da funo. PASSO 3: Digite o cdigo abaixo na funo _tmain:
//FUNO PRINCIPAL******************************************************** int _tmain() {

182

Console::WriteLine(S"Teste de lanamento de excees"); try { int n = 0; Console::WriteLine(S"Chamando a funo com n = 0"); func(n); } catch(System::ArgumentException *pex) { Console::WriteLine(S"Exceo capturada em _tmain()"); } Console::WriteLine(S"Fim"); return 0; }//***********************************************************************

A Figura 11.5 mostra que, se executar o cdigo, voc ver que a exceo capturada localmente em func e o bloco catch em _tmain no executado.

Figura 11.5: Sada atual do programa Rethrow mostrando captura local da exceo. PASSO 4: Modifique a definio de func de modo que ela relance a exceo depois que a manipular:
void func(int a)//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { try { if(a <= 0) throw new System::ArgumentException(S"Aaargh!"); } catch(System::ArgumentException *pex) { Console::WriteLine(S"Exceo capturada em func()"); throw;//Relanamento da exceo } }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

O uso de throw sem um argumento equivale a relanar a exceo atual e ela pode ser usada e ela pode ser usada deste modo somente dentro de um bloco catch. Neste ponto, a rotina de execuo passa a procurar outro manipulador, o que significa subir nvel na pilha de chamadas para a funo _tmain, onde a exceo capturada uma segunda vez. Se executar o cdigo agora, voc ver o console mostrado na Figura 11.6 com as impresses das mensagens emitidas de func e de _tmain.

183

Figura 11.6: Sada atual do programa Rethrow. Note que voc no tem que relanar a mesma exceo. prtica bastante habitual lanarmos um tipo de exceo, fazermos sua manipulao e, ento, relanarmos uma exceo de outro tipo. Voc ver exemplo disto na seo onde cria seus prprios tipos de exceo, mais adiante, neste captulo.

11.3.6.

EXERCCIO: O BLOCO __finally

O C++ gerenciado adiciona uma nova construo manipulao de excees do C++ tradicional, o bloco __finally. O propsito deste bloco lhe permitir fazer limpezas depois de uma exceo ter ocorrido e o exerccio a seguir mostra como isto funciona. PASSO 1: Abra o projeto Rethrow. PASSO 2: Modifique a funo _tmain com o cdigo abaixo:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Teste de lanamento de excees"); try { int n = 3; Console::WriteLine(S"Chamando a funo com n = 3"); func(n); Console::WriteLine(S"Chamando a funo com n = 0"); n = 0; func(n); } catch(System::ArgumentException *pex) { Console::WriteLine(S"A exceo foi {0}", pex); } __finally { Console::WriteLine(S"Este o bloco __finally"); } Console::WriteLine(S"Fim"); return 0; }//***********************************************************************

A Figura 11.7 mostra a sada atual do programa Rethrow. Note que o bloco __finally executado aps o bloco catch. PASSO 3: Modifique a funo _tmain de modo que a segunda chamada no cause uma exceo, chamando o valor ou isolando a declarao como comentrio. Quando voc executar o programa novamente, ver que o bloco __finally ainda executado, embora no haja erro. 184

Figura 11.7: Sada atual do programa Rethrow. O propsito deste bloco garantir que se fizer alguma coisa no bloco try por exemplo, abrir um arquivo ou alocar memria voc poder se prevenir caso ocorra uma exceo ou no porque o bloco __finally sempre executado quando a execuo deixa um bloco try. Esta construo lhe permite um modo de fazer limpeza o que sem o seu uso iria requerer uma codificao complexa.

11.3.7.

O BLOCO catch(...)

O C++ tem uma construo que s vezes vista em cdigo tradicional e usada para capturar qualquer exceo aps uma dada exceo. Eis como ela funciona:
try { //Faz alguma operao aritmtica } catch(System::ArithmeticException *pex) { //Manipula esta exceo } catch(...) { //Manipula qualquer exceo }

Se uma exceo no casa com o primeiro bloco catch, ela ser capturada pelo segundo no importa qual o seu tipo. O problema que voc perde todas as informaes sobre a exceo porque o bloco catch(...) no tem argumento. Se voc quiser esta funcionalidade quando usa excees gerenciadas, tome um bloco catch que tenha uma Exception* como argumento. Isto ir capturar qualquer objeto gerenciado de exceo.

11.4.

CRIANDO SEUS PRPRIOS TIPOS DE EXCEO

Voc j viu que todos os tipos de exceo se derivam da classe System::Exception. Se no encontrar um que se adapte s suas necessidades na hierarquia de exceo padronizada, voc pode derivar sua prpria classe de Exception e us-la no seu cdigo.

185

11.4.1.

EXERCCIO: CRIANDO E USANDO UMA CLASSE DE EXCEO DERIVADA DE Exception

O exerccio a seguir lhe mostrar como derivar nova classe de exceo e como us-la no cdigo. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado OwnExcept. PASSO 2: Aps a declarao using namespace System; no arquivo-fonte OwnExcept.cpp, adicione a definio de classe:
//DEFINIO DA CLASSE DE USURIO MyException****************************** __gc class MyException : public System::Exception { public: int errNo; MyException(String *msg, int num) : Exception(msg), errNo(num)//++++++ { }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ };//**********************************************************************

Esta classe de exceo personalizada uma classe gerenciada herdeira de System::Exception e ela estende Exception adicionando um nico campo para suportar um nmero de erro. O construtor da classe recebe uma mensagem e um nmero, e passa a string de mensagem de volta para a classebase. NOTA: Declaramos o campo errNo public. Embora lhe tenhamos aconselhado a tornar private todos os membros de dados das classes, em certas circunstncias voc pode declarar membros de dados public. Uma vez que voc tenha criado um objeto Exception e o tenha mandado de volta para a cliente, voc se preocupa com o que o cliente faz com o objeto? As excees so objetos chama e cinza e voc normalmente no se deve interessar com a integridade do estado de tais objetos uma vez que eles deixam seu cdigo em uma declarao throw. PASSO 3: Aps a definio da classe myException, adicione a definio de funo abaixo:
void func(int a)//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { try { if(a <= 0) throw new System::ArgumentException(S"Argumento negativo"); } catch(System::ArgumentException *pex) { Console::WriteLine(S"Objeto ArgumentException capturado em func()"); throw new MyException(pex->Message, 1000); } }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

A funo checa seu argumento e lana uma System::ArgumentException se achar um valor negativo. Esta exceo capturada localmente e uma mensagem impressa no console. Decidimos, ento, que realmente queremos manipular a exceo em outro lugar. Assim, criamos um novo objeto myException e o relanamos, inicializando-o com a mensagem no objeto original ArgumentException. PASSO 4: Teste a manipulao da exceo chamando a funo em _tmain:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Excees personalizadas");

186

try { func(0); } catch(MyException *pex) { Console::WriteLine(S"Objeto MyException capturado em _tmain()"); Console::WriteLine(S"A mensagem : {0}", pex->Message); Console::WriteLine(S"O nmero do erro : {0}", __box(pex->errNo)); } return 0; }//***********************************************************************

A chamada funo com um valor 0 ativa a exceo, que manipulada na prpria funo func e, ento, a exceo relanada para ser manipulada na funo principal. A Figura 11.8 mostra como a exceo foi capturada nas duas funes.

Figura 11.8: Sada do programa OwnExcept. No cdigo acima, note que necessrio encaixotar o nmero do erro antes que ele possa ser usado em uma chamada para WriteLine, porque a sobrecarga formatada para WriteLine precisa de uma lista de ponteiros Object* e o encaixotamento lhe permite usar um tipo embutido como um objeto. Veja o captulo 25 para mais informaes sobre encaixotamento.

11.4.2.

USANDO CLASSES __value

No exemplo acima, voc implementou uma exceo personalizada como uma classe gerenciada (declarada usando a palavra-chave __gc), mas tambm possvel usar tipos de valor (declarados com a palavra-chave __value) como excees personalizadas. A diferena aparece quando voc quer usar tipos de valor como excees porque, ento, precisa lanar um ponteiro para um tipo gerenciado. A obteno do ponteiro no problema com classes __gc porque voc sempre os cria usando new e sempre obtm como retorno um ponteiro. Os tipos de valor so criados na stack e voc opera diretamente neles e no atravs de ponteiros. Se quiser obter o ponteiro para um tipo de valor, voc tem que encaixot-lo, criando uma envoltria para ele em um objeto e lhe retornando um ponteiro. Eis um exemplo sobre como voc deve usar um tipo de valor como uma exceo:
__value struct MyExceptionStruct { int errNo; }; void SomeFunction() { //Lana uma struct...

187

MyExceptionStruct mes = {1000}; throw __box(mes); }

A chamada funo __box envolve a estrutura em uma caixa-objeto e retorna um ponteiro para o objeto, que, ento, pode ser lanado.

11.5.

USANDO __try_cast PARA CONVERSO DINMICA

O C++ suporta a idia de casting, na qual voc informa ao compilador para ele converter um tipo em outro e us-lo em uma expresso. Embora o casting possa ser til, ele tambm pode ser danoso e a palavra-chave __try_cast foi introduzida nas extenses C++ gerenciadas para ajudar a realizar a operao com segurana. O fragmento de cdigo abaixo mostra um casting seguro e um no seguro:
//Define as classes Vehicle e Car __gc class Vehicle { }; __gc class Car : public Vehicle { }; __gc class Truck : public Vehicle { }; __gc class Bus : public Vehicle { }; //... Car *pc = new Car(); //Cria um objeto Car Vehicle *pv = pc; //Aponta para o objeto pc usando um ponteiro Vehicle. Ok //.. Car *pc2 = pv; //Copia pv em outro ponteiro Car*. Problema!

O detecta um erro na ltima linha, e reclamar que no pode converter um Vehicle* para um Car*. O problema que um ponteiro Vehicle pode apontar para qualquer objeto derivado de Vehicle, como Truck ou Bus. A converso implcita de um Car para um Vehicle bem sucedida porque um Car um Vehicle. No outro sentido, a converso no funciona porque nem todo Vehicle um Car. neste tipo de problema que usamos a construo __try_cast. Por exemplo:
try { Car *pc2 = __try_cast<Car*>(pv); } catch(System::InvalidCastException *pce) { Console::WriteLine("Falha na converso"); }

Em tempo de execuo, __try_cast checa o objeto na outra extremidade do ponteiro para ver se ele tem o mesmo tipo do objeto que voc est tentando converter. Se tiver, a converso funciona; caso contrrio, uma InvalidCastException lanada. NOTA: Programadores com experincia em C++ verificaro que a construo __try_cast quase idntica dynamic_cast suportada pelo C++ padro. A diferena que __try_cast lana uma exceo sem a converso falhar.

188

11.6.

USANDO EXCEES NAS LINGUAGENS .NET

Uma das grandes vantagens das excees gerenciadas C++ que elas funcionam nas linguagens .NET. Assim, voc pode, por exemplo, lanar uma exceo em C++ e captur-la em um aplicativo do Visual Basic. Simplesmente as excees j no so uma caracterstica C++ e isto nos habilita a harmonizar manipulao de erros atravs de cdigo escrito em diferentes linguagens, tornando a programao com linguagem misturada mais fcil do que era no passado.

11.6.1.

EXERCCIO: CRIANDO UMA CLASSE C++ E UTILIZANDO-A EM UM APLICATIVO DO VISUAL BASIC

No exerccio final deste captulo, voc criar uma classe C++ em uma biblioteca de vnculo dinmico (DLL, Dynamic Link Library) e, ento, a utilizar em um aplicativo do Visual Basic. NOTA: Voc precisar ter o Visual Basic .NET instalado para completar a segunda parte deste exerccio. PASSO 1: Inicialize o Visual Studio .NET e abra um novo projeto Visual C++. Desta vez, escolha um projeto Class Library (.NET)(Figura 11.9), que usado que voc quer criar uma DLL no um EXE. Chame o projeto de MyClass.

Figura 11.9: Criando um novo projeto Visual C++ do tipo Class Library (.NET). Voc ver que criou um projeto que define um namespace chamado MyClass, contendo uma nica classe chamada Class1. esta classe que voc editar, adicionando um mtodo que pode ser chamado de um cliente Visual Basic. PASSO 2: O projeto conter diversos de arquivos, entre eles MyClass.h e MyClass.cpp, que so usados para suportar a definio e implementao da classe Class1. Abra MyClass.h e adicione a funo Test com o cdigo abaixo:
//////////////////////////////////////////////////////////////////////////

189

// MyClass.h //BIBLIOTECAS E ARQUIVOS DE CABEALHO************************************* #pragma once using namespace System; //************************************************************************ namespace MyClass//******************************************************* { public __gc class Class1//++++++++++++++++++++++++++++++++++++++++++++ { public: void Test(int n)//-----------------------------------------------{ if(n < 0) { throw new ArgumentException(S"O argumento deve ser positivo"); } }//--------------------------------------------------------------};//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ }//*********************************************************************** //////////////////////////////////////////////////////////////////////////

O mtodo Test j nos familiar. Ele simplesmente checa seu argumento e lana uma exceo se o argumento for menor do que 0. PASSO 3: Construa o projeto. No final voc ver que uma DLL criada na pasta debug do projeto (Figura 11.10).

Figura 11.10: Localizao da DLL no projeto MyClass. PASSO 4: Feche o projeto e crie um novo projeto Visual Basic Console Application, chamado Tester (Figura 11.11). Antes que possa acessar a DLL C++, criada h pouco, voc tem que adicionar uma referncia a ela no projeto atual. Para fazer isto, abra o Solution Explorer, expanda o projeto e clique com o boto direito do mouse no cone References (Figura 11.12). PASSO 5: Escolha Add Reference no menu drop-down e, quando surgir a caixa de dilogo Add Reference mostrada na Figura 11.13, clique no boto Browse e localize a DLL que voc construiu no PASSO 3. Veja se ela foi corretamente adicionada em Selected Components. Clique OK. 190

Figura 11. 11: Criando um projeto Visual Basic do tipo Console Application.

Figura 11.12: Adicionando uma referncia para a DLL C++.

Figura 11.13: Adicionando uma DLL C++ a um projeto do Visual Basic. 191

PASSO 5: Agora voc pode adicionar o cdigo para o projeto. Edite a funo Main com o cdigo abaixo:
'Aplicativo para demonstrar a manipulao de excees de uma linguagem para outra Imports [MyClass] Module Module1 Sub Main() Dim obj As New Class1 Try obj.Test(-1) Catch e As ArgumentException Console.WriteLine("Exceo: " & e.Message) End Try Console.WriteLine("Fim") End Sub End Module

A primeira linha importa o namespace MyClass para o programa. Esta linha exerce a mesma funo de using namespace em C++. Assim, voc no tem que qualificar o nome completo da Class1 quando ela aparece. A primeira linha na funo Main cria um novo objeto Class1 de um modo equivalente ao do C++ quando usamos o operador new. A chamada funo Test est dentro de uma construo try e catch, e voc pode ver a similaridade entre os modos como as excees so manipuladas no Visual Basic e no C++. A principal diferena que no Visual Basic os blocos catch esto dentro do bloco try. PASSO 7: Construa e execute o cdigo. Quando passamos 1 como argumento de Test, ativamos a exceo e, assim, veremos a mensagem que o bloco catch imprime no console.

192

11.7.

RPIDAS REFERNCIAS SOBRE O CAPTULO 11

A Tabela 11.3 mostra um resumo deste captulo. Tabela 11.3: Resumo do captulo 11.
PARA Gerar uma exceo FAA ISTO Use a palavra-chave throw, tomando um ponteiro para um tipo gerenciado como o argumento. Ex.: throw new SomeException(); Capturar uma exceo Use a construo try e catch, envolvendo o cdigo sujeito a falha com um bloco try, seguido de pelo menos um bloco catch. Ex.: try { //O cdigo que pode falhar } catch(SomeException *pce) { //Manipula a exceo } Capturar mais de uma Encadear blocos catch. exceo Ex.: catch(SomeException *pce) { //Manipula a exceo } catch(SomeOtherException *pce) { //Manipula a exceo } Capturar uma famlia Use a classe-base das excees que voc quer capturar no de excees bloco catch. Por exemplo, ArithmeticException capturar DivideByZeroException em vrias outras. Capturar toda exceo Use um bloco catch que receba Exception* como um parmetro, que capturar todo tipo que for derivado de Exception. Manipular excees em Use throw para relanar excees de um bloco catch para mais de um ponto de outro. um programa Criar suas prprias Derive da classe Exception, adicionando seus membros excees prprios.

193

12.

CAPTULO 12: MATRIZES E COLEES

Neste captulo, voc aprender como Implementar matrizes em C++; Criar matrizes unidimensionais e multidimensionais; Criar matrizes gerenciadas; Usar as caractersticas da classe System::Array com matrizes gerenciadas; Usar as classes de coleo fornecidas nos namespace System::Collections e System::Specialized.

Este captulo relacionado com estruturas de dados. Voc aprender sobre matrizes e outras classes de coleo e tambm como utiliz-las nos seus programas. Na primeira parte do captulo, voc estudar dois tipos de matrizes: as matrizes nativas fornecidas pela linguagem C++ e as matrizes gerenciadas .NET, que usam a funcionalidade herdada do .NET Framework. Voc descobrir como elas funcionam e quando uma deve ser usada ao invs de outra. A segunda parte do captulo aborda mais amplamente a srie de classes de coleo fornecidas pelo .NET Framework, discutindo suas caractersticas e mostrando como e quando us-las.

12.1.

MATRIZES NATATIVAS C++

As matrizes nativas so as fornecidas como parte da linguagem C++. Elas so baseadas nas matrizes que o C++ herda do C. Embora as matrizes nativas estejam projetadas para ser mais rpidas e eficientes, h desvantagens associadas com o seu uso, como veremos em breve.

12.1.1.

EXERCCIO: CRIANDO E USANDO MATRIZES NATIVAS C++

Este primeiro exerccio introduzir as matrizes nativas C++ lhe mostrando como criar uma matriz de tipos de valor e como us-la. PASSO 1: Abra um novo projeto Visual C++ Console Application (.NET) chamado Trad. PASSO 2: No arquivo-fonte Trad.cpp, adicione o seguinte cdigo funo _tmain:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Matrizes tradicionais"); //Cria uma matriz int mt[10]; //Preenche a matriz for(int i = 0; i < 10; i++) mt[i] = i * 2; return 0; }//***********************************************************************

194

A matriz criada fornecendo-lhe um tipo, um nome e um tamanho entre colchetes. Aqui a matriz se chama mt e ela suporta dez valores int. Todas as matrizes so criadas usando a mesma sintaxe. Por exemplo:
//Cria uma matriz de 6 double double mt[6]; //Cria uma matriz de 2 char* char* mt[2];

Eis o primeiro ponto importante sobre matrizes nativas: uma vez voc tendo criado uma matriz, no possvel redimension-la. Assim, voc tem que saber quantos elementos so necessrios a priori. Se no souber, melhor utilizar uma matriz .NET, discutida mais adiante, neste captulo. NOTA: O tamanho da matriz tem que ser conhecido em tempo de compilao. Assim, por exemplo, voc no pode perguntar ao usurio um valor e, ento, us-lo para especificar uma dimenso de matriz em tempo de execuo. Porm, comum criarmos constantes, ou usando as declaraes de prprocessador #define, ou declarando variveis const int e as utilizando para especificar dimenses de matriz. Como voc pode ver do lao no cdigo acima, os elementos da matriz so acessados usando pares de colchetes contendo o ndice. Eis o segundo ponto importante sobre matrizes nativas: offset indexado de zero e no de um. Assim, o intervalo vlido de ndices para uma matriz com n elementos vai de 0 a n 1. Em outras palavras, numa matriz com 10 elementos, os ndices vlidos vo de [0] a [9]. PASSO 3: Adicione um segundo lao para imprimir no console o contedo da matriz, aps seu preenchimento:
//Imprime os contedos for(int i = 0; i < 10; i++) Console::WriteLine(mt[i]);

A Figura 12.1 mostra a sada do programa Trad.

Figura 12.1: Sada atual do programa Trad. PASSO 4: O que acontece se voc mudar o intervalo do segundo lao de modo que ele tente imprimir o elemento [10]? Altere o cdigo no segundo lao para:
//Imprime os contedos for(int i = 0; i <= 10; i++) Console::WriteLine(mt[i]);

Note a condio menor ou igual (<=). O efeito desta condio a tentativa de imprimir 11 elementos ao invs de 10. Compile e execute o programa e voc vai obter a sada mostrada na Figura 12.2. 195

Figura 12.2: Sada atual do programa Trad. Note o valor aleatrio que foi impresso no final da lista. Eis o terceiro ponto importante sobre matrizes nativas: os limites no so checados. As matrizes nativas C++ no so objetos e, portanto, elas no sabem quantos elementos comportam. Cabe a voc manter valores significativos nos limites da matriz; caso contrrio, voc se arrisca a corromper dados ou arruinar o seu programa.

12.1.2.

PASSANDO MATRIZES PARA FUNES

A passagem de matrizes para funes introduz uma complicao por causa do fato de que uma matriz no sabe o seu tamanho ou contedos. Como ver em breve, quando voc passa uma matriz para uma funo, passa apenas o endereo do incio da matriz, o que significa que voc tem de encontrar algum meio de passar informao sobre o tamanho junto com a matriz, quando chamar a funo. Normalmente, h dois modos de se fazer isto: Passar o tamanho como um parmetro explcito para a funo chamada; Garantir que a matriz sempre termina em um valor de marcador nico, de modo que a funo pode dizer quando o fim dos dados foi alcanado.

COMO FUNCIONAM AS MATRIZES NATIVAS? Uma matriz nativa C++ no um objeto; simplesmente uma coleo de valores enfileirados na memria. Assim, uma matriz de inteiros com 10 elementos consiste de 10 inteiros, um aps o outro, na memria. O nome da matriz um ponteiro para o primeiro elemento. Assim, quando declara uma matriz como
int mt[10];

voc est dizendo ao compilador para reservar memria suficiente para suportar 10 inteiros e retornar seu endereo como mt. Quando acessar um elemento da matriz, voc na verdade est especificando o offset deste endereo. Portanto, mt[1], significa desloque 1 int do endereo mt e use o que ali estiver armazenado. Isto explica porque a indexao de matriz comea de 0: um ndice 0 denota um deslocamento 0 do endereo inicial, i.e., o primeiro elemento. Uma vez o compilador tenha alocado o espao, quase tudo o que ele sabe sobre uma matriz seu endereo inicial. Quando voc fornece um offset em termos de um ndice de matriz, o compilador gera cdigo para acessar este pedao de memria. E se voc erradamente cair fora dos limites da matriz, pode terminar lendo ou escrevendo alguma coisa inadequada em algum lugar. Na verdade, acessar 196

deliberadamente regies fora dos limites da matriz tem sido a base para muitos ataques segurana dos programas e sistemas ao longo dos anos. Para finalizar esta breve explicao, note que h um vnculo estreito entre matrizes e ponteiros na realidade, to estreito que qualquer acesso a matriz pode ser escrito usando notao de ponteiro ao invs da notao de matriz, como neste exemplo:
//Duas atribuies equivalentes n = mt[3]; n = *(mt + 3);

Na segunda declarao acima, o compilador est sendo instrudo a desreferenciar o local na memria cuja distncia do endereo mt o tamanho de 3 variveis int.

12.1.2.1.

EXERCCIO: PASSANDO UMA MATRIZ PARA UMA FUNO

Vamos investigar a passagem de uma matriz para uma funo. PASSO 1: Abra o projeto Trad. PASSO 2: Em Trad.cpp, aps a declarao namespace System;, adicione a seguinte definio de funo:
void func(int mt[], size_t size)//++++++++++++++++++++++++++++++++++++++++ { for(size_t i = 0; i < size; i++) Console::WriteLine(mt[i]); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

O primeiro argumento da funo diz ao compilador que o endereo de uma matriz est sendo passado, o que equivalente a passar um ponteiro. muito comum usarmos size_t *mt ao invs de size_t mt[]. O segundo argumento passa o tamanho da matriz em efeito, a quantidade de memria apontada pelo primeiro argumento. O tipo size_t uma typedef para unsigned int e boa prtica usar este tipo para argumentos inteiros que denotem tamanhos, comprimentos ou dimenses. A funo imprime no console a matriz usando o tamanho, exatamente como antes. PASSO 3: Na funo _tmain, chame func:
func(mt, 10);

O que aconteceria se o tamanho da matriz fosse mudado em algum ponto? Voc pode tornar seu cdigo mais robusto calculando automaticamente o nmero de elementos na matriz, usando o operador sizeof, como abaixo:
func(mt, sizeof(mt) / sizeof(mt[0]));

O operador sizeof retorna o tamanho do seu argumento em bytes. O argumento pode ser um nome de varivel ou um nome de tipo. O uso de sizeof em uma matriz retorna o tamanho total da matriz em bytes, neste caso, 40 bytes. Quando divide pelo tamanho de um elemento 4 bytes voc obtm o nmero de elementos da matriz.

12.1.3.

INICIALIZANDO MATRIZES

possvel inicializar matrizes no ponto da declarao, como mostrado abaixo no fragmento de sintaxe:
int mt[4] = {1, 2, 3, 4};

Os valores a serem usados para a inicializao so fornecidos como uma lista, separados por vrgula, entre chaves, no lado direito da declarao. Estes valores so conhecidos como um inicializador 197

agregado. O compilador suficientemente inteligente para entender quantos valores esto na lista e, portanto, dimensionar a matriz para se ajustar sem que voc precise, explicitamente, fornecer um tamanho. Por exemplo:
//Dimenso da matriz obtida automaticamente int mt[] = {1, 2, 3, 4};

Se voc fornece uma dimenso e, ento, passa mais valores, ir gerar um erro de compilao. Se fornecer menos valores, a quantidade que voc passar ser usada para inicializar a matriz comeando do elemento [0] at a quantidade fornecida, fixando os demais elementos com zeros.

12.1.4.

MATRIZES MULTIDIMENSIONAIS

As matrizes multidimensionais em C++ so uma extenso da variedade unidimensional, j que na verdade uma matriz bidimensional uma matriz de matrizes (vetores) unidimensionais. Assim, matrizes de dimenses maiores do que um so construdas de vetores.

12.1.4.1.

EXERCCIO: CRIANDO E USANDO UMA MATRIZ 2D

O exerccio abaixo mostra como criar e usar uma matriz 2D. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado MultiD. PASSO 2: No arquivo-fonte MultiD.cpp, adicione o seguinte cdigo funo _tmain:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Matrizes multidimensionais"); //Cria uma matriz 2D int mt[2][3]; //Preenche a matriz for(int i = 0; i < 2; i++) for(int j = 0; j < 3; j++) mt[i][j] = (i + 1) * (j + 1); return 0; }//***********************************************************************

Note que uma matriz 2D declarada usando dois conjuntos de colchetes []. Voc no coloca os dois valores dentro de um nico conjunto de colchetes, como em muitas outras linguagens. Para ordens maiores voc simplesmente adiciona mais conjuntos []. Como no caso dos vetores, voc tem que fornecer o tamanho em tempo de compilao e os ndices de cada dimenso variam de 0 at o tamanho declarado menos 1. Os elementos da matriz tambm so acessados usando dois conjuntos []. PASSO 3: Imprima a matriz no console usando a extenso abaixo do mtodo para impresso de vetores:
//Imprime o contedo da matriz for(int i = 0; i < 2; i++) { for(int j = 0; j < 3; j++) Console::Write("{0} ", __box(mt[i][j])); Console::WriteLine(); }

198

Note que uma linha da matriz impressa em uma linha do console. O lao interno imprime uma nica linha usando repetidas chamadas funo Console::Write. Para formatar a sada, o elemento da matriz tem que ser encaixotado usando uma chamada palavra-chave __box. Aps cada linha ter sido impressa, uma chamada funo Console::WriteLine produz uma nova linha. Para passar uma matriz multidimensional para uma funo, use os dois conjuntos de colchetes vazios (por exemplo, int mt[][]) e, como antes, especifique as informaes dimensionais.

12.1.5.

ALOCAO DINMICA E MATRIZES

At agora, todas as matrizes neste captulo tiveram um tamanho fixo alocado em tempo de compilao. possvel e muito comum criar matrizes dinamicamente em tempo de execuo usando o operador new. A matriz que voc cria ainda tem o tamanho fixo, mas este tamanho pode ser especificado em tempo de execuo, quando voc sabe de quantos elementos precisa.

12.1.5.1.

EXERCCIO: CRIANDO E USANDO MATRIZ DINAMICAMENTE ALOCADA

O exerccio a seguir mostra como criar uma matriz dinamicamente e, ento, us-la. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Dynamic. PASSO 2: Em Dynamic.cpp, adicione o seguinte cdigo funo _tmain:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Matrizes dinmicas"); //Cria um vetor dinamicamente int *pVt = new int[10]; //Preenche o vetor for(int i = 0; i < 10; i++) pVt[i] = i * 2; //Imprime o contedo do vetor for(int j = 0; j < 10; j++) Console::WriteLine(pVt[j]); //Libera o vetor j que ele no mais necessrio delete pVt; return 0; }//***********************************************************************

Voc j utilizou o operador new para criar tipos de referncia .NET, mas este operador tambm usado em cdigo C++ tradicional para alocar memria dinamicamente em tempo de execuo. A sintaxe new, seguido pelo tipo da matriz e a dimenso entre colchetes. Uma vez tendo criado a matriz, voc obtm de retorno um ponteiro para o incio da matriz. Voc pode ver que as matrizes dinmicas so acessadas exatamente do mesmo modo que as estaticamente alocadas, usando a notao dos colchetes. Este uso de um ponteiro com uma notao de

199

matriz sublinha a relao entre ponteiros e matrizes, como explicado na observao COMO FUNCIONA MATRIZES NATIVAS?, neste captulo. Note a chamada ao operador delete antes da sada do programa. A alocao dinmica de uma matriz em C++ tradicional no cria o objeto gerenciado. Assim, no h coletor de lixo associado com esta matriz. Portanto, para usar a memria eficientemente, voc tem que se lembrar de liber-la quando no mais precisar da matriz. Estritamente falando, a chamada aqui desnecessria porque toda a memria alocada liberada quando o programa finaliza. Porm, em qualquer programa no mundo real, voc precisa gerenciar cuidadosamente a memria para garantir que tudo que for alocado dinamicamente seja liberado em um ponto apropriado. NOTA: Uma vez tendo chamado delete em um ponteiro, voc no deve usar novamente o ponteiro porque a memria para a qual ele aponta j no mais alocada para voc. Se tentar usar um ponteiro aps a liberao da memria para a qual ele aponta, voc vai obter um erro em tempo de execuo. PROBLEMAS COM O GERENCIAMENTO MANUAL DE MEMRIA O gerenciamento manual de memria de longe considerado a nica grande causa de bugs em programas C e C++ e a fora motriz por trs do desenvolvimento dos mecanismos de coleta de lixo em linguagens como C#. Se lhe dissermos para chamar delete em todo pedao de memria alocado, enganos vo ser cometidos. Os dois principais problemas associados com o gerenciamento manual de memria so: No liberar memria: Este problema normalmente o menos danoso dos dois e resulta em um programa toma mais memria do que o necessrio, um processo conhecido como vazamento de memria. Em casos extremos, a quantidade extra de memria consumida por um aplicativo pode atingir o ponto em que o vazamento de memria comea a interferir com outros aplicativos ou mesmo com o sistema operacional. Liberar memria de modo no apropriado: Em um programa complexo, pode no ser bvio o local onde um pedao particular de memria deve ser liberado ou de quem a responsabilidade para fazer isto. Se delete for chamado muito cedo e outro pedao do cdigo tenta usar a matriz dinamicamente alocada, voc vai obter um erro em tempo de execuo. O mesmo verdade se voc tentar chamar delete no mesmo ponteiro mais de uma vez.

Embora a alocao manual de memria usando new e delete lhe permita fazer coisas muito inteligentes, estes dois problemas foram o impulso que moveu o desenvolvimento de coletores de lixo, que permitem que o sistema planeje o uso da memria alocada dinamicamente e a libere quando no mais necessria.

12.1.6.

MATRIZES __gc

O .NET Framework estendeu o modelo de matriz C++ adicionando matrizes __gc. Como voc pode esperar do uso da palavra-chave __gc, uma matriz __gc uma matriz dinmica que alocada na heap .NET e est sujeita s regras usuais do coletor de lixo. NOTA: Ao contrrio das matrizes C++ padronizadas, o subscrito em matrizes __gc no sinnimo para aritmtica de ponteiros. Voc pode criar uma matriz __gc de um modo bastante similar ao da criao de uma matriz dinmica tradicional:
int32 gcMt[] = new int32[10];

200

Note que o tipo da matriz o .NET Int32 ao invs do tipo embutido int. Note tambm o modo como gcMt foi declarada: ela j no mais um ponteiro, como acontece com as matrizes tradicionais, mas um objeto gerenciado. Todas as matrizes __gc so herdeiras de System::Array. Assim, qualquer mtodo ou propriedade de System::Array pode ser diretamente aplicada matriz __gc. Veja a seo A CLASSE .NET Array, ainda neste captulo, para detalhes sobre System::Array e como us-la.

12.1.7.

USANDO AS PALAVRAS-CHAVES __gc E __nogc

Voc pode usar as palavras-chaves __gc e __nogc para determinar que uma matriz gerenciada ou no gerenciada est sendo criada. Normalmente, a criao de uma matriz de tipos primitivos resultar em uma matriz no gerenciada. Porm, voc pode usar a palavra-chave __gc para criar uma matriz gerenciada de um tipo primitivo, como no exemplo abaixo:
//Cria um vetor no gerenciado int *vt = new int[10]; //Cria um vetor gerenciado de int int vt1 __gc[] = new int __gc[10];

A sintaxe __gc[] criar um vetor de tipos primitivos que est sujeito ao mecanismo de coleta de lixo usual. De modo similar, voc pode usar __nogc para criar matrizes no gerenciadas tradicionais de tipos .NET, fornecendo a este o tipo correspondente a um dos tipos primitivos C++:
//Cria um vetor no gerenciado de Int32 Int32 vt1 __nogc[10];

Este vetor no objeto de matriz gerenciado e ele no coletado pela lixeira. Alm do mais, como no um objeto de matriz, ele no suporta qualquer funcionalidade da classe System::Array.

12.1.8.

EXERCCIO: MATRIZES E TIPOS DE REFERNCIA

Como os tipos de referncia sempre so acessados usando referncias, a criao e inicializao de matrizes de tipos de referncia ligeiramente diferente da criao de matrizes de tipos de valor. O exerccio abaixo mostra como criar e usar uma matriz de tipos de referncia. Neste exerccio, voc usar System::String como o tipo de referncia, mas esta classe pode ser substituda por um tipo de referncia criado por voc: PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado RefArray. PASSO 2: No arquivo-fonte RefArray.cpp, adicione o cdigo abaixo funo _tmain:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Matrizes de tipos de referncia"); //Cria uma matriz de referncias a String String* pa[] = new String*[5]; //Atribui explicitamente uma nova String ao elemento 0 pa[0] = new String("abc"); //Atribui implicitamente uma nova String ao elemento 1 pa[1] = "def";

201

//Imprime o contedo da matriz for(int i = 0; i < 5; i++) { if(pa[i] == 0) Console::WriteLine("null"); else Console::WriteLine(pa[i]); } return 0; }//***********************************************************************

PASSO 3: Compile e execute o cdigo. A Figura 12.3 mostra a sada deste programa.

Figura 12.3: Sada do programa RefArray. A declarao de pa cria uma nova matriz de referncias de string, no de objetos string. As referncias na matriz so inicializadas com null e voc precisa criar objetos e atribu-los s referncias na matriz. No exemplo, voc atribuiu valores aos dois primeiros dos cinco valores. Assim, quando voc imprime a matriz, v as duas strings impressas, seguidas por trs null.

12.1.9.

MATRIZES __gc MULTIDIMENSIONAIS

As extenses gerenciadas para C++ fornecem um novo modelo, mostrado no fragmento de cdigo abaixo, para criar matrizes __gc multidimensionais:
//Cria uma matriz multidimensional de referncias de String Int32 pn[,] = new Int32[3,2]; //Inicializa dois membros pn[0,0] = 3; pn[1,1] = 4;

A declarao da referncia de matriz neste caso, pn usa dois colchetes contendo zero ou mais vrgulas para denotar o nmero de dimenses na matriz. O nmero de vrgula mais um igual dimenso da matriz. Assim, se voc quiser criar uma matriz tridimensional, declare-a como Int32 p3d[,,]. O operador new tambm usa colchetes, mas com a lista de dimenses formando um conjunto, o que torna a notao mais fcil do que a tradicional. Voc conhecer as matrizes __gc multidimensionais na ltima seo deste captulo, OPERAES BSICAS COM MATRIZES, quando falamos com mais detalhes sobre a classe System::Array.

202

12.2.

A CLASSE .NET Array

Todas as matrizes gerenciadas no .NET Framework so herdeiras de System::Array, o que significa que toda matriz gerenciada dispe de algumas propriedades e mtodos teis. Estas propriedades e mtodos so resumidos na Tabela 12.1. Tabela 12.1: Propriedades e mtodos da classe System::Array.
PROPRIEDADE IsFixedSize IsReadOnly IsSynchronized Length Rank SyncRoot MTODO BinarySearch Clear Clone Copy CopyTo GetEnumerator GetLength GetLowerBound GetUpperBound GetValue IndexOf Initialize DESCRIO Retorna true se a matriz tiver um tamanho fixo. Sempre retorna true, sobreposta por uma classe-derivada. Retorna true se a matriz somente para leitura. Sempre retorna false, a no ser que sobreposta por uma classe-derivada. Retorna true se a matriz sincronizada. Sempre retorna false, a no ser que sobreposta por uma classe-derivada. Retorna o nmero total de elementos em todas as dimenses da matriz. Retorna o nmero de dimenses na matriz. Retorna um ponteiro para um objeto que pode ser usado para sincronizar acesso matriz. DESCRIO Mtodo static que procura um vetor para um valor, usando um algoritmo de procura binria. Mtodo static que fixa toda ou parte de uma matriz para zero ou uma referncia null. Cria uma cpia superficial da matriz. Mtodo static que copia toda ou parte de uma matriz para outra matriz, realizando converso de tipo para baixo como requerido. Mtodo que copia toda ou parte de um vetor para outro. Retorna um enumerador para a matriz. Veja a ltima seo deste captulo, ENUMERADORES, para detalhes. Retorna o nmero de elementos em uma dimenso especificada como um inteiro. Retorna o limite inferior de uma dimenso especificada como um inteiro. Retorna o limite superior de uma dimenso especificada como um inteiro. Retorna o valor em uma posio especificada em um vetor ou em uma matriz multidimensional. Mtodo static que retorna o ndice da primeira ocorrncia de um elemento em uma matriz ou em parte dela. Inicializa uma matriz de tipos de valor chamando o construtor padro do tipo de valor. Este mtodo no deve ser usado em matrizes de tipos de referncia. Mtodo static que retorna o ndice da ltima ocorrncia de um elemento em uma matriz ou em parte dela. Mtodo static que inverte a ordem dos elementos em todo o vetor ou em parte dele. Fixa um elemento de matriz para um valor especificado. Mtodo static que ordena os elementos em um vetor.

LastIndexOf Reverse SetValue Sort

12.2.1.

EXERCCIO: OPERAES BSICAS EM MATRIZES

Ao contrrio das matrizes do C++ tradicional, as matrizes gerenciadas so objetos e elas sabem quantas dimenses tm e quantos elementos contm. O exerccio abaixo introduz algumas das funcionalidades bsicas da classe System::Array. 203

PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado SysArray. PASSO 2: No incio da funo _tmain, declare alguns indexadores de lao e uma matriz bidimensional de inteiros de 32 bits:
//Declara indexadores de lao int i, j, k; //Cria uma matriz bidimensional de Int32 Int32 pMt[,] = new Int32[3,2];

Esta a matriz que voc usar para explorar as caractersticas da classe System::Array no restante desta seo. PASSO 3: Como pMt um objeto gerenciado, ele herdeiro direto de System::Array. Assim, voc pode usar as propriedades Rank e Length da classe Array para descobrir qual o grau (nmero de dimenses) e o comprimento da matriz.
Console::WriteLine("O grau {0}", __box(pMt->Rank)); Console::WriteLine("O comprimento {0}", __box(pMt->Length));

Quando executa o cdigo, voc obtm os resultados mostrados na Figura 12.4, que concordam com a declarao.

Figura 12.4: Sada atual do programa SysArray. PASSO 4: O mtodo GetLength no confundi-lo com a propriedade Length retorna o tamanho de qualquer dimenso da matriz. Assim, voc pode imprimir no console os tamanhos de cada dimenso de pMt:
//Imprime informaes sobre as dimenses da matriz for(i = 0; i < pMt->Rank; i++) Console::WriteLine("A dimenso {0} tem tamanho igual a {1}", __box(i), __box(pMt->GetLength(i)));

Agora que definiu uma matriz e pode descobrir o tamanho de cada dimenso, voc precisa saber como obter e fixar elementos na matriz. PASSO 5: Adicione os seguintes laos aninhados na seqncia do seu cdigo:
//Preenche a for(j = 0; j for(k = 0; k pMt[j, matriz com valores < pMt->GetLength(0); j++) < pMt->GetLength(1); k++) k] = (j + 1) * (k + 1);

O lao externo indexa as linhas, enquanto o interno indexa as colunas. A notao [x, y] usada para referenciar os elementos da matriz. A classe Array tambm possui o mtodo SetValue, que fornece um modo alternativo de fixar valores para as linguagens que no suportam a notao de matriz no estilo C++:
//Coloca o valor 10 no elemento de matriz [1, 1]

204

pMt->SetValue(__box(10), 1, 1);

Note que, quando usar SetValue, voc tem que encaixotar o valor a ser inserido na matriz porque SetValue espera um ponteiro para um tipo de referncia. PASSO 6: Imprima no console os valores na matriz usando o cdigo abaixo:
//Imprime os dados da matriz Console::WriteLine(S"\nMatriz pMt"); for(j = pMt->GetLowerBound(0); j <= pMt->GetUpperBound(0); j++) for(k = pMt->GetLowerBound(1); k <= pMt->GetUpperBound(1); k++) Console::WriteLine("pMt[{0}, {1}] = {2}", __box(j), __box(k), __box(pMt[j, k]));

Uma vez mais, o lao externo indexa sobre as linhas e o interno sobre as colunas. Neste caso, os mtodos GetLowerBound e GetUpperBound retornam os ndices dos limites inferior e superior. O argumento para GetUpperBound e GetLowerBound a dimenso da matriz cujo limite voc quer encontrar. Em C++, o limite inferior , invariavelmente, 0; e o superior pode ser obtido usando o mtodo GetLength. Estes mtodos so teis principalmente em outras linguagens onde pode ser comum ter matrizes com os limites inferior e superior arbitrrios. A Figura 12.5 mostra a sada do programa SysArray.

Figura 12.5: Sada atual do programa SysArray. Todos os valores inteiros tm que ser encaixotados antes de serem impressos. Um modo alternativo para encaixotar o valor da matriz consiste em usar o mtodo GetValue para acessar um elemento da matriz, que retorna uma referncia de objeto:
Console::WriteLine("pMt[{0}, {1}] = {2}", __box(j), __box(k), pMt->GetValue(j, k));

12.2.2.

OPERAES DE MATRIZ MAIS AVANADAS

Agora voc pode criar matrizes, descobrir quantas dimenses elas possuem e qual o tamanho de cada dimenso, bem como fixar e obter valores. Esta seo introduz algumas das operaes mais avanadas suportadas pela classe Array, como, por exemplo, copiar, procurar e ordenar.

12.2.2.1.

EXERCCIO: COPIANDO ELEMENTOS DE MATRIZ

205

O exerccio a seguir mostra como usar o mtodo Copy para copiar parte de uma matriz para outra. PASSO 1: Abra o projeto SysArray. PASSO 2: No final da funo _tmain, crie uma segunda matriz bidimensional com o mesmo tamanho e tipo da original.
//Cria outra matriz bidimensional de Int32 Int32 pMt2[,] = new Int32[3, 2];

PASSO 3: Adicione o cdigo abaixo para preencher a nova matriz com um valor constante.
//Preenche a matriz com um valor constante for(j = 0; j < pMt2->GetLength(0); j++) for(k = 0; k < pMt2->GetLength(1); k++) pMt2[j, k] = 47;

PASSO 4: Para copiar alguns valores da primeira para a segunda matriz, use o mtodo static Copy:
//Copia dois valores de pMt para pMt2 System::Array::Copy(pMt, 0, pMt2, 2, 2);

Este mtodo lhe permite copiar uma matriz completa ou parte dela para outra matriz. Os dois primeiros argumentos so a matriz fonte e o ndice de onde comea a copia. Os dois argumentos da seqncia so a matriz destino e o ndice inicial a partir do qual os elementos so colocados. O ltimo argumento o nmero de elementos a serem copiados. Neste exemplo, voc copiou dois elementos de pMt [0, 0] e [0, 1] para a linha do meio de pMt2, i.e., [1, 0] e [1, 1]. PASSO 5: Para ver o resultado da copia feita no PASSO 4, imprima no console os valores na matriz pMt2, usando o cdigo abaixo:
//Imprime os novos dados da matriz pMt2 Console::WriteLine(S"\nMatriz pMt2"); for(j = pMt2->GetLowerBound(0); j <= pMt2->GetUpperBound(0); j++) for(k = pMt2->GetLowerBound(1); k <= pMt2->GetUpperBound(1); k++) Console::WriteLine("pMt2[{0}, {1}] = {2}", __box(j), __box(k), __box(pMt2[j, k]));

A sada do programa SysArray mostrada na Figura 12.6.

Figura 12.6: Sada atual do programa SysArray. 206

12.2.2.2.

EXERCCIO: PROCURANDO ELEMENTOS DE MATRIZ

comum fazermos investigaes em uma matriz procurando uma dada entrada. Voc pode fazer isto usando os mtodos IndexOf e LastIndexOf. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Strings. PASSO 2: Na funo _tmain, adicione o cdigo abaixo para criar uma matriz de strings:
//FUNO PRINCIPAL******************************************************** int _tmain() { //Cria uma matriz de strings String *sa[] = { S"Cachorro", S"Gato", S"Elefante", S"Pato", S"Cachorro", S"Cavalo", S"Porco", S"Gato" }; //Checa o comprimento Console::WriteLine(S"sa tem comprimento igual a {0}", __box(sa->Length)); }//***********************************************************************

PASSO 3: As funes IndexOf e LastIndexOf lhe permitem verificar se ocorre um dado objeto na matriz. Adicione o seguinte cdigo em _tmain:
//Procura por um valor String *s = S"Cachorro"; int pos = Array::IndexOf(sa, s); Console::WriteLine(S"O ndice de s em sa {0}", __box(pos)); //Procura a prxima ocorrncia pos = Array::IndexOf(sa, s, pos + 1); Console::WriteLine(S"O prximo ndice de s em sa {0}", __box(pos));

A chamada para IndexOf encontra a primeira ocorrncia da string Cachorro na matriz e retorna seu ndice, que, no caso acima 0. A segunda chamada a uma sobrecarga de IndexOf, procura por uma ocorrncia a partir de um ndice fornecido. Como a procura comea exatamente aps a primeira ocorrncia, o ndice retornado ser o da segunda ocorrncia, i.e., 4. A terceira sobrecarga de IndexOf lhe permite procurar dentro de uma poro da matriz. NOTA: Se o valor no for encontrado, o ndice retornado ser o limite inferior da matriz menos 1, que em C++ usualmente corresponde ao valor 1. A funo LastIndexOf funciona do mesmo modo que IndexOf mas comea a procura da outra extremidade da matriz.

12.2.2.3.

EXERCCIO: ORDENANDO ELEMENTOS DE MATRIZ

O mtodo static Array::Sort e suas sobrecargas lhe fornecem um meio de ordenar uma matemtica ou parte dela, enquanto Array::Reserve lhe permite inverter a ordem dos elementos. Na funo _tmain do projeto Strings, experimente o seguinte cdigo: 207

Console::WriteLine(S"\nOrdenando sa"); Array::Sort(sa); for(int i = 0; i < sa->get_Length(); i++) Console::WriteLine(sa[i]); Console::WriteLine(S"\nInvertendo sa ordenada"); Array::Reverse(sa); for(int i = 0; i < sa->get_Length(); i++) Console::WriteLine(sa[i]);

A Figura 12.8 mostra a sada do programa Strings.

Figura 12.7: Sada atual do programa Strings. Uma valiosa sobrecarga para Sort lhe fornece duas matrizes, uma das quais contm a teclas usadas para definir o tipo de ordenamento. Veja como isto funciona no exerccio abaixo. PASSO 1: Abra o projeto Strings. De acordo com a Figura 12.7, atualmente a matriz sa contm as seguintes entradas:
Porco Pato Gato Gato Elefante Cavalo Cachorro Cachorro

PASSO 2: No final da funo _tmain, adicione a nova matriz:


Int32 keys[] = {6, 5, 4, 4, 3, 2, 1, 1};

Esta matriz contm as teclas que voc usar para ordenar a matriz dos nomes dos animais. Elas refletem uma preferncia particular voc pode mudar isto, se quiser. PASSO 3: Adicione a nova chamada Sort e imprime o resultado para sa:
Console::WriteLine(S"\nOrdenando sa com base em keys"); Array::Sort(keys, sa);

208

for(int i = 0; i < sa->get_Length(); i++) Console::WriteLine(sa[i]);

A matriz keys ordenada e os elementos em sa so ordenados exatamente na mesma ordem. A Figura 12.8 mostra a nova sada do programa Strings.

Figura 12.8: Sada atual do programa Strings. A INTERFACE IComparable Qualquer tipo usado no mtodo Sort tem que implementar a interface IComparable, que tem um membro, CompareTo. Quando CompareTo invocada em um objeto, passada uma referncia para outro objeto. A funo retorna 0 se as duas instncias forem iguais, um valor negativo se o objeto passado for maior do que a instncia que est chamando a funo e um valor positivo se o objeto passado for menor.

12.2.3.

ENUMERADORES

O mtodo GetEnumerator retorna um enumerador que lhe permite indexar os elementos da coleo de um modo parecido com o indexador ForEach do Microsoft Visual Basic ou foreach do C#. Um enumerador fornece aos programadores um modo de indexao de alto nvel em uma coleo sem que ele precise saber como a coleo implementada. Assim, voc pode usar um enumerador para indexar os contedos de uma matriz ou de uma lista vinculada exatamente do mesmo modo, sem precisar saber como passar de um elemento para outro nas estruturas de dados subjacentes. Muitas das colees no .NET Framework suportam o modelo de enumerador. Uma chamada a GetEnumerator lhe retorna um ponteiro para uma interface IEnumerator, que tem dois mtodos e uma propriedade que voc pode usar: MoveNext avana o enumerador para o prximo elemento na coleo; Reset desloca o enumerador para antes do primeiro elemento na coleo; Current retorna uma referncia para o elemento atual. 209

12.2.3.1.

EXERCCIO: USANDO UM ENUMERADOR NUMA MATRIZ DE STRINGS

Neste exerccio, voc usar um enumerador para listar os elementos na matriz de strings. PASSO 1: Abra o projeto Strings e adicione a seguinte declarao using no incio do arquivofonte Strings.cpp.
using namespace System::Collections;

A interface IEnumerator definida no namespace System::Collections. Assim, mais fcil usar os enumeradores se voc adicionar uma declarao using para o namespace. PASSO 2: Na funo _tmain, adicione no fim do cdigo:
Console::WriteLine(S"\nUsando Enumeradores"); IEnumerator *ie = sa->GetEnumerator(); while(ie->MoveNext()) Console::WriteLine(ie->Current);

H muito que se falar sobre este cdigo. No incio, o enumerador posicionado antes do primeiro elemento. Assim, voc precisa chamar MoveNext uma vez para obter o primeiro elemento. Quando no houver mais elementos para reaver, as chamadas a MoveNext retornaro false. A propriedade Current retm o objeto atual mas no desloca o ponteiro. Assim, voc obter o mesmo valor de volta at que chame MoveNext novamente. A propriedade Current tambm retorna um ponteiro Object geral . Portanto, voc freqentemente precisar converter este ponteiro para o tipo atual do objeto, usando a palavra-chave dynamic_cast do C++ ou a equivalente __try_cast do .NET (veja o captulo 11 para detalhes sobre como usar __try_cast). O que no bvio no cdigo acima que o enumerador obtm um instantneo da coleo subjacente. Os enumeradores so projetados para acessar colees somente para leitura e voc pode ter vrios enumeradores independentes ativos na mesma coleo, ao mesmo tempo. Se alguma mudana for implementada na coleo subjacente, o instantneo perder a sincronia, o que faz com que a IEnumerator lance uma InvalidOperationException para lhe informar que ela j no reflete os dados subjacentes. NOTA: Qualquer tipo que queira fornecer acesso a enumerador para seus membros deve implementar a interface IEnumerator. Esta interface tem um nico membro, GetEnumerator, que retorna um ponteiro para algum objeto que implemente a interface IEnumerator.

12.3.

OUTRAS CLASSES .NET DE COLEO

Os namespaces System::Collections e System::Specialized contm diversas classes de coleo que podem ser usadas em programas C++. A Tabela 12.2 mostra as mais utilizadas. Uma dupla delas ser examinada com detalhe mais adiante para dar uma idia de como elas funcionam.

210

Tabela 12.2: As classes mais importantes dos namespaces System::Collections e System::Collections.


CLASSE ArrayList Hashtable Queue SortedList Stack StringCollection StringDictionary DESCRIO Uma matriz que pode ser aumentada dinamicamente. Uma coleo que armazena elementos por rtulo. Armazena uma lista de elementos e os acessa na mesma ordem que eles foram amostrados. Uma coleo que estende Hashtable, permitindo que voc retire elementos por ndice ou por rtulo. Acessa uma lista de elementos apenas pelo topo. Armazena uma lista de strings e as retira por indexao (definida em System::Collections::Specialized). Uma Hashtable com o rtulo fortemente tipificado para String (definida em System::Collections::Specialized).

12.3.1.

A CLASSE ArrayList

A classe ArrayList, definida no namespace System::Collections, uma matriz que voc pode aumentar (ou diminuir) dinamicamente. Por padro, as instncias desta classe so redimensionveis e nelas se pode escrever, mas a classe fornece dois mtodos static que lhe permitem criar objetos ArrayList somente para leitura e com dimenso fixa.

12.3.1.1.

EXERCCIO: CRIANDO E MANIPULANDO UM OBJETO ArrayList

O exerccio abaixo mostra como criar e manipular um objeto ArrayList. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado ArrayList. PASSO 2: No arquivo-fonte ArrayList.cpp, adicione, imediatamente abaixo da declarao using namespace System;, a linha
using namespace System::Collections;

A classe ArrayList definida no namespace System::Collections. Inserindo uma diretiva using, voc pode usar o nome (ArrayList) sem precisar qualific-lo por completo toda vez que usar dentro do cdigo (Collections::ArrayList). PASSO 3: Em _tmain, adicione o cdigo abaixo:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Demo de ArrayList"); //Cria um objeto ArrayList padro ArrayList *pal = new ArrayList(); //Observa a contagem e a capacidade Console::WriteLine(S"Capacidade = {0}", __box(pal->Capacity)); Console::WriteLine(S"Contagem = {0}", __box(pal->Count)); //Ajusta a capacidade pal->set_Capacity(10); Console::WriteLine(S"Capacidade atual = {0}", __box(pal->Capacity)); //Adiciona alguns elementos

211

pal->Add(__box(0)); pal->Add(__box(2)); pal->Add(__box(3)); pal->Insert(1, __box(1)); Console::WriteLine(S"Contagem atual = {0}", __box(pal->Count)); return 0; }//***********************************************************************

A Figura 12.9 mostra a sada do programa ArrayList.

Figura 12.9: Sada do programa ArrayList. O construtor padro da classe ArrayList cria um objeto ArrayList vazio. As duas linhas seguintes no cdigo usam as propriedades Capacity e Count para imprimir a capacidade atual da matriz ArrayList e uma contagem de quantos elementos ela possui atualmente. A Figura 12.9 mostra que, ento, a contagem 0 o que no surpreende j que voc ainda no adicionou nada e a capacidade 16. A capacidade padro de uma ArrayList 16, o que significa que voc pode adicionar at 16 itens antes que o objeto tenha que solicitar ao sistema operacional mais memria. Um construtor alternativo lhe permite especificar uma capacidade inicial diferente, como vemos abaixo:
//Cria uma ArrayList com uma capacidade de 10 elementos ArrayList *pal = new ArrayList(10);

Se voc ultrapassar a capacidade quando adiciona elementos, esta automaticamente dobrada. Se sua matriz muito grande, voc pode reduzir sua capacidade para casar com o atual nmero de elementos armazenados chamando TrimToSize. Voc tambm pode resetar a capacidade da matriz ArrayList em qualquer instante usando sua propriedade Capacity. Uma ArrayList no contm qualquer elemento at que voc adicione algum usando as funes Add ou Insert. Add adiciona um novo item no final da lista, enquanto Insert recebe um ndice maior ou igual a zero e insere um novo item nesta posio. Note uma vez mais que para adicionar um int para a ArrayList, temos que encaixot-lo para obter um ponteiro para Object. NOTA: Como ArrayList armazena objetos que so herdeiros de Object, i.e., tipos .NET, voc pode armazenar tipos misturados em uma nica ArrayList. PASSO 4: Voc pode imprimir no console os contedos da matriz ArrayList de dois modos: explicitamente, usando a funo get_Item, ou usando um enumerador. Adicione o cdigo abaixo para imprimir os contedos usando o mtodo get_Item:
Console::WriteLine(S"\nUsando o mtodo get_Item"); for(int i = 0; i < pal->Count; i++) Console::WriteLine(S"Item({0}) = {1}", __box(i), pal->get_Item(i));

O mtodo get_Item retorna uma referncia para o item em um dado ndice na coleo e lana uma ArgumentOutOfRangeException se o argumento for menor do que 0 ou maior do que a 212

contagem atual. Voc pode obter e usar um enumerador exatamente do mesmo modo utilizado na seo ENUMERADORES deste captulo:
Console::WriteLine(S"\nUsando um enumerador"); IEnumerator *ie = pal->GetEnumerator(); i = 0; while(ie->MoveNext()) Console::WriteLine(S"Item({0}) = {1}", __box(i++), ie->Current);

A Figura 12.10 mostra a sada atual do programa ArrayList.

Figura 12.10: Sada atual do programa ArrayList. PASSO 5: A sintaxe para remoo de itens da ArrayList similar usada para adicion-los. Por exemplo:
//Remove item no ndice 2 pal->RemoveAt(2);

Se voc quiser remover mais de um elemento, a funo RemoveRange recebe um ndice inicial e um nmero de elementos a remover. Alm disso, se tiver armazenado um ponteiro para um objeto na coleo, voc pode usar a funo Remove, que procurar a ArrayList e remover a primeira ocorrncia.

12.3.1.2.

OUTRAS OPERAES ArrayList

A classe ArrayList implementa as mesmas interfaces discutidas neste captulo para a classe System::Array, o que significa que ela fornece muito daquela funcionalidade. A interface IList fornece os mtodos Add, Clear, Contains, IndexOf, Insert, Remove e RemoveAt, e as propriedades Item, IsFixedSize e IsReadOnly. A interface ICollection fornece o mtodo CopyTo e as propriedades Count, IsSynchronized e SyncRoot. A interface IEnumerable fornece o mtodo GetEnumerator. 213

A interface ICloneable fornece o mtodo Clone.

Estas interfaces so usadas para especificar funcionalidades comuns s classes de coleo. Uma vez sabendo como os mtodos da interface funcionam, voc poder usar mais facilmente outras classes de coleo.

12.3.2.

A CLASSE SortedList

A classe SortedList, tambm definida no namespace System::Collections, representa uma coleo de rtulos e valores. Uma SortedList muito similar a uma Hashtable, que tambm mantm pares rtulo/valor, mas a SortedList mantm seus dados na ordem do rtulo classificado e permite que voc acesse itens pelo ndice ou pelo rtulo. Neste seo, voc ver como a classe SortedList funciona e poder aplicar o que aprender tambm para a classe Hashtable. NOTA: Pelo fato de ter que manter a ordem de classificao, operaes em uma SortedList usualmente so mais lentas do que as equivalentes Hashtable. Assim, s use uma SortedList se precisar de acesso aos elementos por ndice e por rtulo. SortedList ordena suas entradas de dois modos: Os objetos armazenados na SortedList podem implementar a interface IComparable com seu mtodo CompareTo. Todos os tipos de valor, tais como as classes de nmero e de string, implementam esta interface e voc deve implement-la em quaisquer outros tipos definidos pelo usurio cujos valores podem ser ordenados. Um objeto comparador externo pode ser fornecido para implementar a interface IComparer com seu mtodo Compare.

12.3.2.1.

EXERCCIO: CRIANDO E MANIPULANDO UM OBJETO SortedList

O exerccio abaixo mostra como criar e manipular um objeto SortedList. Como um exemplo, suponha que voc queira manter uma lista de nomes de empregados juntos com suas extenses telefnicas. Uma SortedList funciona bem neste caso, usando o nome como o rtulo e a extenso telefnica como o valor. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado SortedList. PASSO 2: No arquivo-fonte SortedList.cpp, adicione, abaixo da declarao using namespace System;, a linha:
using namespace System::Collections;

A classe SortedList definida no namespace System::Collections e, inserindo uma diretiva using, voc j sabe que pode usar o nome da classe sem ter que qualific-la completamente. PASSO 3: Em _tmain, adicione o cdigo abaixo para criar um objeto SortedList e adicionar nele alguns dados:
//FUNO PRINCIPAL******************************************************** int _tmain() { SortedList *psl = new SortedList(); psl->Add(new String("Diva"), __box(1044));

214

psl->Add(new String("Wally"), __box(2213)); psl->Add(new String("Ted"), __box(1110)); psl->Add(new String("Alice"), __box(3375)); }//***********************************************************************

Como acontece com a ArrayList discute na seo anterior, uma SortedList tem uma capacidade padro e automaticamente aumentar sua capacidade se necessrio. Construtores alternativos lhe permitem criar classes SortedList com capacidades iniciais particulares e voc pode aparar excessos usando a funo TrimToSize. Voc tambm pode usar o metafile set_Item para adicionar um par rtulo/valor, com a mesma sintaxe de Add. Em C++, no h diferena entre o uso de set_Item e Add. NOTA: Os rtulos no podem ser ponteiros null, mas voc pode usar ponteiros null como valores. PASSO 4: Adicione cdigo para imprimir os valores da SortedList:
Console::WriteLine(S"Valores"); for(int i = 0; i < psl->get_Count(); i++) Console::WriteLine(psl->GetByIndex(i));

O lao usa a funo GetByIndex para reter cada valor e a Figura 12.11 mostra a sada do programa. Note que a ordem 3375-1044-1110-2213 reflete a ordem alfabtica dos rtulos.

Figura 12.11: Sada atual do programa SortedList. PASSO 5: Alm de reter valores por ndice, voc pode ret-los por rtulo, como abaixo:
Console::WriteLine(S"\nRtulos e valores"); for(int i = 0; i < psl->get_Count(); i++) Console::WriteLine(S"{0}\t{1}", psl->GetKey(i), psl->GetByIndex(i));

A Figura 12.12 mostra a sada.

Figura 12.12: Sada atual do programa SortedList. 215

Voc obter o valor para um dado rtulo, usando:


Console::WriteLine(S"O valor para o rtulo 'Alice' {0}", psl->get_Item(new String("Alice")));

O mtodo get_Item checa seu argumento e retorna o valor associado se encontrar um rtulo idntico ao argumento; caso contrrio, retorna um ponteiro null. PASSO 6: Voc tambm pode modifica entradas na lista por rtulo ou por valor, como abaixo:
//Muda o valor associado com o rtulo 'Alice' psl->set_Item(new String("Alice"), __box(5555)); //Muda o valor no ndice 3 psl->SetByIndex(3, __box(1010));

O mtodo set_Item armazena um valor dado um rtulo. Se o rtulo j existir, seu valor associado sobrescrito; caso contrrio, um novo par rtulo/valor criado. SetByIndex funcionar somente se houver rtulos e trocar o valor em um dado ndice.

12.3.2.2.

OUTRAS OPERAES SortedList

Voc pode usar os mtodos IndexOfKey e IndexOfValue para retornar o ndice de um rtulo ou valor. Em ambos os casos, se o rtulo ou valor solicitado no existir na coleo, o mtodo retornar 1. De modo similar, as funes ContainsKey e ContainsValue retornaro true se a coleo contiver um dado valor ou rtulo. Se voc quiser deletar itens da coleo, Remove pode ser usada para remover um rtulo. RemoveByIndex remove um ndice e Clear pode remover todas as entradas.

12.3.3.

A CLASSE StringCollection

A classe StringCollection, definida no namespace System::Collections::Specialized, representa uma coleo de strings. StringCollection lhe permite acessar elementos por ndice e tambm lhe permite adicionar, remover e inserir itens. Como a classe StringCollection implementa as interfaces ICollection, IEnumerable e IList, ela implementa os mtodos associados com estas interfaces. Assim, ela fornece muitas das funcionalidades que voc j encontrou neste captulo, como Clear, Count, Contains e CopyTo.

12.3.3.1.

EXERCCIO: CRIANDO E MANIPULANDO UM OBJETO StringCollection

Neste exerccio, voc criar e usar um objeto StringCollection. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado StringCollection. PASSO 2: No arquivo-fonte StringCollection.cpp, adicione, aps a declarao using namespace System;, a linha:
using namespace System::Collections::Specialized;

216

A classe StringCollection System::Collections::Specialized.

definida

no

namespace

PASSO 3: Em _tmain, adicione o cdigo abaixo para criar um objeto StringCollection e adicionar dados a ele:
StringCollection *psc = new StringCollection(); psc->Add(new psc->Add(new psc->Add(new psc->Add(new String("Bom")); String("Prestativo")); String("Amigo")); String("Nefasto"));

Ao contrrio os objetos ArrayList e SortedList, StringCollection criado sem nenhuma capacidade e simplesmente cresce com os itens que so adicionados. PASSO 4: Como StringCollection implementa as interfaces ICollection, IList e IEnumerable, ela suporta muitas das funcionalidades j vistas nas outras classes aqui discutidas, incluindo a habilidade para usar enumeradores. Adicione o cdigo abaixo em _tmain para imprimir os contedos da coleo no console:
StringEnumerator *ie = psc->GetEnumerator(); while(ie->MoveNext()) Console::WriteLine(S"A string {0}", ie->Current);

12.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 12

A Tabela 12.3 mostra um resumo deste captulo. Tabela 12.3: Resumo do captulo 12.
PARA Criar uma matriz de tamanho fixo de tipos embutidos C++ Criar uma matriz gerenciada de tipos embutidos C++ Criar uma matriz no gerenciada de tipos .NET Indexar os membros de uma matriz gerenciada FAA ISTO Use uma matriz C++ nativa. Use a palavra-chave __gc quando criar a matriz. Ex.: int mt1 __gc[] = new int __gc[10]; Use a palavra-chave __nogc quando criar a matriz. Ex.: Int32 mt2 __nogc[22]; Use a propriedade Count com um lao como int mt1 __gc[] = new int __gc[10]; for(int i = 0; i < mt1->Count; i++) //Fazer alguma coisa Ou obtenha um enumerador e use o mtodo MoveNext e a propriedade Current, como IEnumerator *ie = sa->GetEnumerator(); while(ie->MoveNext()) Console::WriteLine(ie->Current); Use a classe ArrayList. Use as classes SortedList ou Hashtable. Use a classe StringCollection.

Criar uma matriz dinmica Manter uma lista de pares rtulo/valor Manter uma lista de strings

217

13.

CAPTULO 13: PROPRIEDADES

Neste captulo, voc aprender O que so propriedades; Quais as propriedades suportadas pelas extenses gerenciadas para C++; Como as propriedades so implementadas.

As propriedades estiveram disponveis em algumas linguagens de programao como a Microsoft Visual Basic por algum tempo, mas o .NET Framework adicionou suporte para elas na MSIL (Microsoft Intermediate Language) de modo que elas podem ser implementadas em qualquer linguagem do .NET Framework. Neste captulo, voc ver quais as propriedades que podem freqentemente conduzir a um estilo mais natural de programao sem sacrificar a robustez ou violar princpios da programao orientada para objetos.

13.1.

O QUE SO PROPRIEDADES?

Uma doutrina aceita h muito tempo em programao orientada para objetos consiste em classificar como uma m idia permitir que o usurio tenha acesso direto aos membros de dados que compem suas classes. H duas fortes razes para isto: Se os usurios acessarem membros de dados diretamente, eles vo conhecer alguma coisa sobre a implementao da classe e isto pode limitar sua habilidade para modificar a implementao posteriormente. Os usurios das suas classes podem deliberada ou acidentalmente corromper dados em objetos usando valores de modo no apropriado, possivelmente levando a falhas do programa ou outros resultados indesejveis.

Portanto, recomendvel que voc esconda membros de dados, tornando-os private e lhes fornecendo acesso indireto atravs das funes-membros. Em C++ tradicional, o acesso indireto freqentemente tem sido implementado usando os membros get e set. Assim, um membro de dados chamado date pode ser acessado usando um par de funes-membros set_date e get_date. Este mtodo funciona bem, mas o cdigo cliente sempre tem que chamar diretamente as funes get e set. As propriedades no .NET Framework lhe fornecem um modo para implementar um membro de dados virtual para uma classe. Voc implementa as propriedades get e set e o compilador as converte em chamadas para o mtodo get ou set apropriado. Por exemplo:
MyClass *pmc = new MyClass; pmc->Name = "Fred"; //Chamada pmc->set_Name("Fred") String s = pmc->Name; //Chamada pmc->get_Name()

O que aparece para o usurio que esta MyClass tem um membro de dados chamado Name e que a propriedade pode ser usada exatamente do mesmo modo que um membro de dados real. Se voc j programou no Visual Basic, a idia de implementar propriedades usando os mtodos get, set e let lhe deve soar familiar. No .NET Framework, as propriedades podem ser criadas e usadas em qualquer linguagem .NET. Assim, voc pode criar uma classe em Visual Basic e ainda usar suas propriedades em um programa C++, e vice-versa.

218

13.1.1.

OS DOIS TIPOS DE PROPRIEDADES

Dois tipos de propriedades so suportados pelas extenses gerenciadas pelo C++: propriedades escalares e propriedades indexadas. Uma propriedade escalar permite acesso a um nico valor usando os mtodos get e set. Por exemplo, uma propriedade Name implementaria as funes get_Name e set_Name para dar acesso aos dados do nome subjacente. importante notar que no h propriedade para representar um membro de dados simples da classe gerenciada. Uma propriedade pode representar valores derivados. Por exemplo, se uma classe tiver um membro data de nascimento, seria possvel implementar uma propriedade age para calcular o ano. As propriedades tambm podem representar valores bem mais complexos, que podem envolver o uso de dados de outras fontes, tais como procura em bancos de dados ou acesso a URLs. Uma propriedade indexada pode ser acessada como se fosse uma matriz, i.e., usando a notao de colchetes ([]) tradicionais do C++. NOTA: Se alguma vez j se encontrou com o operador [] sobrecarregado no C++ tradicional, voc achar que as propriedades indexadas fornecem funcionalidade similar, mas aqui no necessrio que voc faa sua prpria sobrecarga do operador. As propriedades indexadas ainda usam os mtodos get e set para implementao e o compilador automaticamente gera o cdigo requerido de modo que os clientes possam usar a notao []. Assim, se o compilador v uma propriedade que pode ser implementada como uma propriedade indexada, ele automaticamente gera o cdigo. As prximas sees deste captulo demonstram como implementar propriedades escalares e indexadas.

13.2.

IMPLEMENTANDO PROPRIEDADES ESCALARES

Como mencionamos na seo anterior, uma propriedade escalar a que lhe fornece acesso a um nico membro de dados usando os mtodos get e set.

13.2.1.

EXERCCIO: IMPLEMENTANDO PROPRIEDADES ESCALARES DE UMA CLASSE

Neste exerccio, voc ver como implementar propriedades escalares em uma classe gerenciada, usando uma classe simples, Person, que contm os membros name e age. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Person. PASSO 2: Aps a declarao using namespace System;, adicione a seguinte definio de classe:
//DEFINIO DA CLASSE Person********************************************** __gc class Person { String *name; int age; public: Person()//Construtor da classe Person+++++++++++++++++++++++++++++++++ { name = 0; age = 0;

219

}//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //A propriedade Name __property String* get_Name()//+++++++++++++++++++++++++++++++++++++++ { return name; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __property void set_Name(String *s)//+++++++++++++++++++++++++++++++++ { name = s; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //A propriedade Age __property int get_Age()//++++++++++++++++++++++++++++++++++++++++++++ { return age; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __property void set_Age(int n)//++++++++++++++++++++++++++++++++++++++ { age = n; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ };//**********************************************************************

A classe tem dois membros de dados private que suportam o nome e a idade da pessoa. As propriedades so implementadas usando os mtodos get e set, e voc informa ao compilador que estes so mtodos de propriedade (em preveno contra alguns outros mtodos que possam aparecer com os prefixos get_ e set_) usando a palavra-chave __property. O compilador usa a parte do nome aps get_ ou set_ para gerar o membro de dados virtual que os clientes usaro. No cdigo acima, get_Name e set_Name iro equivaler na classe a um membro de dados virtual chamado Name. NOTA: Voc no pode usar get_name e set_name como os nomes dos seus mtodos de propriedade porque fazendo isto obteria um membro virtual chamado name, que colidiria com o membro de dados real existente chamado name. Porm, comum usar o nome de um membro de dados existente com a primeira letra maiscula para o nome da propriedade. PASSO 3: Voc pode usar a propriedade do cdigo C++ como se ela fosse um membro de dados real da classe. Na funo _tmain, adicione o seguinte cdigo para testar a propriedade:
//FUNO PRINCIPAL******************************************************** int _tmain() { //Cria um objeto Person Person *pp = new Person(); //Fixa o nome e a idade usando propriedades pp->Name = "Fred"; pp->Age = 77; //Acessa as propriedades Console::WriteLine(S"A idade de {0} {1}", pp->Name, __box(pp->Age)); return 0; }//***********************************************************************

Uma vez criado e inicializado o objeto Person, os membros name e age podem ser acessados atravs dos membros de dados virtuais Name e Age que foram gerados pelo compilador.

220

13.2.2.

ERROS EM PROPRIEDADES

O que acontece se um mtodo de propriedade get ou set encontrar um erro? Considere o cdigo abaixo:
//Fixa o nome e a idade usando propriedades pp->Name = "spiro"; pp->Age = -31;

Esta uma situao boa para usar as excees discutidas no captulo 11. O mtodo de propriedade set precisa comunicar que o valor passado no aceitvel e as excees fornecem o melhor modo de fazer isto. Voc pode modificar a funo set_Age para checar seu argumento usando:
__property void set_Age(int n)//++++++++++++++++++++++++++++++++++++++ { if(n < 0) throw new ArgumentException("Idades negativas no so aceitas"); age = n; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Se voc tentar fixar um valor negativo para a idade, uma ArgumentException, capturada no cdigo de chamada ser lanada. REGRAS PARA CONSTRUO DE PROPRIEDADES ESCALARES Algumas regras governam o modo como as propriedades escalares so criadas em C++ gerenciado. Primeiramente, todas as propriedades devem ser declaradas usando a palavra-chave __property. As propriedades so implementadas usando os mtodos get e set, e qualquer mtodo cujo nome comece com get_ ou set_ deve definir um mtodo de propriedade get ou set. Os nomes dos mtodos get e set devem ser o mesmo, tirando o prefixo e, como j falamos, voc no pode usar o mesmo nome para o membro de dados virtual advindo da propriedade e o membro de dados real da classe. Os nveis de acesso aos mtodos get e set podem ser diferentes. Assim, por exemplo, uma classe pode ter um mtodo get public e um mtodo set protected (visvel na classe-base e nas suas derivadas). As propriedades podem ser virtuais e mesmo virtuais puras. Alm disso no preciso que os mtodos get e set tenham o mesmo especificador virtual.

13.2.3.

PROPRIEDADES SOMENTE PARA LEITURA E SOMENTE PARA ESCRITA

No sempre que voc precisa fornecer os mtodos get e set para uma propriedade. Se no fornecer um mtodo set, voc estar produzindo uma propriedade somente para leitura. Se omitir o mtodo get, voc ter uma propriedade somente para escrita (que possvel embora menos comum do que a variedade somente para leitura).

13.2.3.1.

EXERCCIO: IMPLEMENTANDO UMA PROPRIEDADE SOMENTE PARA LEITURA E CRIANDO UMA PROPRIEDADE DERIVADA

Este exerccio mostra como implementar uma propriedade somente para leitura e tambm ilustra como criar uma propriedade derivada. Voc mudar a classe Person do exerccio anterior de modo que ele inclua a data de nascimento ao invs da idade. Ento, a propriedade derivada Age calcular a idade da pessoa a partir a data de nascimento. Esta obviamente uma propriedade derivada porque voc no pode mudar a idade de algum sem mudar sua data de nascimento. Tambm obviamente uma 221

propriedade somente para leitura porque ela sempre calculada e no pode ser fixada pelos usurios da classe. PASSO 1: Abra o projeto Person. PASSO 2: Edite o novo cdigo abaixo para a classe Person:
//DEFINIO DA CLASSE Person********************************************** __gc class Person { String *name; int dd, mm, yyyy; public: Person()//Construtores da classe Person+++++++++++++++++++++++++++++++ { name = 0; dd = 0; mm = 0; yyyy = 0; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Person(String *n, int d, int m, int y)//++++++++++++++++++++++++++++++ { name = n; dd = d; mm = m; yyyy = y; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //A propriedade Name __property String* get_Name()//+++++++++++++++++++++++++++++++++++++++ { return name; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __property void set_Name(String *s)//+++++++++++++++++++++++++++++++++ { name = s; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //A propriedade somente para leitura Age __property int get_Age()//++++++++++++++++++++++++++++++++++++++++++++ { DateTime now = DateTime::get_Now(); return now.get_Year() - yyyy; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ };//**********************************************************************

Agora a classe tem quatro membros de dados: uma String para o nome e trs inteiros para suportar as datas de nascimento. Um segundo construtor para a classe usado para criar um objeto com a data de nascimento recebendo os valores dos argumentos. NOTA: Seria mais natural usar um objeto System::DateTime para representar a data de nascimento, mas voc no pode ter ponteiros para tipos gerenciados como membros de uma classe __gc. Agora, a propriedade Age possui apenas o mtodo get, que retm um objeto DateTime representando a data atual e, ento, calcula a idade pela diferena entre o ano atual e o ano armazenado. PASSO 3: Use as propriedades Name e Age como no exemplo anterior:
//FUNO PRINCIPAL********************************************************

222

int _tmain() { //Cria um objeto Person Person *pp = new Person("Fred", 4, 9, 1955); //Acessa as propriedades Name e Age Console::WriteLine(S"A idade de {0} {1}", pp->Name, __box(pp->Age)); return 0; }//***********************************************************************

Voc no pode fixar a propriedade Age porque no forneceu uma funo set_Age. Se tentasse atribuir valor a Age, obteria um erro de compilao.

13.3.

IMPLEMENTANDO PROPRIEDADES INDEXADAS

Agora que voc sabe como implementar uma propriedade escalar, vamos considerar as propriedades indexadas, tambm conhecidas como indexadores. Eles so teis para classes que tm membros de dados que so colees de itens e onde voc deseja acessar um dos itens na coleo.

13.3.1.

O EXEMPLO DO BANCO

Um bom exemplo consiste em uma classe Bank que mantm uma coleo de contas. Se no usar propriedades, voc tenderia a produzir cdigos para acessar membros da classe Bank como no exemplo abaixo:
//Obtm um ponteiro para uma das contas seguradas pelo banco Account *pacc = theBank->getAccount(1234567);

Uma propriedade indexada lhe permitir acessar os membros Account usando notao de matriz, como no exemplo:
//Obtm um ponteiro para uma das contas seguradas pelo banco Account *pacc = theBank->Account[1234567];

Voc pode implementar os mtodos get e set para propriedades indexadas de modo que possa uslos em ambos os lados do sinal de igualdade (=). O fragmento de cdigo abaixo usa duas propriedades, com a primeira propriedade indexada permitindo acesso a uma conta e a segunda permitindo acesso a um limite de cobertura:
//Fixa o limite de cobertura para uma das contas theBank->Account[1234567]->Overdraft = 250.0;

Agora vamos comear um longo exerccio, implementando as classes Bank e Account e tambm mostrando como criar e usar as propriedades escalares e indexadas.

13.3.2.

EXERCCIO: IMPLEMENTANDO A CLASSE Bank

PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Banker. PASSO 2: Adicione a definio de uma nova classe chamada Bank dando um clique com o boto direito do mouse no nome do projeto no Solution Explorer, escolhendo Add no menu pop-up e, ento, escolhendo Add Class do menu hierrquico que aparece (Figura 13.1). 223

Figura 13.1: Adicionando uma nova classe ao projeto Banker. PASSO 3: Na caixa de dilogo Add Class que aparece (Figura 13.2), voc escolhe o tipo de classe a ser adicionada. Marque o cone Generic C++ Class e clique no boto Open.

Figura 13.2: Escolhendo o modelo de classe Generic C++ Class. 224

PASSO 4: O Generic C++ Class Wizard constri o esqueleto de uma nova classe para voc. Preencha os campos desta caixa de dilogo como indicado na Figura 13.3 e, ento, clique no boto Finish.

Figura 13.3: Caixa de dilogo Generic C++ Class Wizard. O wizard cria uma classe genrica que simplesmente consiste de um construtor e um destrutor. A definio da classe estar no arquivo Bank.h e a sua implementao, no Bank.cpp. PASSO 5: Voc precisa editar o cdigo da classe ajustado para este aplicativo. Abra o arquivo de cabealho Bank.h e realize as seguintes edies: Delete a definio do destrutor da classe; Adicione a palavra-chave __gc definio da classe porque voc precisa de uma classe gerenciada; Digite a linha #using <mscorlib.dll> imediatamente aps a declarao #pragma once. Esta linha necessria quando definimos tipos gerenciados.

O cdigo final do arquivo de cabealho este:


////////////////////////////////////////////////////////////////////////// //Bank.h #pragma once #using <mscorlib.dll> //DEFINIO DA CLASSE Back************************************************ __gc class Bank { public: Bank(void);

225

};//********************************************************************** //////////////////////////////////////////////////////////////////////////

PASSO 6: No arquivo-fonte Bank.cpp, faa as correspondentes edies: Remova a implementao do destrutor; Adicione uma chamada a Console::WriteLine no construtor de modo que voc possa verificar que ele est sendo chamado. Adicione a declarao using namespace System; aps a linha #using de modo que Console e outras classes do namespace System podem ser usadas sem qualificao adicional.

O resultado destas edies o cdigo:


////////////////////////////////////////////////////////////////////////// //Bank.cpp #include "StdAfx.h" #include ".\bank.h" #using <mscorlib.dll> using namespace System; //IMPLENTAES DE MEMBROS DA CLASSE Bank********************************** Bank::Bank(void)//++++++++++++++++++++++++++++++++++++++++++++++++++++++++ { Console::WriteLine(S"Bank: Construtor"); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //************************************************************************ //////////////////////////////////////////////////////////////////////////

PASSO 7: Para verificar se tudo est correto, adicione o cdigo abaixo para a funo _tmain, no arquivo-fonte Banker.cpp:
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Exemplo Bank"); //Cria um objeto Bank Bank *theBank = new Bank(); return 0; }//***********************************************************************

PASSO 8: Voc tambm deve incluir o arquivo de cabealho Bank.h em Banker.cpp de modo que o compilador saiba onde localizar a declarao da classe Bank. Adicione o cdigo abaixo aps a linha #include stdafx.h, em Banker.cpp:
#include "Bank.h"

Compile e execute este cdigo e voc ver a mensagem do construtor impressa no console.

13.3.3.

EXERCCIO: ADICIONANDO UMA CLASSE Account

A prxima etapa envolve a criao da classe Account, usando o Generic C++ Class Wizard de modo similar ao anterior. PASSO 1: Adicione uma classe chamada Account ao projeto Banker, exatamente do mesmo modo como voc adicionou a classe Bank. 226

PASSO 2: Faa as mesmas edies para a classe Account. O cdigo final no arquivo de cabealho Account.h :
////////////////////////////////////////////////////////////////////////// //Account.h #pragma once #using <mscorlib.dll> //DEFINIO DA CLASSE Account********************************************* __gc class Account { public: Account(void); };//********************************************************************** //////////////////////////////////////////////////////////////////////////

A implementao no arquivo-fonte Account.cpp :


////////////////////////////////////////////////////////////////////////// //Account.cpp #include "StdAfx.h" #include ".\Account.h" #using <mscorlib.dll> using namespace System; //IMPLENTAES DE MEMBROS DA CLASSE Account******************************* Account::Account()//++++++++++++++++++++++++++++++++++++++++++++++++++++++ { Console::WriteLine(S"Account: Construtor"); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //************************************************************************ //////////////////////////////////////////////////////////////////////////

PASSO 3: Adicione alguma estrutura para a classe Account. As contas tero um nmero de conta, um balano e um limite de cobertura. Assim, adicione trs membros private definio da classe Account em Account.h:
long accNumber; double balance; double limit; //Nmero da conta //Balano atual //Limite de cobertura

PASSO 4: Edite a definio e a implementao do construtor como abaixo de modo que os trs valores sejam passados e usado para inicializar trs variveis:
Account::Account(long num, double bal, double lim)//++++++++++++++++++++++ { Console::WriteLine(S"Account: Construtor"); //Checagem bsica de sanidade if(num < 0 || lim < 0) throw new ArgumentException(S"Problemas com os argumentos o construtor"); //Inicializa valores accNumber = num; balance = bal; limit = lim; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

A checagem bsica de sanidade simplesmente verifica se o nmero da conta e o limite de cobertura so negativos e, caso sejam, lana uma ArgumentException. 227

13.3.4.

EXERCCIO: CRIANDO PROPRIEDADES PARA A CLASSE Account

Uma vez construda a classe Account, voc pode adicionar propriedades para permitir acesso aos trs membros escalares. PASSO 1: Adicione um mtodo get public a Account.h para permitir acesso somente para leitura ao nmero da conta:
__property long get_AccountNumber()//+++++++++++++++++++++++++++++++++ { return accNumber; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Voc pode adicionar a definio da funo inline na definio da classe. Tenha o cuidado de coloc-la na seo public. PASSO 2: Voc tambm precisa adicionar uma propriedade somente para leitura para o nmero balance porque na vida real voc que qualquer pessoa possa modificar os balanos nas contas do seu banco. Use o cdigo:
__property double get_Balance()//+++++++++++++++++++++++++++++++++++++ { return balance; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 3: Adicione uma propriedade de leitura/escrita para o limite de cobertura porque sempre possvel que o limite possa mudar ao longo do tempo.
__property double get_Limit()//+++++++++++++++++++++++++++++++++++++++ { return limit; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __property void set_Limit(double lim)//+++++++++++++++++++++++++++++++ { if(lim < 0) throw new ArgumentException(S"O limite de cobertura no pode ser negativo"); limit = lim; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Para implementar estas propriedades inline na definio da classe, devemos usar o nome completo da exceo, System::ArgumentException, ou apenas ArgumentException. Para usar esta ltima qualificao precisamos adicionar uma linha using namespace System; no arquivo de cabealho Account.h. Faa isto. PASSO 4: Precisamos testar esta implementao, adicionado em _tmain algum cdigo para criar um novo objeto Account e acessar suas propriedades. Por enquanto, inclua o arquivo de cabealho Account.h no arquivo-fonte Banker.cpp e adicione o cdigo abaixo para criar um objeto Account:
Account *theAccount = new Account(123456, 0.0, 0.0);

13.3.5.

ADICIONANDO CONTAS NA CLASSE Bank

O propsito da classe Bank dar suportes s contas. Assim, o prximo passo modificar a classe Bank para suportar uma coleo de objetos Account. Ao invs de projetar algo do nada, voc usar a classe System::Collections::ArrayList (veja o captulo 12) para suportar as contas. 228

13.3.5.1.

EXERCCIO: IMPLEMENTANDO OS MTODOS Add E Remove

Os mtodos Add e Remove fornecem um modo para manipular a coleo de contas segurada pela classe Bank. PASSO 1: No arquivo de cabealho Bank.h, adicione as duas linhas de cdigo abaixo aps a diretiva #using <mscorlib.dll>:
using namespace System::Collections; #include "Account.h"

A declarao using facilitar o uso de ArrayList na classe Bank e voc precisar referenciar a classe Account mais adiante. PASSO 2: Adicione uma varivel private ArrayList para a classe Bank:
ArrayList *accts;

PASSO 3: Em Bank.cpp, adicione a linha de cdigo abaixo para o construtor Bank com a finalidade de criar o membro ArrayList:
accts = new ArrayList();

PASSO 4: Adicione na classe Bank o cdigo abaixo para o mtodo public e inline Add:
bool Add(Account *pAcc)//+++++++++++++++++++++++++++++++++++++++++++++ { //Checa se a conta j est na lista if(accts->Contains(pAcc)) return false; else accts->Add(pAcc); return true; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Add recebe um ponteiro para um objeto Account e, ento, usa o mtodo Contains de ArrayList para checar se a conta j existe na coleo. Se no existir, Add adiciona a conta coleo. Adicione cdigo similar para Remove:
bool Remove(Account *pAcc)//++++++++++++++++++++++++++++++++++++++++++ { //Checa se a conta j existe na lista if(accts->Contains(pAcc)) { accts->Remove(pAcc); return true; } else return false; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Remove checa se a conta est na ArrayList e a remove em caso afirmativo. No necessrio chamar Contains porque Remove silenciosamente nada far se voc tentar remover um item que no estiver na lista. Porm, os usurios podem querer saber que a conta que eles esto tentando remover j no est na coleo.

13.3.5.2.

EXERCCIO: IMPLEMENTANDO UMA PROPRIEDADE INDEXADA PARA RETER CONTAS

229

Agora, voc pode manipular a coleo de contas, adicionando e removendo itens. Se quiser observar uma conta particular, voc provavelmente faria isto atravs do nmero da conta e uma propriedade indexada fornece uma boa maneira de acessar contas atravs dos seus nmeros. Voc s precisar reter as referncias a Account usando a propriedade. Portanto, implementar uma propriedade indexada somente para leitura. PASSO 1: No arquivo de cabealho Bank.h, adicione o cdigo abaixo para implementar a propriedade:
//Propriedade indexada para retornar uma conta __property Account* get_Acct(long number)//+++++++++++++++++++++++++++ { IEnumerator *ie = accts->GetEnumerator(); while(ie->MoveNext()) { Account *pa = dynamic_cast<Account*>(ie->get_Current()); if(pa->AccountNumber == number) return pa; } throw new ArgumentOutOfRangeException(S"Este nmero de conta no existe"); }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Por que a propriedade no se chamou get_Account? O problema que este nome resultaria em um membro de dados virtual chamado Account adicionado classe Bank, o que colidiria com o nome da classe Account. A funo get_Acct usa um enumerador discutido no captulo 12 para indexar as contas na ArrayList. Enquanto houver elementos no coleo, MoveNext se desloca para o prximo elemento que pode, ento, ser acessado usando o mtodo Current. O ponteiro retornado por Current um Object* genrico. Assim, ele precisar ser convertido em um Account* antes que voc possa obter o nmero da conta. A converso feita usando uma dynamic_cast, mas no precisa checar o retorno da cast neste caso porque voc pode estar certo de que no h nada na coleo exceto ponteiros Account. Quando encontrar uma conta cujo nmero casa com o passado para a funo, o ponteiro retornado. Se nada for encontrado, uma exceo lanada porque tentar acessar uma conta no existente equivalente a ler em voz alta o fim de uma matriz, i.e., um erro srio que deve ser notificado ao chamador. PASSO 3: Teste a sada da classe Bank adicionando o cdigo abaixo funo _tmain. Certifique-se de que os arquivos de cabealho Bank.h e Account.h foram includos em Banker.cpp.
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Exemplo Bank"); //Cria um objeto Back Bank *theBank = new Bank(); //Cria algumas contas Account *accountOne = new Account(123456, 100.0, 0.0); Account *accountTwo = new Account(234567, 1000.0, 0.0); Account *accountThree = new Account(345678, 10000.0, 0.0); //Adiciona as contas ao banco theBank->Add(accountOne); theBank->Add(accountTwo); theBank->Add(accountThree);

230

//Usa a propriedade indexada para acessar uma conta Account *pa = theBank->Acct[234567]; Console::WriteLine(S"O balano {0}", __box(pa->get_Balance())); return 0; }//***********************************************************************

Aps criar um objeto Bank e alguns objetos Account, voc adiciona os objetos Account coleo de Bank chamando Add. Ento, voc pode usar a propriedade indexada para acessar uma conta pelo nmero e usar um ponteiro para mostrar o balano. Teste a propriedade passando um nmero de conta no existente e verifique que uma exceo lanada.

13.4.

RPIDAS REFERNCIAS SOBRE O CAPTULO 13

A Tabela 13.1 mostra um resumo deste captulo. Tabela 13.1: Resumo do captulo 13.
PARA Criar uma propriedade para uma classe C++ FAA ISTO Use a palavra-chave __property com os mtodos get e set. Ex.: __property void set_Weight(int n) { //... } __property int get_Weight() { //... } Implemente apenas o mtodo get.

Implementar uma propriedade somente para leitura Implementar uma propriedade somente para escrita Implementar uma propriedade indexada

Implemente apenas o mtodo set.

Implemente uma propriedade cujos mtodos get e set recebam parmetros que sero usados para determinar que valor obter ou fixar. Ex.: __property void set_Pay(Person*, Amount*); __property Amount* get_Pay(Person*);

231

14.

CAPTULO 14: DELEGADOS E EVENTOS

Neste captulo, voc aprender O que so delegados; Como criar e usar delegados; O que so eventos; Como criar e usar eventos.

Delegados e eventos so extremamente potentes em importantes construes no Microsoft .NET Framework. Os eventos em particular so largamente usados em programas de GUIs como um meio de comunicao entre componentes, mas tanto delegados quanto eventos podem ser usados com bons resultados em cdigo no GUI.

14.1.

O QUE SO DELEGADOS?

O mecanismo de ponteiro de funo em C e C++ tem sido usado por muitos anos pelos programadores e um modo muito til de implementar mecanismos como manipuladores de eventos. Infelizmente, ponteiros de funo so caractersticas da linguagem C++. Assim, eles so inteis no ambiente .NET, onde as caractersticas precisam ser acessveis a muitas linguagens. Se voc estiver interessado em conhecer mais sobre ponteiros de funo e como eles funcionam, veja o destaque, O QUE SO PONTEIROS DE FUNO?, a seguir. Delegados so o equivalente .NET aos ponteiros de funo e eles podem ser criados e usados de qualquer linguagem .NET. Eles tm uso independente e tambm formam a base para o mecanismo de evento .NET, discutido na segunda parte deste captulo. O QUE SO PONTEIROS DE FUNO? Um ponteiro normal lhe permite acessar uma varivel atravs do endereo que a contm. Um ponteiro de funo lhe permite executar uma funo usando o endereo da rotina. Exatamente do mesmo modo que pode usar um ponteiro para suportar os endereos de diferentes variveis, voc pode usar o mesmo ponteiro de funo para invocar diferentes funes. E do mesmo modo que os ponteiros normais devem ter um tipo associado com eles (de modo que voc s possa apontar em double com um double*, por exemplo), os ponteiros de funo devem ter uma assinatura de funo associada com eles. A linha de cdigo abaixo mostra como voc deve declarar um ponteiro de funo em C++:
long (*pf)(int, int);

O cdigo declara um ponteiro de funo chamado pf, que pode ser usado para invocar qualquer funo que receba dois parmetros int e retorne um long. O prottipo de funo abaixo tem a assinatura apropriada:
long func1(int, int);

Voc pode invoc-la indiretamente, como em:


pf = func1; long l = pf(3, 4); //Atribui o endereo de func1 para pf //Invoca func1() atravs de pf

232

Lembre-se que em C++ o nome de uma funo sem quaisquer parnteses avalia o seu endereo. Assim, a primeira linha recebe o endereo da funo e o armazena em pf. A segunda linha usa pf para invocar a funo. Voc pode usar um ponteiro de funo para invocar qualquer funo que case com sua assinatura e isto que torna este tipo de ponteiro til para manipular eventos. Voc pode definir um ponteiro de funo para representar o manipulador do evento e, posteriormente, conectar a funo atual ao ponteiro.

14.1.1.

O QUE FAZEM OS DELEGADOS?

Um delegado uma classe que lhe permite invocar um ou mais mtodos que tenha uma assinatura particular. Eis um exemplo simples para mostrar quando voc pode usar um delegado. Imagine que desejamos executar operaes em nmeros passando um nmero em uma funo e obtendo de volta um valor transformado, como:
double double result result result d = 3.0; result = square(d); = cube(d); = squareRoot(d); = tenToThePowerOf(d);

Em cada caso, chamamos uma funo que tem a mesma assinatura: recebe um double e retorna um double. Com delegados, podemos definir um mecanismo que nos permitir chamar qualquer um dos mtodos porque todos eles tm a mesma assinatura. No somente podemos chamar qualquer um dos quatro mtodos acima, como tambm podemos definir outros mtodos e podemos cham-los atravs do delegado contanto que a assinatura case. Isto torna possvel que uma classe ou componente defina um delegado para outras classes anexarem funes ao delegado e us-lo. Voc ver exemplos deste uso de delegados posteriormente no captulo, quando falarmos sobre eventos. Agora, queremos usar o delegado para chamar um mtodo de cada vez, mas possvel anexar mais de uma funo ao um delegado. Todas as funes sero chamadas quando o delegado invocado. O .NET Framework define a classe System::Delegate como a base para delegados que chamam um nico mtodo, e a classe System::MulticastDelegate como a base para delegados que podem chamar mais de um mtodo. Todos os delegados em C++ gerenciado so multicast.

14.1.2.

EXERCCIO: DEFININDO DELEGADOS

Este exerccio usa o exerccio das operaes numricas da seo anterior para mostrar como criar e usar um delegado simples em cdigo C++ gerenciado. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Delegate. PASSO 2: No topo do arquivo-fonte Delegate.cpp, adicione a definio de um delegado logo aps a linha using namespace System:
__delegate double NumericOp(double);

A palavra-chave __delegate usada para definir um delegado. Poderamos imaginar como se isto fosse um prottipo para uma funo chamada NumericOp, mas na verdade estamos definindo um tipo de delegado herdeiro de System::MulticastDelegate. Este delegado, chamado 233

NumericOp, pode ser ligado a qualquer funo que receba um double como um argumento e retorne um double.

14.1.3.

IMPLEMENTANDO DELEGADOS

Agora que j definiu um delegado, voc pode escrever cdigo para us-lo para chamar funes. Uma das regras para usar delegados que voc s pode usar um delegado para chamar funes que so membros das classes gerenciadas C++; voc no pode usar um delegado para chamar uma funo global ou uma funo que um membro de uma classe no gerenciada C++.

14.1.4.

EXERCCIO: DELEGADOS

CHAMANDO

FUNES-MEMBROS

static

USANDO

Vamos comear pelo caso mais simples: chamando funes-membros static usando um delegado. PASSO 1: Todas as funes que desejamos chamar precisam ser membros static de uma classe. Assim, adicione a classe abaixo no arquivo-fonte Delegate.cpp, acima da funo _tmain:
//DEFINIO DA CLASSE Ops************************************************* __gc class Ops { public: static double square(double d)//++++++++++++++++++++++++++++++++++++++ { return d * d; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ };//**********************************************************************

A definio da classe tem que usar __gc para torn-la uma classe gerenciada e ela contm um nico membro static, que simplesmente recebe um nmero e retorna o seu quadrado. PASSO 2: Na funo _tmain, crie um delegado como mostrado abaixo:
//Declara um delegado NumericOp *pOp = new NumericOp(0, &Ops::square);

Quando declara o delegado, voc cria um novo tipo de referncia chamado NumericOp. A partir da voc pode criar um objeto NumericOp. O construtor recebe dois argumentos: o primeiro um ponteiro para um objeto, usado quando chamamos membros no static. Portanto, neste exemplo, ele um ponteiro null. O segundo argumento o endereo da funo que est sendo associada ao delegado. Assim, voc usa o operador & para especificar o endereo de Ops::square. O objeto apontado por pOp , agora, configurado de modo a chamar a funo square quando ela invocada e ele receber exatamente os mesmos argumentos (e retornar o mesmo tipo) de Ops::square. NOTA: Voc no pode mudar a funo que um delegado invoca uma vez ele tenha sido criado. Neste aspecto, os delegados diferem dos ponteiros de funo C++. PASSO 3: Todo delegado tem um mtodo Invoke que voc usa para chamar a funo que estiver ligada a ele. Invoke receber os mesmos argumentos e retornar o mesmo tipo da funo que est sendo chamada. Em _tmain, adicione as linhas abaixo para usar pOp para chamar a funo square:
//Chama a funo atravs do delegado double result = pOp->Invoke(3.0); Console::WriteLine(S"O resultado de square() {0}", __box(result));

234

PASSO 4: Voc pode criar outro membro static, criar um delegado e chamar a funo. Faa este teste adicionando um segundo membro static classe Ops:
static double cube(double d)//++++++++++++++++++++++++++++++++++++++++ { return d * d * d; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

PASSO 5: Crie outro delegado do mesmo modo que o primeiro, mas desta vez passe para ele o endereo da funo cube no construtor:
//Declara um segundo delegado NumericOp *pOp2 = new NumericOp(0, &Ops::cube);

Quando chamar Invoke neste delegado, ela chamar a funo cube para voc, como abaixo:
//Chama a funo atravs do delegado double result2 = pOp2->Invoke(3.0); Console::WriteLine(S"O resultado de cube() {0}", __box(result2));

14.1.5.

USANDO DELEGADOS PARA CHAMAR FUNES-MEMBROS NO static

Voc tambm pode chamar funes-membros no static de classes usando delegados. Por definio, uma funo-membro no static tem que ser chamada em um objeto. Assim, voc precisa informar ao delegado a funo que ele vai chamar e o objeto que ele vai usar. Voc deve escrever algo como:
//Declara um delegado ligado a um membro no static MyDelegate *pDel = new MyDelegate(pMyObject, &MyClass::myFunction);

O construtor especifica o endereo de um objeto, pMyObject, e uma funo-membro da classe qual pMyObject pertence. A chamada a Invoke neste delegado equivalente a chamar diretamente pMyObject->myFunction.

14.1.6.

USANDO DELEGADOS multicast

Voc viu como possvel usar um delegado para chamar uma nica funo, mas tambm possvel a um delegado chamar mais de uma funo com uma nica chamar funo Invoke. Um delegado que faz isto chamado de delegado multicast e derivado da classe System::MulticastDelegate. NOTA: Todos os delegados que voc criar no C++ gerenciado usando a palavra-chave __delegate so multicast. Todos os objetos delegados tm uma lista de invocaes que suporta as funes a serem chamadas. A lista de invocaes para um delegado normal tem um membro. Voc manipula as listas de invocaes para delegado multicast usando os mtodos Combine e Remove. Se olhar na documentao para o mtodo Combine, voc ver que ele recebe dois ou mais objetos Delegate como argumentos. Voc no constri um delegado multicast especificando mais funes para adicionar sua lista de invocaes. Ao invs disto, um delegado multicast construdo combinando outros delegados que, por seu turno, podem ser simples ou multicast.

14.1.6.1.

EXERCCIO: CRIANDO E USANDO UM DELEGADO multicast

235

O exerccio abaixo mostra como criar e usar um delegado multicast. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Multicast. PASSO 2: No arquivo-fonte Multicast.cpp, adicione a definio de um delegado aps a linha using namespace System;.
__delegate void NotifyDelegate(int);

Este delegado, chamado NotifyDelegate, pode ser ligado a qualquer funo que receba um int como parmetro e retorne void. PASSO 3: Voc vai chamar duas funes atravs do delegado multicast. Como todas as funes chamadas por delegados tm que ser membros de uma classe gerenciada, defina duas classes aps a definio do delegado, cada uma das quais contendo uma funo-membro static:
__gc class Client1//++++++++++++++++++++++++++++++++++++++++++++++++++++++ { public: static void NotifyFunction1(int n)//---------------------------------{ Console::WriteLine(S"Client1: Adquiriu o valor {0}", __box(n)); }//------------------------------------------------------------------};//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __gc class Client2//++++++++++++++++++++++++++++++++++++++++++++++++++++++ { public: static void NotifyFunction2(int n)//---------------------------------{ Console::WriteLine(S"Client2: Adquiriu o valor {0}", __box(n)); }//------------------------------------------------------------------};//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Estas duas classes so quase idnticas, ambas definindo uma nica funo-membro static que tem a assinatura requerida pelo delegado. PASSO 4: Voc quer chamar as duas funes-membros static atravs de um delegado, mas no pode criar um delegado para ligar as duas funes diretamente. Ao invs de fazer isto, voc precisar criar dois delegados (como fez no exemplo anterior) e combin-los em um delegado multicast. Assim, defina dois delegados na funo _tmain, cada um deles ligado a um dos mtodos static.
Console::WriteLine(S"Delegados multicast"); //Declara dois delegados NotifyDelegate *pDel1, *pDel2; //Liga-os s funes pDel1 = new NotifyDelegate(0, &Client1::NotifyFunction1); pDel2 = new NotifyDelegate(0, &Client2::NotifyFunction2);

At este estgio, voc pode chamar Invoke para usar os dois delegados do mesmo modo feito no exerccio anterior. PASSO 5: Construa um delegado multicast a partir de pDel1 e pDel2, usando o mtodo Combine, static da classe Delegate:
//Combina as listas de invocaes dos dois delegados NotifyDelegate *pDel3 = dynamic_cast<NotifyDelegate*>(Delegate::Combine(pDel1, pDel2));

236

Combine um membro static da classe Delegate que recebe ponteiros para dois delegados e lhe retorna um novo delegado cuja lista de invocaes combina as entradas dos dois delegados do argumento. Voc precisa usar uma converso porque o ponteiro retornado de Combine simplesmente um Delegate* e ele precisa ser convertido em um NotifyDelegate* antes que possa ser usado. NOTA: Em outras linguagens .NET, como a Microsoft Visual Basic e a Visual C#, a sintaxe mais simples, contudo os delegados multicast so criados pelo mesmo mecanismo acima descrito. PASSO 6: O delegado multicast agora usado exatamente do mesmo modo que os delegados normais, usando o mtodo Invoke:
//Invoca o delegado multicast Console::WriteLine(S"Invocando pDel3"); pDel3->Invoke(3);

A Figura 14.1 mostra a sada atual do programa Multicast.

Figura 14.1: Sada atual do programa Multicast. Note que as funes so chamadas na ordem em que os delegados so combinados. Assim, se quiser mudar a ordem, voc precisar mudar o modo como criou o multicast. PASSO 7: Voc pode usar o delegado multicast atual para compor um outro:
//Cria um segundo delegado multicast e o invoca NotifyDelegate *pDel4 = dynamic_cast<NotifyDelegate*>(Delegate::Combine(pDel3, pDel3)); Console::WriteLine(S"Invocando pDel4"); pDel4->Invoke(3);

Neste caso, voc est combinando a lista de invocaes de pDel3 duas vezes, o que significa que obter a sada mostrada na Figura 14.2.

Figura 14.2: Sada atual do programa Multicast. 237

PASSO 8: Para finalizar este exerccio, voc pode usar o mtodo Remove para remover um item de uma lista de invocaes de delegados multicast. Eis como:
//Remove um item NotifyDelegate *pDel5 = dynamic_cast<NotifyDelegate*>(Delegate::Remove(pDel3, pDel1)); Console::WriteLine(S"Invocando pDel5"); pDel5->Invoke(3);

A funo Remove recebe dois argumentos: o delegado a ser operado e o item a ser removido. Se o item a ser removido existir na lista de invocaes do primeiro argumento, ele removido. Neste exemplo, pDel1 est sendo removido da lista de invocaes de pDel3, restando apenas pDel2 como vemos na Figura 14.3.

Figura 14.3: Sada atual do programa Multicast.

14.2.

O QUE SO EVENTOS?

A maioria, para no dizer todas as plataformas de GUI suportam a idia de eventos e, portanto, eles so maciamente utilizados em programao de GUI. Por exemplo, considere um boto. Botes no existem por si mas so usados como parte de uma interface de usurio e so controlados por algum outro item. Este item normalmente uma forma, mas tambm pode ser algum outro controle, como, por exemplo, uma barra de utilitrio. A razo fundamental para se ter um boto em uma forma permitir que, ao clicar nele, o usurio d alguma instruo de procedimento ao programa. Por exemplo, o usurio clicou no boto OK, portanto, a caixa de dilogo deve desaparecer; ou o usurio clicou no boto Print na barra de utilitrio, de modo que preciso imprimir o documento. Os eventos fornecem um mecanismo formalizado padro que permite que as fontes de eventos (tais como um boto) se liguem a receptores de eventos (tais como uma forma). Os eventos no .NET Framework implementam um mecanismo de publicao e subscrio, onde as fontes de eventos tornam pblicos os eventos que elas criaro, i.e., elas publicam os eventos, e os receptores informam fonte quais eventos lhes interessam, i.e., eles subscrevem para eventos. Os receptores tambm podem no subscrever quando j no quiserem receber um evento particular. Os eventos no .NET Framework so baseados em delegados multicast e no muito difcil ver como isto funciona. Uma fonte de evento declara um delegado para cada evento que ela quer gerar, como, por exemplo, um clique, um clique duplo, etc.. Ento, um receptor de eventos define mtodos apropriados e os passa para a fonte de eventos, que usa Combine para adicion-los aos seus delegados 238

multicast. Quando chega o instante de disparar o evento, a fonte chama Invoke no delegado, assim chamando as funes requisitadas nos receptores. O mecanismo de eventos atual simplifica a sintaxe de modo que voc no tem que lidar com delegados diretamente, e projetado para se ajustar ao mecanismo de eventos j existente no Visual Basic. O prximo exerccio lhe permite criar uma classe de fonte de eventos e classes de receptores de eventos que so registrados com a fonte e usam os eventos quando eles so disparados.

14.2.1.

EXERCCIO: IMPLEMENTANDO UMA CLASSE DE FONTE DE EVENTOS

PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Event. PASSO 2: Fontes e receptores de eventos usam delegados. Assim, defina um delegado para cada um dos eventos produzidos pela fonte. Neste exemplo, dois eventos sero usados. Portanto, no arquivofonte Event.cpp, defina aps a linha using namespace System;, os dois delegados:
__delegate void FirstEventHandler(String*); __delegate void SecondEventHandler(String*);

Os delegados definem as assinaturas dos mtodos que os receptores de eventos tm que implementar para manipular os eventos. Assim, freqentemente eles recebem nomes que terminam com Handler (manipulador). Cada um destes eventos simplesmente passar uma string como os dados do evento, mas voc pode passar dados to complexos quanto queira. PASSO 3: Adicione a implementao da classe da fonte dos eventos ao Event.cpp:
__gc class EvtSrc//Classe fonte dos eventos+++++++++++++++++++++++++++++++ { public: //Declara os eventos __event FirstEventHandler *OnFirstEvent; __event SecondEventHandler *OnSecondEvent; //Funes que produzem o evento void RaiseOne(String *pMsg)//----------------------------------------{ OnFirstEvent(pMsg); }//------------------------------------------------------------------void RaiseTwo(String *pMsg)//----------------------------------------{ OnSecondEvent(pMsg); }//------------------------------------------------------------------};//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

A primeira coisa a notar o uso da palavra-chave __event para declarar os dois eventos. Voc precisa de uma declarao __event para cada evento que quiser produzir e seu tipo o do delegado associado com o evento. Assim, no caso do primeiro objeto de evento, o tipo FirstEventHandler* para casar com o delegado FirstEventHandler. O uso da palavrachave __event faz com que o compilador gere parte do cdigo de manipulao do delegado para voc. Se estiver interessado em saber como isto ocorre, veja o texto, COMO A PALAVRA-CHAVE __event FUNCIONA?, em destaque abaixo. Ento, voc pode usar os objetos de evento na classe EvtSrc para produzir os eventos, simplesmente usando-os como se eles fossem chamadas de funo e passando-lhes o argumento apropriado.

239

COMO A PALAVRA-CHAVE __event FUNCIONA? A palavra-chave __event no usada apenas em cdigo gerenciado, mas tambm pode ser usada para descrever eventos nas classes C++ nativas e eventos COM. Quando voc declara um membro __event para uma classe em cdigo gerenciado, o compilador gera cdigo para implementar o mecanismo de delegado subjacente. Para o objeto de evento OnFirstEvent no exerccio, voc obtm os seguintes mtodos gerenciados: add_OnFirstEvent, um mtodo public que chama Delegate::Combine para adicionar um receptor para esta lista de invocaes do evento. Ao invs de chamar add_OnFirstEvent diretamente, voc usa o operador += no objeto do evento, que chama a funo para voc. remove_OnFirstEvent, um mtodo public que chama Delegate::Remove para remover um receptor desta lista de invocaes do evento. Como na funo add_, voc no chama este mtodo diretamente, mas, por meio do operador = no objeto do evento. raise_OnFirstEvent, um membro protected que chama Delegate::Invoke para chamar todos os mtodos nesta lista de invocaes do evento.

O mtodo de produo protected de modo que ele s pode ser chamado atravs dos canais apropriados e no diretamente pelo cdigo cliente.

14.2.2.

EXERCCIO: IMPLEMENTANDO UM RECEPTOR DE EVENTOS

Agora voc tem uma classe que pode ser usada para disparar eventos. Assim, a prxima coisa que voc precisa de uma classe que ouvir os eventos e agir sobre eles quando fossem gerados. PASSO 1: Adicione ao projeto Event uma nova classe chamada EvtRcv:
__gc class EvtRcv//Classe receptora dos eventos+++++++++++++++++++++++++++ { EvtSrc *theSource; public: };//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

O receptor tem que saber com que fontes de eventos ele est trabalhando para fazer a seleo a ser subscrita. Por isso preciso adicionar um membro EvtSrc* para a classe representar as fontes que estaro em funcionamento. PASSO 2: Adicione um construtor para que a classe EvtRcv receba um ponteiro para um objeto EvtSrc e verifique se ele no null. Se o ponteiro for vlido, salve-o no membro EvtSrc*.
EvtRcv(EvtSrc *pSrc)//-----------------------------------------------{ if(pSrc == 0) throw new ArgumentNullException(S" preciso ter uma fonte de eventos!"); //Salva a fonte theSource = pSrc; }//-------------------------------------------------------------------

PASSO 3: Defina as funes manipuladoras membros em EvtRcv que EvtSrc vai chamar. Como voc sabe da discusso de delegados, as assinaturas destes mtodos tero que casar com as assinaturas dos delegados usados para definir os eventos:
//Funes manipuladoras void FirstEvent(String *pMsg)//--------------------------------------{

240

Console::WriteLine("EvtRcv: Evento um, mensagem: {0}", pMsg); }//------------------------------------------------------------------void SecondEvent(String *pMsg)//-------------------------------------{ Console::WriteLine("EvtRcv: Evento dois, mensagem: {0}", pMsg); }//-------------------------------------------------------------------

FirstEvent o manipulador para o delegado FirstEventHandler e SecondEvent o manipulador para o delegado SecondEventHandler. Cada um deles simplesmente imprime no console a string que recebem. PASSO 4: Uma vez definidos os manipuladores, voc pode subscrever para a fonte de eventos. Edite o construtor para a classe EvtRcv como o cdigo abaixo:
EvtRcv(EvtSrc *pSrc)//-----------------------------------------------{ if(pSrc == 0) throw new ArgumentNullException(S" preciso ter uma fonte de eventos!"); //Salva a fonte theSource = pSrc; //Adiciona seus manipuladores theSource->OnFirstEvent += new FirstEventHandler(this, &EvtRcv::FirstEvent); theSource->OnSecondEvent += new SecondEventHandler(this, &EvtRcv::SecondEvent); }//-------------------------------------------------------------------

Voc subscreve para um evento usando o operador +=. No cdigo, voc est criando dois novos objetos delegados, que chamaro de volta para os manipuladores FirstEvent e SecondEvent no objeto atual. Isto exatamente a mesma sintaxe que voc usaria se fosse criar manualmente um delegado. A diferena est no operador += que combina o delegado recentemente criado com o delegado multicast da fonte de eventos. Como voc leu no texto destacado acima, += chama o mtodo add_OnFirstEvent gerenciado pelo compilador, que por seu turno chama Delegate::Combine. Embora tenha subscrito para todos os eventos automaticamente no construtor, voc tambm pode usar funes-membros para subscrever para eventos individuais quando necessrio. PASSO 5: O operador = lhe permite retirar a subscrio de eventos. Adicione a seguinte funomembro a EvtRcv, que lhe permitir remover a subscrio do primeiro evento:
//Remove um manipulador void RemoveHandler()//-----------------------------------------------{ //Remove o manipulador para o primeiro evento theSource->OnFirstEvent -= new FirstEventHandler(this, &EvtRcv::FirstEvent); }//-------------------------------------------------------------------

A sintaxe para usar o operador = para a remoo exatamente a mesma usada para o operador += na subscrio.

14.2.3.

EXERCCIO: JUNTANDO TUDO

Agora que escreveu as classes da fonte de eventos e do receptor de eventos, voc pode escrever algum cdigo para test-las. 241

PASSO 1: Edite a funo _tmain para criar objetos de fonte e receptor de eventos.
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Exemplo de eventos"); //Cria uma fonte EvtSrc *pSrc = new EvtSrc(); //Cria um receptor e o conecta fonte EvtRcv *pRcv = new EvtRcv(pSrc); return 0; }//***********************************************************************

O construtor EvtSrc no recebe argumentos, enquanto o construtor EvtRcv recebe um ponteiro EvtSrc vlido. Neste ponto, o receptor configurado para os eventos que sero disparados da fonte.
//FUNO PRINCIPAL******************************************************** int _tmain() { Console::WriteLine(S"Exemplo de eventos"); //Cria uma fonte EvtSrc *pSrc = new EvtSrc(); //Cria um receptor e o conecta fonte EvtRcv *pRcv = new EvtRcv(pSrc); //Eventos disparados Console::WriteLine(S"Dispara os dois eventos"); pSrc->RaiseOne(S"Este o primeiro evento"); pSrc->RaiseTwo(S"Este o segundo"); return 0; }//***********************************************************************

As chamadas s funes RaiseOne e RaiseTwo da fonte faz com que esta dispare os dois eventos. A Figura 14.4 mostra a sada do programa Event.

Figura 14.4: Sada do programa Event. O receptor teve ambos os manipuladores chamados. Assim, ele imprimiu as duas mensagens associadas com os eventos. PASSO 2: Insira o cdigo abaixo para chamar a funo RemoveHandler do receptor, e tente disparar os dois eventos novamente:
//Remove o manipulador para o evento um

242

pRcv->RemoveHandler(); //Eventos disparados novamente Console::WriteLine(S"\nTentando disparar os dois eventos novamente"); pSrc->RaiseOne(S"Este o primeiro evento"); pSrc->RaiseTwo(S"Este o segundo");

Desta vez, como vemos na Figura 14.5, apenas a segunda mensagem impressa porque o receptor j no manipula o primeiro evento.

Figura 14.5: Sada atual do programa Event.

243

14.3.

RPIDAS REFERNCIAS SOBRE O CAPTULO 14

A Tabela 14.1 mostra um resumo deste captulo. Tabela 14.1: Resumo do captulo 14.
PARA Definir um delegado Criar um delegado ligado a um membro de classe static Criar um delegado ligado a um membro de classe no static Executar a funo ligada a um delegado Criar um evento FAA ISTO Use a palavra-chave __delegate com um prottipo de funo Ex.: __delegate void DelegateOne(double d); Use new para criar um objeto Delegate, passando 0 para o primeiro parmetro e o endereo da funo static como o segundo parmetro. Ex.: DelegateOne *pDel = new DelegateOne(0, &MyClass::MyFunc); Use new para criar um objeto Delegate, passando um ponteiro para a instncia no primeiro prottipo e o endereo da funo-membro como o segundo parmetro. Ex.: Delegate *pDel = new DelegateOne(pMyObject, &MyClass::MyOtherFunc); Use a funo Invoke do delegado, passando quaisquer parmetros requeridos. Ex.: pDel->Invoke(22.7); Primeiro defina um delegado para definir a rotina do manipulador para este evento, fazendo, por exemplo: __delegate void ClickHandler(int, int); Depois, na classe da fonte de eventos, use a palavra-chave __event para definir um objeto de evento, como: __event ClickHandler *OnClick; Use o objeto de evento como se ele fosse uma funo, passando-lhe quaisquer parmetros. Ex.: OnClick(xVal, yVal); Use o operador +=. Ex.: mySrc->OnClick += new ClickHandler(this, &myHandler); Use o operador -=. Ex.: mySrc->OnClick -= new ClickHandler(this, &myHandler);

Produzir um evento

Subscrever para um evento Fazer remoo de um evento

244

245

Jos Wilson Vieira PARTE 4: USANDO O .NET Framework 15. CAPTULO 15: A BIBLIOTECA DE CLASSES .NET Framework

Neste captulo, voc aprender De que consiste o Microsoft .NET Framework; Quais os principais componentes do .NET Framework; Sobre os principais namespace que caracterizam a biblioteca de classes do .NET Framework.

Nos captulos anteriores, voc aprendeu como usar o C++ gerenciado para construir aplicativos simples. Agora chegou o momento de aprender como construir aplicativos Microsoft .NET reais que envolvem GUIs, bancos de dados, servidores Web e todos os outros mecanismos necessrios a um aplicativo moderno do Microsoft Windows. E a que entra o .NET Framework. O .NET Framework a nova biblioteca de classes que voc usa para construir aplicativos Windows. Ela grande, bastante complexa e de longo alcance em seu escopo. Este captulo lhe dar uma viso geral do que esta biblioteca e o que ela pode fazer antes de abordarmos com mais detalhes suas caractersticas nos captulos posteriores.

15.1.

O QUE O .NET Framework?

O .NET Framework uma plataforma de computao que foi projetada pela Microsoft para simplificar o desenvolvimento de aplicativos modernos, tais como: Aplicativos que usam sofisticadas GUIs de entrada e sada; Aplicativos que usam a Internet; Aplicativos que so distribudos por mais de um computador; Aplicativos que utilizam sofisticados bancos de dados.

H duas componentes principais para o .NET Framework: a CLR (Common Language Runtime, linguagem comum em tempo de execuo) e a biblioteca de classes .NET Framework. Examinaremos as duas neste captulo.

15.1.1.

A LINGUAGEM COMUM EM TEMPO DE EXECUO

Voc j ouviu falar da linguagem comum em tempo de execuo, porque ela a parte do .NET que fornece o gerenciamento nas extenses gerenciadas para C++. A CLR um mecanismo que funciona em tempo de execuo e responsvel pelo gerenciamento da execuo do cdigo dentro do ambiente .NET, fornecendo servios como segurana, gerenciamento de memria e comunicao remota (comunicao entre objetos em diferentes domnios, processos ou computadores). O cdigo que executado pela CLR conhecido como cdigo gerenciado; se o cdigo executado fora do controle da CLR, trata-se de um cdigo no gerenciado. Todos os cdigos Microsoft Visual Basic e C# so gerenciados, mas, no Microsoft Visual C++, tanto possvel escrever cdigo gerenciado quanto no gerenciado; e ter ambos os tipos de cdigo funcionando juntos no mesmo programa.

246

Microsoft Visual C++ .NET Passo-a-Passo 15.1.2. A LINGUAGEM INTERMEDIRIA Todas as linguagens .NET compilam sob uma forma intermediria chamada linguagem intermediria, IL. s vezes tambm conhecida como MSIL (Microsoft Intermediate Language). A IL similar ao Java bytecode, no aspecto em que ela uma forma intermediria de cdigo produzida pelo compilador que no pode ser diretamente executada em um sistema-alvo. O cdigo IL tambm porttil. Porm, em contraste com Java, o cdigo IL sempre convertido no cdigo nativo antes de ser executado, o que feito por um compilador JIT (Just-In-Time). Esta converso pode acontecer em demanda, funo-por-funo como na execuo de um programa, ou tudo de uma vez como na instalao de um programa. Uma das grandes inovaes da IL que ela simplesmente no um cdigo de objeto de baixo nvel, independente da mquina. Na verdade, suportes para funcionalidades orientadas para objetos como as idias de classes, encapsulamento e ocultao de dados, polimorfismo e herana so construdos em IL. Assim, voc pode v-la como um tipo de linguagem assembler orientada para objetos. Esta funcionalidade a torna muito mais potente do que Java bytecode e ela lhe permite realizar programao orientada para objetos com linguagens cruzadas, facilmente chamando membros de classes gerenciadas C++ no Visual Basic e vice-versar, e at herana de uma classe gerenciada C++ no Visual Basic. NOTA: Se estiver interessado em saber a aparncia da IL, voc pode usar o utilitrio IL no assembler, ILDASM, para abrir um executvel .NET e ver seu cdigo em IL. H um exemplo de como fazer isto na seo METADATA ainda neste captulo.

15.1.3.

O SISTEMA DE TIPO COMUM

O CTS (Common Type System) fornece uma especificao sobre como os tipos so definidos, gerenciados e usados, o que constitui uma importante parte da integrao de linguagens cruzadas .NET. O CTS fornece um conjunto de regras que as linguagens devem obedecer e que ajuda a garantir que os tipos criados em diferentes linguagens possam inter-operar uma com as outras.

15.1.4.

A ESPECIFICAO DA LINGUAGEM COMUM

A CLS (Common Language Specification) um conjunto de regras e limitaes que os escritores de compilador e biblioteca precisam seguir para garantir que as linguagens e o cdigo que eles produzem inter-operaro com outras linguagens .NET. A CLS forma um subconjunto do CTS e se uma linguagem ou uma biblioteca submissa CLS, ela ser completamente inter-operante com outras linguagens submissas CLS. Voc ver na documentao online que algumas funes-membros .NET no so marcadas como submissas CLS, o que significa que elas no podem ser acessveis a algumas linguagens .NET. Por exemplo, as funes que usam inteiros sem sinal no so submissas CLS porque inteiros sem sinal no so suportados pelo Visual Basic. Conseqentemente, os inteiros sem sinal no so includos nos tipos especificados pela CLS.

15.1.5.

A BIBLIOTECA DE CLASSES .NET Framework

A .NET Framework uma biblioteca de classes orientadas para objetos que fornecem todos os utilitrios que voc precisa para escrever uma grande variedade de programas.

247

Jos Wilson Vieira Durante anos, desde que o Windows foi lanado, os programadores tm escrito aplicativos usando a API (Application Programming Interface) Windows. Esta API lhe dispe um grande nmero de funes C que voc pode chamar nos seus programas para interagir com o Windows. Porm, h dois problemas principais com a API Windows: primeiro, ela no orientada para objetos; segundo, ela uma biblioteca C, i.e., no pode ser facilmente usada por outra linguagem. Um dos benefcios da programao orientada para objetos a ajuda que ela d na estruturao e gerenciamento de projetos de larga escala. A API Windows acumulou milhares de funes e tem se tornado cada vez mais difcil e custoso gerenciar uma coleo to grande de rotinas no estruturadas. Alm dos outros benefcios, como encapsulamento e polimorfismo, a programao orientada para objetos lhe permite impor uma estrutura ao cdigo. Assim, por exemplo, uma classe Dialog pode conter todas as funes relacionadas com caixas de dilogo. Esta habilidade a torna mais fcil usar uma biblioteca do que toda a API Windows. O segundo problema com a API Windows que ela basicamente escrita para programadores em C. Assim, ela usa muitas caractersticas que so prprias do C, tais como ponteiros e strings terminadas em zero, o que se torna difcil e s vezes impossvel usar alguma funcionalidade de outras linguagens alm do C ou C++. Voc tambm tende a usar emendas muito feias para conseguir conexes entre, por exemplo, o Visual Basic e a API Windows. A biblioteca de classes .NET Framework fornece um conjunto de classes que pode ser usado de qualquer linguagem .NET porque ele funciona no nvel da IL. Todas as linguagens .NET compilam sob o mesmo cdigo intermedirio e, como todas elas usam referncias e concordam no conjunto bsico de tipos de valor, todas podem usar as classes definidas na biblioteca. Este um avano enorme e fornece inter-operacionalidade de linguagem em uma escala nunca vista antes. No passado, os vendedores de compilador que suportavam mais de uma linguagem como TopSpeed e Salford tinham adicionado caractersticas para seus compiladores fazerem simples mistura de linguagens em aplicativo de multi-linguagem, mas ningum produziu um framework de programao independente da linguagem antes do .NET Framework. NOTA: O primeiro lanamento do .NET Framework foi feito na plataforma Windows, mas foi projetado de modo que ele pudesse se transportado para outras plataformas no futuro.

15.1.6.

ASSEMBLIES

Assemblies so os blocos bsicos de construo com os quais os aplicativos .NET so construdos e constituem a unidade fundamental de desenvolvimento e de verses. Os assemblies contm o cdigo IL, metadata que descreve o assembly e seu contedo, e todos os outros arquivos necessrios para a operao em tempo de execuo. Portanto, um assembly muito mais auto-suficiente do que um objeto executvel Windows padro ou um objeto COM porque no h dependncia das fontes externas de informao tais como o registro do Windows. Todo tipo .NET parte de um assembly e no existe tipo .NET fora de um assembly. H diversas situaes nas quais os assemblies so fundamentais no mundo .NET: VERSES: O assembly a menor unidade para a qual aplicada a verso e o manifesto do assembly descreve a sua verso junto com as verses de quaisquer assemblies dos quais ele dependa. Esta informao significa que possvel checar quais componentes com a informao de verso errada esto sendo usados em tempo de execuo. DESENVOLVIMENTO: Os assemblies s so carregados quando necessrio, o que os torna altamente satisfatrios para aplicativos distribudos. TIPO: Uma identidade de tipo inclui o assembly no qual ele vive. Dois tipos com o mesmo nome vivendo em diferentes assemblies so considerados como dois tipos completamente diferentes.

248

Microsoft Visual C++ .NET Passo-a-Passo SEGURANA: A fronteira entre assemblies onde as permisses da segurana so testadas.

15.1.7.

METADATA

As classes .NET so autodescritveis pois carregam informaes descritivas com elas no EXE ou DLL. Estas informaes, chamadas metadata, incluem: O nome, a verso e informaes de cultura especfica (como, por exemplo, a linguagem e o calendrio usados) para o assembly; Os tipos que so exportados pelo assembly; Outros assemblies que dependam assembly focado; Permisses de segurana necessrias para execuo; Informaes para cada tipo de assembly: nome, visibilidade, classe-base, interfaces implementadas e detalhes dos membros; Informaes de atributos adicionais.

A maioria dos metadata padronizada e criada pelo compilador quando ele produz o cdigo IL, mas voc pode usar atributos para adicionar informaes extras de metadata.

15.1.7.1.

EXERCCIO: MODIFICANDO UM METADATA PADRO

O exerccio abaixo mostra como modificar o metadata padro produzido pelo compilador. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado Meta1. A Figura 15.1 mostra o Solution Explorer. Veja que o projeto contm trs arquivosfontes na pasta Source Files. O Meta1.cpp o cdigo para o aplicativo, AssemblyInfo.cpp contm definies dos itens metadata padres que voc pode modificar e StdAfx.cpp est ali para incluir o arquivo de cabealho StdAfx.h.

Figura 15.1: Projeto Meta1, visto do Solution Explorer. PASSO 2: No AssemblyInfo.cpp, voc ver que o arquivo contm algumas linhas como estas: 249

Jos Wilson Vieira


[assembly:AssemblyTitleAttribute("")]; [assembly:AssemblyDescriptionAttribute("")]; [assembly:AssemblyConfigurationAttribute("")]; [assembly:AssemblyCompanyAttribute("")]; [assembly:AssemblyProductAttribute("")]; [assembly:AssemblyCopyrightAttribute("")]; [assembly:AssemblyTrademarkAttribute("")]; [assembly:AssemblyCultureAttribute("")];

O metadata adicionado ao cdigo C++ colocando sua declarao entre colchetes. A parte assembly: do incio da declarao significa que este um atributo que se aplica um assembly, em oposio a um tipo dentro de um assembly. H um conjunto de atributos padres que voc pode usar para mudar o metadata compilado em um assembly e a maioria deles est listada em AssemblyInfo.cpp. PASSO 3: Edite a linha AssemblyCompanyAttribute para conter um nome apropriado como:
[assembly:AssemblyCompanyAttribute("Acme Rocket Sled, Inc.")];

PASSO 4: Agora, construa o projeto, que automaticamente cria o assembly para voc. Como voc pode saber se o metadata no assembly refletiu sua mudana? Um modo de descobrir isto usar o ILDASM, que parte do .NET SDK (Software Development Kit), e est localizado na pasta C:\Arquivos de programas\ Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin. Voc pode inici-lo dando um clique duplo no ILDASM.exe ou abrindo um comando Prompt Microsoft Visual Studio .NET 2003, usando a seqncia Programas Microsoft Visual Studio .NET Visual Studio .NET Tools Visual Studio .NET 2003 Command Prompt. Uma janela console aparecer j no caminho para incluir todas pastas Visual Studio .NET e .NET SDK. Digite ildasm para entrar no programa. PASSO 5: Quando abrir a janela do ILDASM, use o menu File para acessar o Meta1.exe e abrilo. Voc ver algo como a Figura 15.2.

Figura 15.2: Janela do programa ILDASM, mostrando o Meta1.exe. PASSO 6: D um clique duplo em MANIFEST para abrir a janela da Figura 15.3 com o metadata do assembly. Localize a linha do AssemblyCompanyAttribute, que deve conter algo como: 250

Microsoft Visual C++ .NET Passo-a-Passo


.custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 16 41 63 6D 65 20 52 6F 63 6B 65 74 20 53 // ...Acme Rocket S 6C 65 64 2C 20 49 6E 63 2E 00 00 ) // led, Inc...

Embora os contedos sejam dados em hexadecimal, voc pode ver que o metadata reflete a mudana que foi feita no projeto.

Figura 15.3: Janela MANIFEST do programa ILDASM.

15.2.

OS namespaces DA BIBLIOTECA .NET Framework

A biblioteca de classes .NET Framework composta por um conjunto de classes, interfaces, estruturas e enumeraes que esto contidas em quase 100 namespaces. Esta seo comea explicando como usar namespaces em cdigo C++ gerenciado e, ento, lista os principais namespaces .NET, juntos com breves detalhes sobre seu funcionamento e contedo. Voc j encontrou namespaces .NET em uso em cdigo C++ gerenciado quando usou a palavrachave using, como no exemplo:
using namespace System::Collections;

Como nos namespaces do C++ tradicional, os namespaces .NET fornecem um nvel adicional de escopo que lhe ajuda a organizar cdigos e se prevenir contra superposio de nomes. Duas classes com o mesmo nome podem ser usadas em um programa, desde que informemos que elas pertencem a namespaces diferentes. Um nome de tipo que inclua a informao do namespace chamado de nome completamente qualificado, como nos exemplos abaixo:
System::Collections::ArrayList System::Threading::Thread //A classe ArrayList de System::Collections //A classe Thread de System::Threading

Os nomes dos namespaces em .NET tipicamente consistem de mais de uma palavra. Em C++ gerenciado, os componentes do nome so separados pelo operador de resoluo de escopo (::). Para linguagens .NET como C# e Visual Basic, os componentes so separados usando um ponto (.). Assim, no Visual Basic, o exemplo acima seria:
System.Collections.ArrayList System.Threading.Thread

251

Jos Wilson Vieira Todas as classes, interfaces, estruturas e enumeraes que compem a biblioteca de classes .NET Framework pertencem a um namespace. Todos os namespaces fornecidos pela Microsoft comeam com um dos dois prefixos: System ou Microsoft. Os que comeam com System foram desenvolvidos como parte da biblioteca de classes .NET Framework, enquanto os que comeam com Microsoft foram desenvolvidos por outros grupos de produtos dentro da Microsoft. Os nomes de namespace podem ter qualquer nmero de componentes, mas no h nenhuma relao hierrquica decorrente de nomes que tenham as mesmas componentes de raiz. A natureza hierrquica dos nomes de namespace simplesmente lhe fornece um modo para organizar suas classes. Assim, por exemplo, tanto System::Collections::Specialized quanto System:: Collections contm colees, contudo elas no esto, necessariamente, correlacionadas. NOTA: Nota para programadores de Java: Embora os namespaces .NET se paream muito com os nomes dos pacotes de Java, no h relao alguma entre nomes de namespace e os caminhos de pastas usados em Java. No h nenhuma exigncia de que todas as classes que pertenam a um namespace estejam definidas na mesma DLL ou que uma nica DLL contenha classes de um nico namespace.

15.2.1.

USANDO namespaces EM PROGRAMAS C++

Os programas em C++ gerenciado usam a diretiva de pr-processador #using para importar metadata em um programa usando as extenses gerenciadas para C++. Relembre que metadata so informaes que descrevem os tipos em um assembly e incluem os nomes completamente qualificados de todos os tipos. Por exemplo, se o compilador v uma linha como esta
#using <mscorlib.dll>

ele carrega a DLL e l o metadata para todos os tipos que so ali definidos. Como mscorlib.dll contm a essncia das classes .NET Framework, quando l a declarao acima, o compilador importa o metadata para um nmero muito grande de tipos. NOTA: Atualmente, voc s usa #using para referenciar assemblies definidos em DLLs. Esta restrio pode ser suspensa posteriormente, mas, por enquanto, voc pode referenciar apenas em componentes em processo. A palavra-chave #using significa que voc tem que saber qual DLL suporta a classe ou classes que voc deseja usar. Sua fonte normal de informaes ser a ajuda on-line e sempre que mencionarmos uma namespace no restante deste captulo, tambm diremos a que DLL ele pertence. Alguns dos nomes completamente qualificados podem se tornar muito longos. Assim, comum usar uma diretiva tridimensional using para especificar os nomes do namespace de modo que voc pode usar nomes no qualificados, como abaixo:
//L o metadata para MSCORLIB #using <mscorlib.dll> //Importa todos os nomes using namespace System::Collections; //Agora, voc pode usar ArrayList ao invs de System::Collections::ArrayList ArrayList *pal = new ArrayList();

15.2.2.

O namespace System

O namespace System, definido em mscorlib.dll, contm diversas classes fundamentais, incluindo: 252

Microsoft Visual C++ .NET Passo-a-Passo Classes-bases para tipos de valor e de referncia comumente usados e tambm a classe-base para matrizes; Eventos e manipuladores de evento; Delegados e interfaces; Atributos; Excees; Math; Gerenciamento de ambiente de aplicativo; Coletor de lixo; Invocao de programa local e remota; Converso de tipos de dados.

Voc j encontrou alguns dos tipos de System nos captulos anteriores e algumas das outras classes ainda esto obscuras. Assim, no entraremos em detalhes sobre elas. Contudo, h alguns pontos que valem a pena mencionarmos sobre algumas das classes em System, o que faremos nesta seo.

15.2.2.1.

TIPOS BSICOS

System implementa todos os tipos bsicos definidos pelo sistema de tipos comuns (CTS). A Tabela 15.1 (j mostrada no captulo 9), lista estes tipos. Tabela 15.1: Tipos bsicos implementados no namespace System.
TIPO DE VALOR Byte SByte Int16 Int32 Int64 UInt16 UInt32 UInt64 Single Double Boolean Char Decimal IntPtr UIntPtr DESCRIO Inteiro sem sinal de 8 bits Inteiro com sinal de 8 bits Inteiro com sinal de 16 bits Inteiro com sinal de 32 bits Inteiro com sinal de 64 bits Inteiro sem sinal de 16 bits Inteiro sem sinal de 32 bits Inteiro sem sinal de 64 bits Nmero ponto-flutuante de preciso simples de 32 bits Nmero ponto-flutuante de dupla preciso de 64 bits Valor booleano Caractere Unicode de 16 bits valor decimal de 96 bits Inteiro com sinal cujo tamanho depende da plataforma Inteiro sem sinal cujo tamanho depende da plataforma TIPO EQUIVALENTE EM C++ GERENCIADO char signed char short int ou long __int64 unsigned short unsigned int ou unsigned long unsigned __int64 float double bool wchar_t Decimal Nenhum tipo embutido Nenhum tipo embutido

Note que muitos dos tipos os inteiros sem sinal e SByte no so submissos ao CLS. Assim, seja cauteloso ao us-los quando estiver escrevendo cdigo que ir ser usado por outras linguagens .NET. Todas as linguagens .NET mapeiam estes tipos como tipos nativos. Assim, o C++ gerenciado mapeia int em System::Int32, mas voc tambm pode usar os tipos diretamente se quiser. 253

Jos Wilson Vieira

15.2.2.2.

TIPOS PONTOS-FLUTUANTES

Os tipos Single e Double implementam a aritmtica de ponto-flutuante IEEE-754. Para quem no sabe, a aritmtica de pontos-flutuantes IEEE-754 significa que toda operao tem um resultado definido. Assim, voc nunca obtm um erro de diviso por zero quando utiliza pontos-flutuantes. Ao invs disto, voc obtm uma resposta divergente (infinito). As classes de ponto-flutuante tm valores, e no nmeros, para representar o infinito positivo e negativo, bem como mtodos para test-los, como no exemplo:
double top = 1.0; double bottom = 0.0; double result = top / bottom; if(result == Double::PositiveInfinity) Console::WriteLine(S"+ infinito"); else if(result == Double::NegativeInfinity) Console::WriteLine(S"- infinito"); else if(result == Double::NaN) Console::WriteLine(S"NaN");

15.2.3.

OS namespaces DE COLEES

Voc j encontrou os namespaces de colees, System::Collections e System::Collections::Specialized, no captulo 12. System::Collections implementado em mscorlib.dll, enquanto System::Collections::Specialized implementado em system.dll. Assim, se quiser usar ambos, voc ter que declarar no seu cdigo:
#using <mscorlib.dll> #using <system.dll>

A Tabela 15.2 mostra as System::Collections.

principais

classes

que

voc

encontrar

no

namespace

Tabela 15.2: Principais classes implementadas no namespace System::Collections.


CLASSE ArrayList BitArray DESCRIO Uma matriz de crescimento dinmico. Uma classe que armazena alguns valores booleanos como os bits individuais de um inteiro. Hashtable Uma tabela picotada que armazena objetos por rtulo de pedaos. Queue Uma lista onde os objetos so adicionados em uma extremidade e retirados em outra. SortedList Uma lista vinculada cujos membros so mantidos ordenadamente. Stack Uma pilha de entrada em cima e sada em baixo.

A Tabela 15.3 mostra as principais classes System::Collections::Specialized.

que

voc

encontrar

no

namespace

254

Microsoft Visual C++ .NET Passo-a-Passo Tabela 15.3: Principais classes do namespace System::Collections::Specialized.
CLASSE BitVector32 DESCRIO Uma classe que armazena alguns valores booleanos como os bits individuais de um inteiro de 32 bits. ListDictionary Um dicionrio implementado que usa uma nica lista vinculada. NameValueCollections Uma Coleo ordenada de rtulos e valores de string. StringCollection Uma coleo no ordenada de strings. StringDictionary Um tabela picotada com rtulo fortemente tipificado para ser uma string ao invs de um objeto. StringEnumerator Um enumerador para funcionar com StringCollection.

15.2.4.

AS INTERFACES DAS COLEES

O namespace System::Collections tambm define uma srie de interfaces que so usadas para definir o comportamento das classes de coleo. As prprias classes de coleo implementam uma ou mais destas interfaces e voc pode us-las como a base para escrever suas classes de coleo personalizadas. A Tabela 15.4 mostra as principais interfaces de System::Collections. Tabela 15.4: Principais interfaces do namespace System::Collections.
INTERFACE ICollection DESCRIO Define o tamanho, o enumerador e mtodos de sincronizao para todas as colees. IComparer Define um mtodo para comparar dois objetos. IDictionary Implementada pelas colees que gerenciam pares rtulo/valor, como, por exemplo, Hashtable e ListDictionary. IDictionaryEnumerator Define mtodos para enumerao em itens de um dicionrio. IEnumerable Define o mtodo GetEnumerator, que retorna um IEnumerator; implementada por quase todas as colees. IEnumerator Define as propriedades e os mtodos dos enumeradores. IHashCodeProvider Implementada pelas classes que fornecem valores de cdigo picotado. IList Implementada pelas classes que definem colees indexadas de objetos.

O namespace Diagnostics System::Diagnostics fornece algumas classes para Rastrear a execuo do programa; Interagir com o depurador; Usar o log de eventos do sistema; Iniciar processos do sistema; Monitorar performance do sistema.

Todas as classes em System::Diagnostics so implementadas em system.dll.

15.2.5.

O namespace IO

O namespace System::IO, definido em mscorlib.dll, fornece as classes que implementam a funcionalidade .NET de entrada/sada (I/O). A Tabela 15.5 mostra as principais classes neste namespace. 255

Jos Wilson Vieira Tabela 15.5: Principais classes do namespace System::IO.


CLASSE BinaryReader BinaryWriter Directory DirectoryInfo File FileInfo FileStream FileSystemInfo FileSystemWatcher IOException MemoryStream Path Stream StreamReader StreamWriter StringReader StringWriter TextReader TextWriter DESCRIO L tipos primitivos .NET de um fluxo de bytes. Escreve tipos primitivos .NET para um fluxo de bytes. Contm mtodos static para operar em diretrios (pastas). Representa um caminho para um diretrio e contm mtodos para operar no caminho do diretrio. Contm mtodo static para operar em arquivos. Representa um caminho para um arquivo e contm mtodos para operar no caminho do arquivo. L e escreve para arquivos usando fluxos. A classe-base para FileInfo e DirectoryInfo. Monitora o tempo para mudanas no sistema de arquivos e dispara eventos quando ocorrem mudanas. Lana excees quando ocorrem erros de I/O. L e escreve fluxos de bytes para e da memria. Representa strings de diretrios de um modo independente da plataforma. A base abstrata para as classes de fluxo. L caracteres Unicode de um fluxo de bytes. Escreve caracteres Unicode para um fluxo de bytes. L caracteres Unicode de uma string. Escreve caracteres Unicode para uma string. A classe-base para StreamReader e StringReader. A classe-base para StreamWriter e StringWriter.

Como ocorre com todas as classes da biblioteca de classes .NET Framework, as classes da Tabela 15.5 so independentes da linguagem. Elas podem ser usadas ao lado ou no lugar das classes de fluxo C++. Voc ver mais sobre algumas das classes de System::IO no captulo 19.

15.2.6.

OS namespaces PARA DESENHO

Eis alguns dos namespaces que fornecem toda a funcionalidade grfica para o .NET Framework: System::Drawing: Encapsula a funcionalidade bsica para desenho GDI+. Este namespace fornece grficos bidimensionais simples orientados para pixel. System::Drawing::Design: Estende System::Drawing para adicionar funcionalidade em tempo de design de modo que voc pode modificar a interface do Visual Studio .NET com itens personalizados. System::Drawing::Drawing2D: Fornece mais avanos bidimensionais e grficos vetoriais. System::Drawing::Imaging: Adiciona funcionalidade para processamento de imagens GDI+. System::Drawing::Printing: Permite que voc personalize e controle o processo de impresso. System::Drawing::Text: Adiciona suporte avanado de tipografia GDI+, incluindo a habilidade para criar e usar colees de fontes.

O conjunto original das rotinas grficas do Windows era chamado GDI (Graphical Device Interface). A GDI fornece um conjunto muito simples de primitivas grficas bidimensionais para desenhar linhas, crculos, retngulos e outras formas simples, bem como strings de texto. O .NET Framework 256

Microsoft Visual C++ .NET Passo-a-Passo tem embutida esta funcionalidade e sua biblioteca grfica chamada GDI+. Voc aprender mais sobre GDI+ no captulo 18.

15.2.7.

O namespace Forms

Os programadores do Visual Basic tm, algumas vezes, programado com formas. Os aplicativos de GUI no Visual Basic consistem de algumas destas formas, cada uma das quais sendo uma janela separada da janela do nvel superior. Os desenvolvedores selecionam controles tais como botes e caixas de lista da barra de utilitrios e os colocam na forma. Todo controle tem propriedades e mtodos associados com ele. O editor de propriedades do Visual Basic permite modificaes interativas das propriedades em tempo de design e os mtodos e as propriedades podem ser acessadas do cdigo em tempo de execuo. O namespace System::Windows::Forms, que voc ver com mais detalhes nos captulos 16 e 17, fornece desenvolvimento para todas as linguagens .NET baseado no estilo de forma do Visual Basic. Este namespace enorme, contendo cerca de 300 classes, estruturas e enumeraes. A Tabela 15.6 lista algumas das suas classes mais importantes lhe dar uma idia do que est disponvel neste namespace.

Tabela 15.6: Principais classes do namespace System::Windows::Forms.


CLASSE Application AxHost Button CheckBox CheckedListBox Clipboard ColorDialog ComboBox Control Cursor DataGrid DateTimePicker DomainUpDown FileDialog Form Label ListBox ListView Panel RichTextBox StatusBar TextBox ToolBar DESCRIO Fornece mtodos e propriedades para gerenciamento de aplicativos. Encapsula controles Microsoft ActiveX de modo que eles podem ser usados como controles de formas do Windows. Representa um controle de boto do Windows. Representa um controle de caixa de checagem do Windows. Representa um controle de caixa de lista que tem uma caixa de checagem esquerda de cada item. D acesso rea de transferncia do sistema. Mostra uma caixa de dilogo com amostras das cores padres. Representa um controle de caixa combo do Windows. A classe-base para todos os controles. Representa um cursor. Mostra dados Microsoft ADO.NET em uma grade com rolagem. Representa um controle para datas do Windows. Representa um controle up-down do Windows que mostra valores de string. A classe-base para mostrar as caixas de dilogo padres File Open e File Save. Representa uma janela ou uma caixa de dilogo que constitui parte de uma interface de usurio do aplicativo. Representa um controle de rtulos do Windows. Representa um controle de caixa de lista do Windows. Mostra uma lista de itens em uma das quatro vistas. Representa um controle de painel do Windows. Representa um controle de caixa de texto rich do Windows. Representa um controle de barra de status do Windows. Representa um controle de caixa de texto do Windows. Representa um controle de barra de utilitrios do Windows.

257

Jos Wilson Vieira 15.2.8. OS namespaces Net O suporte para trabalho com redes fornecido pelas classes System::Net e System::Net::Sockets. A System::Net fornece uma interface para muitos dos protocolos comumente usados hoje em dia, como manipuladores de endereos IP, construtores de lookups DNS, narradores para servidores HTTP e FTP, gerenciadores de cookies, e autenticaes. A System::Net::Sockets fornece uma implementao do protocolo Berkeley Sockets e fornece uma envoltria .NET para o Windows WinSock API.

15.2.9.

OS namespaces Xml

O formato XML bastante utilizado em todo o .NET Framework e diversos namespaces fornecem suporte para criar e manipular XML: System::Xml: Fornece as classes bsicas necessrias para processar XML; System::Xml::Schema: Fornece suporte para esquemas XML; System::Xml::Serialization: Permite-lhe colocar em srie objetos .NET para e de XML; System::Xml::XPath: Contm o parser XPath e a mquina de avaliao; System::Xml::Xsl: Contm o processador Extensible Stylesheet Language (XSL).

Usando estas classes, possvel realizar toda a manipulao de XML que voc freqentemente precisar fazer. Estas classes tornam o .NET Framework um dos ambientes mais produtivos para programao XML.

15.2.10.

OS namespaces Data

Os namespaces System::Data suportam as classes que implementam o ADO.NET, uma nova verso da tecnologia Microsoft Active Data Objects, otimizada para trabalhar com o .NET Framework, o que lhe permite construir componentes que gerenciam dados de diversas fontes. Dados de diversas fontes so fornecidos pelos provedores de dados, dos quais h trs incorporados ao .NET Framework. O .NET Framework Data Provider para OLE DB usa tecnologia baseada no Microsoft COM que torna possvel o uso de muitos tipos diferentes de fontes de dados tais como tabelas de bancos de dados relacionadas, planilhas eletrnicas do Microsoft Excel e at mesmo arquivos de texto como se elas fossem bancos de dados. O .NET Framework Data Provider para servidor SQL trabalha com dados do Microsoft SQL Server. Finalmente, o .NET Framework Data Provider para Oracle torna possvel trabalhar com bancos de dados Oracle de cdigo .NET. A classe mais importante nos namespaces System::Data DataSet, que representa um cache de memria de dados recuperados de uma fonte de dados. Um DataSet consiste de um ou mais objetos DataTable e, por seu turno, estes consistes em uma coleo de objetos DataColumn.

15.2.11.

OS namespaces Web

Como uma das principais razes para a introduo do .NET Framework foi tornar mais fcil a construo de aplicativos Web, talvez no seja surpresa que o .NET Framework contenha alguns namespaces relacionados programao Web. Todos eles esto relacionados com o Microsoft ASP.NET, a ltima verso da tecnologia Microsoft Active Server Pages que foi otimizado para funcionar no ambiente .NET Framework. 258

Microsoft Visual C++ .NET Passo-a-Passo Os namespaces Web mais significantes so: System::Web: Fornece a funcionalidade bsica para comunicao de browser para servidor em HTTP, incluindo as classes HttpRequest e HttpRespouse que permitem que uma pgina ASP.NET troque dados com o cliente usando HTTP; System::Web::Mail: Permite que voc prepare e envie anexos de e-mail usando o servio SMTP construdo no Windows 2000, Windows XP e Windows NT; System::Web::Security: Fornece classes que implementam segurana em ASP.NET; System::Web::Services: Fornece as classes que lhe permitem construir servios Web; System::Web::UI: Contm classes que lhe permitem construir controles de apoio ao servidor.

As caractersticas fornecidas por dois destes namespaces merecem meno particular. Os servios Web, em particular, so uma das maiores caractersticas novas introduzidas pelo .NET Framework. Um servio Web uma entidade programvel que se mantm em um servidor Web e pode ser acessado usando os protocolos padres da Internet. O que isto significa na prtica que voc pode expor uma funo em um servidor Web que outros podem chamar. A comunicao entre cliente e servidor usa protocolos padres, tais como HTTP, e os dados so usualmente passados para e do servio Web em formato XML usando SOAP. O uso de XML em HTTP torna possvel acessar servios Web facilmente de clientes escritos em praticamente qualquer linguagem de programao e qualquer plataforma. Tambm possvel descobrir quais servios um servidor Web suporta e muito fcil no Visual Studio .NET escrever para clientes que usam os servios Web. Os namespaces System::Web::UI lhe permitem construir controles de apoio ao servidor. Voc programa estes como se eles fossem controles normais, mas seu cdigo executa no servidor. O namespace System::Web::UI::HtmlControls contm classes que representam controles de servidor HTML que mapeiam diretamente nos elementos do HTML padro, tais como botes e formas. O System::Web::UI::WebControls mais abstrato e lhe permite programar controles de apoio ao servidor que podem no mapear diretamente em HTML.

15.3.

RPIDAS REFERNCIAS SOBRE O CAPTULO 15

A Tabela 15.7 mostra um resumo deste captulo. Tabela 15.7: Resumo do captulo 15.
PARA Usar estruturas de dados como matrizes dinmicas, listas e tabelas picotadas Criar um aplicativo baseado em formas FAA ISTO Use as classes nos namespaces e no System::Windows::Collections System::Windows::Collections::Specialized. Use as classes em System::Windows::Forms e derive uma classe de System::Windows::Forms::Form. Observe as classes no namespace System::Xml. Us as classes no namespace System::Diagnostics. Observe os namespaces System::Data.

Trabalhar com XML Rastrear a execuo de um programa, interagir com o log do evento ou monitorar a performance do sistema Trabalhar com bancos de dados usando ADO.NET

259

Jos Wilson Vieira 16. CAPTULO 16: INTRODUZINDO AS FORMAS DO WINDOWS

Neste captulo, voc aprender O que o Microsoft Windows Forms; O que contm o namespace System::Windows::Forms; Como criar e usar formas em aplicativos; Como manipular eventos; Como usar os controles bsicos; Como usar menus; Como executar e depurar aplicativos Windows Forms.

O Windows Forms uma potente caracterstica do Microsoft Windows .NET Framework que fornece um conjunto de classes para construo de aplicativos GUI. Em contraste com a maioria das outras bibliotecas GUI, as formas do Windows podem ser usadas de qualquer linguagem .NET e, agora, voc pode construir aplicativos grficos misturando linguagens. As formas do Windows constituem um assunto grande e complexo, envolvendo tudo sobre escrita de aplicativos GUI. O assunto por si j comporta um livro. Assim, os dois captulos a seguir podem apenas arranhar a superfcie para lhe dar um sabor de como operam as formas do Windows e como voc pode us-la para escrever aplicativos GUI.

16.1.

APLICATIVOS WINDOWS FORMS

Se alguma vez voc j programou no Microsoft Visual Basic, a idia por trs das formas do Windows lhe parecer familiar. Na verdade a Microsoft tomou o modelo completo de programao GUI do Visual Basic, com formas, controles e propriedades, e o generalizou de modo que pudesse ser usado de qualquer linguagem .NET. Um aplicativo que use as formas do Windows consiste de uma ou mais janelas chamadas formas. Podem ser janelas-me, janelas-filha ou caixas de dilogo, e um aplicativo pode suportar muitas formas diferentes. Voc coloca controles como botes e caixas de lista em uma forma para construir a GUI para seu programa. Uma forma simples mostrada na Figura 16.1.

Figura 16.1: Uma forma simples. 260

Microsoft Visual C++ .NET Passo-a-Passo Esta forma uma janela-me que pode ser redimensionada e tem uma barra de ttulo. Ela tambm possui os botes para maximiz-la, minimiz-la e fech-la no canto superior direito e, no canto superior esquerdo, um boto de menu do sistema. Contm uma seleo de controles comuns, incluindo um boto, botes de rdio, uma caixa de grupo e uma caixa combo. NOTA: Voc tambm pode desenhar nas formas usando as classes no namespace System::Drawing. Veremos como fazer isto no captulo 18.

16.1.1.

AS FORMAS DO WINDOWS E AS REAS DE DESENHO

Embora possa escrever cdigo para gerar formas, voc normalmente usar reas de desenho para criar a sua interface. O Microsoft Visual Studio .NET suporta a construo de GUI usando reas de desenho, que colocam uma forma na tela e lhe permitem arrastar componentes de uma caixa de utilitrios e coloc-los na forma. Um editor de propriedades lhe permite fixar as propriedades das formas e controles tais como cor e texto e facilitam a adio de manipuladores de eventos. Este modo de desenvolver formas ser familiar a quem j usou o Visual Basic. As formas sempre so construdas em tempo de execuo do cdigo e as reas de desenho simplesmente tomam o que voc fez na tela e geram o cdigo com o qual criaro a interface de usurio. Se abrir um projeto Visual Basic ou C# e observar o cdigo fonte, voc ver uma seo chamada WINDOWS FORM DESIGNER GENERATED CODE, que contm este cdigo. No havia rea de desenho includa no Microsoft Visual C++, na primeira verso do Visual Studio .NET. Assim, voc tinha que criar e povoar suas prprias formas, escrevendo o cdigo. A verso 7.1 inclui uma rea de desenho C++. Assim, voc pode desenvolver aplicativos GUI do mesmo modo que faria no Visual C# ou Visual Basic .NET.

16.1.2.

AS FORMAS DO WINDOWS VERSUS A MFC

Desde seus dias mais remotos, o Visual C++ comportou uma biblioteca chamada MFC (Microsoft Foundation Classes). Esta biblioteca usada para escrever aplicativos C++ para o Microsoft Windows e encapsula a maior parte da API Windows que lida com programao GUI, junto com algumas outras reas como bancos de dados e trabalho com rede. A MFC se tornou um padro para escrever aplicativos Windows no Visual C++. A verso mais recente est contida no Visual Studio .NET. Porm, ela nada utiliza do .NET e, provavelmente, voc no vai querer utiliz-la para novos desenvolvimentos Windows em C++. Porm, voc ainda poder precisar usar MFC quando tiver um aplicativo MFC e no quiser (ou no puder) atualiz-lo para usar as formas do Windows; tiver que usar alguns componentes MFC; realmente gostar da arquitetura documento-vista da MFC, que no suportada no .NET Framework.

Entretanto, na maioria esmagadora dos casos voc ir preferir usar o .NET Framework. A abordagem orientada para objetos da API Windows mais completa e o cdigo orientado para objetos melhor. Alm do mais, fcil praticar a programao de linguagem mista no .NET.

261

Jos Wilson Vieira 16.1.3. UMA PALAVRA SOBRE ATL Ainda sobre o tema bibliotecas C++ para programao Windows, mencionaremos outra biblioteca importante oferecida pela Microsoft, a ATL (Active Template Library). A ATL uma biblioteca C++ para escrever os mais rpidos e menores objetos COM possveis. Portanto, uma biblioteca muito especializada e poucas pessoas precisaro us-la agora que o .NET Framework transformou COM em uma tecnologia popular. NOTA: O .NET Framework diminuiu a necessidade dos desenvolvedores conhecer e usar COM porque ele fornece tecnologias que realizam algumas das tradicionais tarefas de COM (por exemplo, componentes gerais e controles Microsoft ActiveX). Assim, COM tornou-se mais fundamentos para especializaes, principalmente para a tecnologia servidor / apoio. Se quiser saber mais sobre COM no .NET, d uma olhada em COM PROGRAMMING WITH MICROSOFT .NET de Julian Templeman e John Paul Mueller (Microsoft Press, 2003).

16.2.

O namespace System::Windows::Forms

As classes da biblioteca de formas do Windows so fornecidas em dois namespaces: System::Windows::Forms e System::Windows::Forms::Design. Este ltimo contm classes para uso em tempo de desenho, o que tipicamente significa a personalizao e a extenso das reas de desenho usadas no Visual Studio .NET. Como este namespace mais avanado do que os objetivos deste livro, no o mencionaremos novamente. O System::Windows::Forms um namespace muito grande, contendo mais de 300 classes e enumeraes. A Tabela 16.1 mostra alguns dos principais membros deste namespace e voc encontrar mais alguns ao longo deste captulo. Tabela 16.1: Alguns membros do namespace System::Windows::Forms.
NOME Application AxHost BorderStyle DESCRIO Fornece mtodos static para gerenciamento de aplicativos, incluindo inicializao, parada e informaes sobre os mesmos. Encapsula controles Microsoft ActiveX de modo que eles podem ser usados como controles de formas do Windows. Uma enumerao que especifica o estilo de bordas para controles. BorderStyle tem trs membros: Fixed3D para bordas tridimensionais, FixedSingle para bordas de linha simples e None para nenhuma borda. Representa um controle de boto do Windows. Uma enumerao que especifica a aparncia de um boto. Inclui os membros Inactive, Pushed e Normal. Representa um controle de caixa de checagem do Windows. D acesso rea de transferncia do sistema. Mostra uma caixa de dilogo com amostras das cores padres. Representa um controle de caixa combo do Windows. Representa um cursor. Mostra dados Microsoft ADO.NET em uma grade com rolagem. Representa um controle para datas do Windows. Representa uma janela ou uma caixa de dilogo. Representa um controle de barra de rolagem horizontal do Windows. Representa uma coleo de imagens tipicamente usada por barras de rolagem. Representa um controle de rtulos do Windows. Representa um controle de caixa de lista do Windows. Mostra uma lista de itens em uma das quatro vistas. Representa um menu.

Button ButtonState CheckBox Clipboard ColorDialog ComboBox Cursor DataGrid DateTimePicker Form HScrollBar ImageList Label ListBox ListView Menu

262

Microsoft Visual C++ .NET Passo-a-Passo


MessageBox Panel ProgressBar RadioButton Splitter StatusBar TextBox ToolBar Mostra uma caixa de mensagem. Um controle que pode conter outros controles. Representa um controle de barra de progresso do Windows. Representa um controle de boto rdio do Windows. Fornece a funcionalidade de diviso para uma janela. Representa um controle de barra de status do Windows. Representa um controle de edio do Windows. Representa um controle de barra de utilitrios do Windows.

16.3.

CRIANDO E USANDO FORMAS

Como agora o Visual C++ .NET tem uma rea de desenho de GUI, voc j no precisa construir cdigo de GUI manualmente. Nesta seo lhe mostraremos como criar e usar formas com o Visual Studio .NET 2003.

16.3.1.

EXERCCIO: CRIANDO UMA FORMA SIMPLES

O exerccio a seguir mostra como criar e exibir uma forma simples. PASSO 1: No Visual Studio .NET, abra a caixa de dilogo New Project. Escolha a sub-pasta .NET na pasta Visual C++ Projects e crie um novo projeto Windows Forms Application (.NET) chamado CppForm (Figura 16.2). O Visual Studio .NET mostrar uma janela da rea de desenho da forma como a mostrada na Figura 16.3.

Figura 16.2: Criando um novo projeto Windows Forms Application (.NET). PASSO 2: Compile e execute o cdigo. Voc obter uma janela como a mostrada na Figura 16.4.

263

Jos Wilson Vieira

Figura 16.3: Janela de desenho da forma do Visual Studio .NET.

Figura 16.4: Sada do programa CppForm. Voc pode ver como a forma tem todos os atributos de uma janela: uma barra de ttulo com uma legenda e um boto de menu do sistema no canto superior esquerdo, os botes Minimize, Maximize e Close, e voc pode redimension-la. O tamanho e a legenda so fixados pelas propriedades no cdigo C++. Clique com o boto direito do mouse na forma mostrada na rea de desenho e escolha View Code no menu do contexto. Isto lhe mostrar o cdigo para o arquivo de cabealho Form1.h. Se examinar o cdigo na funo InitializeComponent, 264

Microsoft Visual C++ .NET Passo-a-Passo


void InitializeComponent(void) { this->components = new System::ComponentModel::Container(); this->Size = System::Drawing::Size(300,300); this->Text = S"Form1"; }

voc ver onde as propriedades Size e Text so fixadas. Mais adiante, neste captulo, voc ver com usar o editor de propriedades para mudar estas propriedades. PASSO 3: Clique no boto Close no canto superior direito da barra de ttulo para fechar a janela. Isto encerra o processamento de mensagens e fecha a janela. O QUE UM LAO DE MENSAGENS? Se voc no sabe como funciona um aplicativo Windows, eis uma rpida introduo. Um lao de mensagens o corao de todo aplicativo Windows, fornecendo a bomba que direciona a execuo do programa. As partes de um aplicativo Windows, tais como formas, botes e barras de rolagem, se comunicam entre si, com outros aplicativos e com o sistema passando mensagens. No mundo da programao Windows, uma mensagem um pequeno pacote de dados que enviado para um componente para lhe informar que alguma coisa aconteceu. Estes acontecimentos, chamados eventos, podem incluir um cronmetro que foi ligado, uma tecla que foi pressionada em um teclado ou que o usurio acionou um dos botes do mouse. Uma mensagem uma estrutura que descreve um evento e o Windows entrega a mensagem ao aplicativo apropriado, colocando-a na fila de mensagens do aplicativo. Um nmero muito grande de eventos entregue a um aplicativo Windows a todo instante, mesmo quando parece que nada est acontecendo. No corao do aplicativo se encontra o lao de mensagens, um lao de pouco cdigo que remove uma mensagem de cada vez da fila de mensagens e envia cada uma para a parte correta do aplicativo para processamento. Em tempos pr-histricos, quando os nicos utilitrios que os programadores tinham para escrever programas Windows era um compilador C e uma cpia Windows do SDK, voc tinha que codificar manualmente o lao de mensagens e compreender a arquitetura por trs do processamento das mensagens. Agora temos frameworks de desenvolvimento tais como a MFC e o .NET Framework que fazem toda a administrao domstica para voc. Assim voc no precisa entrar nos detalhes de como ocorre o processamento das mensagens. Se quiser, voc pode fazer coisas avanadas e inteligentes, mas, para vasta maioria dos aplicativos, isto no necessrio. Assim, quando quiser executar uma forma como a GUI para seu aplicativo, voc precisa inicializar um lao de mensagens de modo que a forma possa processar mensagens. Voc faz isto usando a funo Application::Run, que tem o efeito de inicializar o lao de mensagens e mostrar a forma (veja no arquivo-fonte Form1.cpp do exerccio acima). O lao de mensagens se mantm executando at receber uma mensagem de sada, que pode ser enviada do cdigo do aplicativo ou pelo sistema operacional como um resultado do usurio fisicamente ter fechado a janela. Em ambos os casos, o lao de mensagens termina, a janela fechada e o aplicativo encerra a execuo.

16.3.2.

USANDO AS PROPRIEDADES DE Form

Agora que dominou os fundamentos da exibio de uma forma, vamos ver como voc pode alterar o modo como a forma vista e o seu comportamento. Quando examinar o cdigo no arquivo de cabealho Form1.h, voc ver que a forma representada por uma classe herdeira de 265

Jos Wilson Vieira System::Windows::Forms::Form. Voc usa esta classe como a classe-base sempre que quiser criar uma forma. Ei-la:
public __gc class Form1 : public System::Windows::Forms::Form { public: Form1(void) { InitializeComponent(); } protected: void Dispose(Boolean disposing) { if (disposing && components) { components->Dispose(); } __super::Dispose(disposing); } private: /// <summary> /// Required designer variable. /// </summary> System::ComponentModel::Container * components; /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> void InitializeComponent(void) { this->components = new System::ComponentModel::Container(); this->Size = System::Drawing::Size(300,300); this->Text = S"Form1"; } };

A classe Form tem um grande nmero de propriedades e mtodos. Os mais importantes so listados na Tabela 16.2. A classe Form tambm tem um nmero muito grande de mtodos e propriedades herdados das suas classes-bases, que so mostradas abaixo:
Object MarshalByRefObject Component Control ScrollableControl ContainerControl Form

Note especialmente a classe Component, que constitui a base para todos os componentes que podem ser usados em formas; e a classe Control, que fornece a classe-base para todos os componentes visuais. Todas as classes-bases juntas fornecem para Form cerca de 110 propriedades, 180 mtodos e 70 eventos! muita coisa para listar aqui e sugerimos que voc consulte a documentao do .NET Framework para mais detalhes. Quando usarmos propriedades herdadas nos exerccios ou exemplos deste captulo, lhe indicaremos de que classe-base procedem.

266

Microsoft Visual C++ .NET Passo-a-Passo Tabela 16.2: Principais propriedades e mtodos da classe Form.
MTODO OU DESCRIO PROPRIEDADE? AcceptButton P Obtm ou fixa referncia para o controle de boto que corresponde ao ato do usurio pressionar Enter. Activate M Ativa a janela, levando-a para o topo da coleo de janelas do aplicativo. ActiveForm P Obtm a forma atualmente ativa para este aplicativo, que a do topo da coleo de janelas do aplicativo. AutoScale P Obtm ou fixa um valor booleano indicando se a forma ajusta suas dimenses com base na altura da fonte usada e dimensiona seus controles adequadamente. O padro true. CancelButton P Obtm ou fixa a referncia a um controle de boto que corresponde ao ato do usurio pressionar Esc. ClientSize P Obtm ou fixa a dimenso da rea cliente da forma (a rea cliente a poro da forma que exclui a barra de ttulo e as bordas). Close M Fecha a forma e libera quaisquer recursos que ela tenha usado. DesktopLocation P Obtm ou fixa a localizao da forma na desktop do Windows. FormBorderStyle P Obtm ou fixa o estilo de borda da forma. O padro FormBorderStyle::Sizeable. HelpButton P Obtm ou fixa um valor booleano indicando se a forma deve mostrar um boto de ajuda na barra de ttulo. O padro false. Icon P Obtm ou fixa o cone associado com a forma. MaximizeBox P Obtm ou fixa um valor booleano que indica se a forma deve mostrar uma caixa Maximize na barra de ttulo. O padro true. Menu P Obtm ou fixa uma referncia ao menu que mostrado na forma. MinimizeBox P Obtm ou fixa um valor booleano que indica se a forma deve mostrar uma caixa Minimize na barra de ttulo. O padro true. OwnedForms P Suporta a coleo de formas-filha prprias para esta forma, se existir. SetDesktopLocation M Fixa a localizao da forma na desktop. ShowDialog M Mostra a forma como uma caixa de dilogo modal. ShowInTaskBar P Obtm ou fixa um valor booleano, que true se a forma vai ser mostrada na barra de tarefas do Windows. O padro true. Size P Obtm ou fixa a dimenso da forma. SizeGripStyle P Determina como (ou mesmo se) o punho de dimensionamento mostrado no canto inferior direito da forma. O padro SizeGripStyle::Hide. TopLevel P Obtm ou fixa um valor booleano, que true se a forma uma janela do topo, i.e., no tem me a no ser a desktop do Windows. TopMost P Obtm ou fixa um valor booleano, que true se a forma uma janela topmost, i.e., ela sempre mostrada sobre as outras janelas, mesmo quando no tem o foco. O padro false. WindowState P Obtm ou fixa o estado de janela da forma, que determina como a forma mostrada minimizada, maximizada ou normal. O padro FormWindowState::Normal. NOME

267

Jos Wilson Vieira


16.3.2.1. EXERCCIO: USANDO O EDITOR DE PROPRIEDADES

Embora voc possa interagir com as propriedades da forma no cdigo, a rea de desenho fornece um editor de propriedades que lhe permite acesso grfico a todas as propriedades usadas com mais freqncia. Neste exerccio, voc ver como usar o editor de propriedades para editar propriedades de uma forma de modo que a faz-las aparecer onde e como voc deseja. PASSO 1: Abra o projeto CppForm e assegure-se de que a janela de desenho est na tela. Se no estiver, d um clique duplo no arquivo Form1.h no Solution Explorer. Voc ver que o arquivo de cabealho de forma tem um cone diferente dos outros arquivos de cabealho e arquivosfontes (Figura 16.5), porque ele o arquivo que contm a definio da forma e tambm usado pela rea de desenho.

Figura 16.5: Projeto CppForm mostrando a janela de desenho e os arquivos. Clique com o boto direito do mouse na forma e escolha Properties no menu de contexto. O editor de propriedades aparecer. Selecione a propriedade Text e mude seu valor para dar uma legenda mais apropriada forma, como vemos na Figura 16.6. A propriedade Text herdada da classe Control e est sendo usada aqui para fixar o texto associado com o controle. Muitos controles tm um pequeno texto associado a eles. No caso de um editor de controle, trata-se do texto no controle; em um boto, a legenda no boto; em uma forma, o ttulo mostrado na sua barra de ttulo. Se voc reconstruir o cdigo e execut-lo, ver que, agora, a forma mostra uma legenda (Figura 16.7). PASSO 2: A borda da forma representada pela propriedade FormBorderStyle, que toma seu valor da enumerao FormBorderStyle. O valor do estilo de borda padro FormBorderStyle::Sizeable, que fornece uma borda simples cuja linha de contorno voc pode arrastar para redimensionar a forma. Use o editor de propriedades para mudar o estilo da borda para Fixed3D. PASSO 3: Compile e execute o cdigo e ver que o estilo da borda (Figura 16.8) mudou e voc j no consegue redimensionar a janela. 268

Microsoft Visual C++ .NET Passo-a-Passo Experimente outras propriedades de Form e observe o resultado.

Figura 16.6: O editor de propriedades.

Figura 16.7: Sada atual do CppForm.

269

Jos Wilson Vieira

Figura 16.8: Sada atual do CppForm.

16.3.3.

RELAES ENTRE FORMAS

Qualquer forma pode criar outras formas pense na visualizao de uma caixa de dilogo e por padro, as formas sero independentes uma da outra, de modo que voc pode minimiz-las e fech-las separadamente. Haver uma relao me/filha entre as formas, mas, a menos disto, elas so independentes. A forma do primeiro nvel, que normalmente usada para uma janela principal de aplicativo, ou no tem uma forma-me ou filha da janela do desktop. Tambm possvel que uma forma do primeiro nvel seja proprietria de outra forma de primeiro nvel. Neste caso, h uma relao entre as duas formas tal que a forma possuda: minimizada, maximizada e escondida junta com sua dona; fechada quando sua dona fechada; nunca exibida antes da sua dona.

Tome como exemplo a caixa de dilogo Localizar e substituir do Microsoft Word (Figura 16.9). Esta janela aparece quando voc quer encontrar alguma coisa no documento e paira sobre a janela do Word at que voc a feche. Se voc minimizar o Word, a caixa de dilogo tambm minimizada, e ela desaparece quando voc fecha o Word.

Figura 16.9: Janela Localizar e substituir do Microsoft Word.

270

Microsoft Visual C++ .NET Passo-a-Passo 16.3.4. EXERCCIO: COLOCANDO CONTROLES NA FORMA PASSO 1: Abra o projeto CppForm. PASSO 2: Abra a janela de desenho e a caixa de utilitrios. Se a caixa de utilitrios ainda no estiver visvel, voc pode abri-la usando o menu View Toolbox ou pressionando Ctrl + Alt + X. Se a aba Windows Forms no estiver abre na caixa de utilitrios, selecione-a. A Figura 16.10 mostra a caixa de utilitrios com todos os controles que podem ser usados com o Windows Forms.

Figura 16.10: caixa de utilitrios. Clique na entrada Button na caixa de utilitrios e arraste-a para a forma. Quando liberar o mouse, voc ver o boto aparecer na forma. PASSO 3: Selecione o boto e use o editor de propriedades para fixar Name para btn1, Text para OK, Size para 70; 25 e Location para 130; 225. As coordenadas para Location esto em pixels, relativos ao canto superior esquerdo da forma que contm o boto. Se examinar o cdigo em Form1.h (d um clique duplo no boto) e prestar ateno funo InitializeComponent, voc ver que lhe foi adicionado cdigo para criar e configurar o boto. A rea de desenho altera o cdigo de InitializeComponent quando voc edita a forma. Assim, voc deve ter cuidado ao editar manualmente a funo InitializeComponent. Eis o cdigo atual:
void InitializeComponent(void) { this->btn1 = new System::Windows::Forms::Button(); this->SuspendLayout(); // // btn1 // this->btn1->Location = System::Drawing::Point(130, 225);

271

Jos Wilson Vieira


this->btn1->Name = S"btn1"; this->btn1->Size = System::Drawing::Size(70, 25); this->btn1->TabIndex = 0; this->btn1->Text = S"OK"; this->btn1->Click += new System::EventHandler(this, btn1_Click); // // Form1 // this->AutoScaleBaseSize = System::Drawing::Size(5, 13); this->ClientSize = System::Drawing::Size(292, 266); this->Controls->Add(this->btn1); this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::Fixed3D; this->Name = S"Form1"; this->Text = S"Forma de Teste"; this->ResumeLayout(false); }

Note como o boto foi adicionado propriedade Controls da forma. Todo continer (tais como uma forma) tem uma propriedade Controls que contm referncias a todos os controles atualmente suportados pela forma. Adicionando um controle coleo, a forma o mostrar e o tratar como uma janela-filha. PASSO 4: Adicione um segundo boto forma com o nome btn2. Sua legenda Cancelar, seu tamanho o mesmo do boto OK, mas coloque-o na posio (210, 225). Compile e execute o cdigo e voc ver os botes na forma como na Figura 16.11.

Figura 16.11: Sada atual do programa CppForm.

16.3.5.

MANIPULANDO EVENTOS

No se colocar botes em uma forma a no ser para fazermos algo com eles. Assim, vamos verificar como manipular os eventos que so enviados pelos controles. O mecanismo de eventos .NET foi abordado no captulo 14. Assim, voc pode rever o material agora, se quiser refrescar a memria. Para manipular um evento, voc precisa criar uma funo manipuladora de eventos e anex-la fonte do evento. No captulo 14, voc viu como fazer isto com classes personalizadas para fonte e receptor de eventos. Agora, mostraremos como fazer isto com classes padres de fontes e receptores de eventos no formato de controles. 272

Microsoft Visual C++ .NET Passo-a-Passo

16.3.5.1.

EXERCCIO: ADICIONANDO MANIPULADORES DE EVENTOS AOS BOTES

Neste exerccio, que uma seqncia do anterior, voc ver como adicionar manipuladores de eventos para os botes na forma. PASSO 1: Abra o projeto CppForm. A forma atualmente contm dois botes e voc vai adicionar manipuladores para os eventos de Click que so criados quando os botes so clicados. Selecione o boto OK e exponha o editor de propriedades. Clique no boto Event no topo da janela do editor de propriedades o boto cujo smbolo um raio e o editor mostrar o evento associado com o boto. Encontre o evento Click na coluna da esquerda e d um clique duplo no espao em branco direita. O nome btn1_Click ser inserido no espao em branco (se ainda no estiver) e uma funo com o mesmo nome ser adicionada ao cdigo. PASSO 2: Esta funo chamada quando o boto for clicado e, apenas para provar que o manipulador chamado, adicione a linha para exibir uma caixa de mensagem, como abaixo:
private: System::Void btn1_Click(System::Object * sender, System::EventArgs * { MessageBox::Show(S"Funciona!", S"Mensagem..."); } e)

A classe MessageBox parte do namespace Forms e um dos seus mtodos, Show, usado para mostrar uma caixa de mensagem padronizada do Windows. Show tem 12 sobrecargas que recebem diferentes combinaes de argumentos e a que usamos acima apenas especifica a mensagem e o ttulo da caixa. Esta caixa de mensagem aparecer sem um cone e com um nico boto OK. PASSO 3: Observe em InitializeComponent como o boto foi alertado sobre o manipulador:
this->btn1->Click += new System::EventHandler(this, btn1_Click);

Da discusso sobre eventos no captulo 14, voc se recordar que os manipuladores de evento so conectados com as fontes de evento por delegados. H diversos manipuladores de evento diferentes associados com controles (encontraremos alguns neste captulo), mas o delegado usado para controlar quaisquer dados em trnsito chamado EventHandler e ele retorna um objeto EventArgs. Assim, para conectar um manipulador de evento ao objeto de boto, um novo EventHandler criado, com o qual passado um ponteiro para o objeto que vai manipular o evento e o endereo da funo manipuladora de evento dentro do objeto. Neste caso, o evento est sendo manipulado dentro do objeto de forma. Assim, o ponteiro this passado como o primeiro argumento e o endereo da funo btn1_Click como o segundo. NOTA: Relembre que em C++ o nome de uma funo, quando usado sem os parnteses, avalia o endereo da funo. O manipulador de evento registrado com a fonte usando o operador += para vincular o manipulador ao evento Click do boto. PASSO 4: Construa e execute o aplicativo. A Figura 16.12 mostra a sada atual do CppForm. Note que a caixa de mensagem modal, i.e., voc no retorna para o aplicativo at que despache a caixa de mensagem clicando no boto OK. PASSO 5: Experimente adicionar um manipulador similar para o boto Cancelar, editando o cdigo abaixo:
private: System::Void btn2_Click(System::Object * { sender, System::EventArgs * e)

273

Jos Wilson Vieira


MessageBox::Show(S"Clique OK para fechar o programa", S"Ateno"); Application::Exit(); }

Figura 16.12: Sada atual do programa CppForm.

16.4.

USANDO CONTROLES

Agora que voc j viu os fundamentos da adio de controles s formas e a manipulao de eventos, vamos observar alguns dos controles que esto disponveis para uso nos aplicativos de formas do Windows. Nesta seo veremos os controles mais fundamentais e no captulo 17 lidaremos com outros controles mais detalhadamente. Todos estes controles so herdeiros das classes Component e Control, que lhes dispem um grande nmero de mtodos e propriedades em comum. CONTROLES E EVENTOS Embora alguns controles tais como cones e rtulos possam ser usados de um modo puramente decorativo ou para apresentar informaes ao usurio, muitos controles so usados para permitir que o usurio interaja com o aplicativo. Os controles disparam eventos quando o usurio interage com eles de algum modo, como por exemplo, clicando em um boto, deslocando uma barra de rolagem ou escolhendo uma data de um controle de calendrio. Exatamente quais e como os diversos eventos so disparados varia de controle para controle. Botes, por exemplo, tendem a disparar um nico evento Click quando o usurio os pressiona. Um controle mais complexo, como um controle de rvore, pode disparar diversos eventos diferentes porque h muitos modos atravs dos quais um usurio pode interagir com a rvore. Quando usar um controle, voc precisa consultar a documentao do .NET Framework para descobrir quais eventos podem ser disparados e quais funes manipuladoras voc precisa providenciar. NOTA: Voc tambm pode criar seus prprios controles para usar nas formas, mas esta tarefa no faz parte do escopo deste livro.

274

Microsoft Visual C++ .NET Passo-a-Passo 16.4.1. RTULOS Um rtulo um controle usado para fornecer texto descritivo em uma forma. Os usurios normalmente no interagem com controles de rtulo e estes controles usualmente no recebem o foco de entrada. Assim, no usual que os programas manipulem eventos originados de rtulos. O uso de rtulos consiste em cri-los e, ento, fixar suas propriedades. A Tabela 16.3 mostra as propriedades mais comumente usadas da classe Label. Tabela 16.3: Propriedades mais comuns da classe Label.
PROPRIEDADE DE Label AutoSize BackColor (herdada de Control) BorderStyle FlatStyle Font (herdada de Control) ForeColor (herdada de Control) Image ImageAlign ImageIndex ImageList PreferredHeight PreferredWidth RenderTransparent TabStop Text TextAlign UseMnemonic DESCRIO Determina se o rtulo deve automaticamente se redimensionar para ajustar o texto. Representa a cor do fundo do rtulo. Obtm ou fixa o estilo da borda do rtulo: tridimensional, simples ou nenhuma. Determina se o rtulo tem uma aparncia plana. Representa a fonte do controle. Representa a cor do primeiro plano do rtulo. Obtm ou fixa a imagem associada com o rtulo. Representa o alinhamento da imagem no rtulo. O padro centralizado. O ndice da imagem para ser usado na ImageList associada. A ImageList a ser usada como uma fonte de imagens para este rtulo. Obtm a altura necessria para mostrar uma linha de texto. Obtm a largura necessria para mostrar o texto atual. Determina se o fundo do continer ser mostrado como o fundo do rtulo. Determina se o usurio tabula para este controle. Representa o texto do rtulo. Obtm ou fixa o alinhamento de texto. O padro alinhado esquerda. Determina se o caractere & pode ser interpretado como indicador de tecla de acesso no texto.

H um par de itens na Tabela 16.3 que merecem uma explicao mais detalhada. Os rtulos participam da ordem de tabulao nas formas mas usualmente no recebem o foco. Se, por alguma razo, voc quer passar o foco para um rtulo, fixe a propriedade TabStop para true. Alm disso, o caractere & normalmente interpretado como indicador de tecla de acesso quando ele est em menus, onde a presena de um & sublinha o caractere seguinte a ele no texto. Se voc que & seja interpretado como indicador de tecla de atalho, fixe a propriedade UseMnemonic para true. Os rtulos podem mostrar uma imagem bem como (ou ao invs de) texto e a propriedade Image representa a imagem atualmente associada com o rtulo. Voc tambm pode usar imagens armazenadas em controles ImageList, via as propriedades ImageList e ImageIndex.

16.4.1.1.

EXERCCIO: ADICIONANDO UM RTULO A UMA FORMA E MANIPULANDO SUAS PROPRIEDADES

275

Jos Wilson Vieira O exerccio a seguir mostra como adicionar um rtulo a uma forma e como manipular as suas propriedades. PASSO 1: Abra o projeto CppForm. Abra a caixa de utilitrios e arraste um Label para a forma. Voc pode deixar o nome do controle como label1. PASSO 2: Fixe o Text do rtulo para Acme Consulting, Inc., e fixe sua propriedade AutoSize para true. Reserve trs espaos antes da palavra Acme; voc vai mostrar a uma imagem e precisa deixar espao de modo que a imagem no se sobreponha ao comeo do texto. A fixao da propriedade AutoSize para true significa que o rtulo se redimensionar para conter qualquer texto a ele atribudo. PASSO 3: Agora configure os detalhes da fonte para o rtulo. Verifique se a propriedade ForeColor est fixada para ControlText, cujo padro a cor preta. Ento, encontre a propriedade Font e clique no sinal + esquerda para expandir os itens da propriedade Font. Use a lista drop-down para fixar Name para Verdana, Italic para true e Size para 16. PASSO 4: Fixe Location para o rtulo em 10;20. PASSO 5: Agora, adicione uma imagem ao rtulo. Encontre a propriedade Image no editor de propriedades e use o boto Browse para encontrar um bitmap satisfatrio para exibir. H um pequeno bitmap na pasta do projeto CppForm do disco do livro, chamado Floppy.bmp. Quando tiver selecionado a imagem, fixe a propriedade ImageAlign para MiddleLeft, que especifica o alinhamento mdio na vertical e esquerda horizontalmente.Quando clicar no boto da seta para baixo direita do valor de ImageAlign, voc ver um modo grfico para escolher o alinhamento. PASSO 6: Construa e execute o programa e voc ver a sada mostrada na Figura 16.13.

Figura 16.13: Sada atual do programa CppForm.

16.4.2.

BOTES

Voc j encontrou a classe Button neste captulo e viu como adicionar botes s formas. Um Button representa um boto em uma forma e, depois do Label, provavelmente o mais simples dos controles Windows comumente usados. As propriedades mais freqentemente usadas da classe Button so listadas na Tabela 16.4.

276

Microsoft Visual C++ .NET Passo-a-Passo Tabela 16.4: Propriedades da classe Button usadas com mais freqncia.
PROPRIEDADE DE Button DialogResult FlatStyle (herdada de ButtonBase) Image (herdada de ButtonBase) ImageAlign (herdade de ButtonBase) IsDefault (herdada de ButtonBase) TextAlign (herdada de ButtonBase) DESCRIO Representa o valor que retornado para a forma-me quando o boto clicado. Determina se o boto desenhado com um estilo plano. Obtm ou fixa a imagem mostrada no boto. Obtm ou fixa o alinhamento da imagem. O valor padro MiddleCenter. Determina se o boto o padro da forma. Obtm ou fixa o alinhamento do texto no boto.

Um boto em uma forma pode ser designado para boto padro, caso em que mostrado com uma borda mais acinzentada do que os outros botes da forma. O ato de pressionar Enter, tomado como equivalente a clicar no boto padro. As formas que so usadas como caixas de dilogo utilizam botes para fechar a caixa de dilogo e retornar um valor para o chamador. A propriedade DialogResult pode ser usada para atribuir um cdigo de resultado (tais como OK ou Cancelar) para a forma e clicando um boto que tenha uma DialogResult fixada voc fechar a forma-me sem que tenha que encadear quaisquer manipuladores de evento.

16.4.3.

CAIXAS DE CHECAGEM E BOTES DE RDIO

Se observar a documentao do .NET Framework, voc descobrir que Button, CheckBox e RadioButton tm a mesma classe-base, ButtonBase. Esta classe-base compartilhada porque as trs classes so tipos diferentes de boto, cada uma compartilhando a mesma funcionalidade liga/desliga mas contribuindo com suas caractersticas especiais. A Tabela 16.5 mostra algumas propriedades de CheckBox e RadioButton. Tabela 16.5: Algumas propriedades de CheckBox e RadioButton.
PROPRIEDADE AutoCheck DESCRIO Determina se a aparncia do controle muda automaticamente quando o usurio clica nele (ao invs de ser fixado pelo cdigo). Representa o alinhamento da caixa de checagem. O padro MiddleLeft. Representa o estado de checagem do controle, com true representando marcado. Representa o estado da CheckBox: marcado, no marcado ou indeterminado (aparece escurecido). Obtm ou fixa a imagem mostrada no boto. Obtm ou fixa o alinhamento da imagem. O valor padro MiddleCenter. Se true, a caixa de checagem pode mostrar trs estados: marcado, no marcado ou indeterminado.

CheckAlign Checked CheckState (apenas em CheckBox) Image (herdada de ButtonBase) ImageAlign (herdada de ButtonBase) ThreeState (apenas em CheckBox)

CheckBox e RadioButton podem disparar eventos quando as propriedades Appearance e Checked mudam e CheckBox tambm dispara um evento se a propriedade CheckState muda.

277

Jos Wilson Vieira 16.4.4. USANDO BOTES DE RDIO COM UM GRUPO comum usar botes de rdio para permitir que o usurio selecione uma de um conjunto de opes. Para fazer isto, voc fornece um conjunto de botes de rdio dentro de um controle de caixa de grupo. A caixa de grupo no apenas limita os botes visualmente, mas tambm faz com que eles atuem como um grupo, garantindo que, quando qualquer um for selecionado, os outros so desmarcados.

16.4.4.1.

EXERCCIO: CONFIGURANDO UMA CAIXA DE GRUPO CONTENDO BOTES DE RDIO

O exerccio a seguir mostra como configurar uma caixa de grupo contendo botes de rdio em uma forma. PASSO 1: Abra o projeto CppForm. Abra a caixa de utilitrios e arraste uma GroupBox para a forma. Use o edito de propriedades para mudar seu Text para Linguagem, seu Size para 200;104 e sua Location para 24;64. PASSO 2: Arraste um RadioButton para a forma e o coloque dentro da caixa de grupo. Voc ver que a caixa de grupo corta o boto de rdio de modo que nenhuma parte do boto mostrada fora da borda da caixa de grupo. Posicione o boto no topo da caixa de grupo e fixe seu Text para Visual Basic. PASSO 3: Adicione outros dois botes de rdio caixa de grupo, posicionando-os abaixo do primeiro. Fixe suas propriedades Text para C# e C++, respectivamente. PASSO 4: Construa e teste o aplicativo. Voc ver os botes de rdio dentro da caixa de grupo e, quando marcar um deles, os outros no desmarcados. Por padro, nenhum dos botes marcado quando o aplicativo inicializa. Para inicializar com um dos botes marcados, use o editor de propriedades para fixa sua propriedade Checked para true. A Figura 16.14 mostra a sada do CppForm.

Figura 16.14: Sada atual do programa CppForm.

16.4.5.

CAIXAS DE LISTA E CAIXAS COMBO

As caixas de lista e as caixas combo so caractersticas comuns dos aplicativos Windows, cada uma representando uma lista com rolagem de itens. A classe ComboBox difere da ListBox porque ComboBox pode mostrar um item em um TextControl acima da lista. Estas duas classes tm 278

Microsoft Visual C++ .NET Passo-a-Passo muitas caractersticas em comum porque ambas se derivam da classe ListControl, que fornece alguma funcionalidade comum. A Tabela 16.6 mostra as propriedades mais utilizadas da classe ListBox.

Tabela 16.6: Propriedades mais comuns da classe ListBox.


PROPRIEDADE ColumnWidth HorizontalScrollbar IntegralHeight Items MultiColumn PreferredHeight DESCRIO Obtm ou fixa as colunas em uma caixa de lista de multicolunas. Obtm ou fixa um valor indicando se o controle mostra uma barra de rolagem. Fixa true se a ListBox ajustar as suas dimenses de modo que no exiba itens parciais. Representa a coleo de itens contida na caixa de lista. Fixa true se a caixa de lista suporta mltiplas colunas. O padro false. Obtm a altura combinada de todos os itens na caixa de lista. Voc pode usar este valor para redimensionar a caixa de lista de modo que ela mostre todos os itens sem barras de rolagem. Se fixada para true, a barra de rolagem vertical sempre estar visvel. Representa o ndice baseado em zero do item atualmente selecionado, ou 1 se no houver seleo. Para caixas de lista de multi-seleo, representa uma coleo dos ndices de todos os itens atualmente selecionados na caixa de lista. Obtm ou fixa o objeto atualmente selecionado na caixa de lista. Para caixas de lista de multi-seleo, representa uma coleo de todos os itens atualmente selecionados na caixa de lista. Representa o modo de seleo da lista (SelectionMode discutida no texto seguinte). Fixa true se os itens na caixa de lista sero ordenados. O padro false. Representa o texto do item atualmente selecionado na caixa de lista. Se voc fixar esta propriedade para uma string, o controle procura o primeiro item que case com a string e o seleciona. O ndice do item do topo visvel no controle.

ScrollAlwaysVisible SelectedIndex SelectedIndices

SelectedItem SelectedItems

SelectionMode Sorted Text

TopIndex

A propriedade SelectionMode (uma enumerao) representa os modos como os usurios podem selecionar itens da lista. So eles: SelectionMode::None: Significa que o usurio no pode selecionar itens. SelectionMode::One: O usurio pode selecionar um item de cada vez. Este o valor padro. SelectionMode::MultiSimple: O usurio pode selecionar mais de um item de uma vez. SelectionMode::MultiExtended: O usurio pode usar Shift, Ctrl e as teclas de seta para fazer selees.

A classe ListBox tambm suporta alguns mtodos. Os mais comuns so mostrados na Tabela 16.7. O principal evento disparado pela classe ListBox o SelectedIndexChanged, que enviado quando um usurio seleciona um novo item na lista. 279

Jos Wilson Vieira Tabela 16.7: Mtodos mais comuns suportados pela classe ListBox.
MTODO BeginUpdate, EndUpdate ClearSelected FindString, FindStringExact GetSelected SetSelected DESCRIO BeginUpdate impede que a caixa de lista seja redesenhada at que EndUpdate seja chamada. Limpa a seleo de itens na caixa de lista. Encontra o primeiro item na caixa de lista que comea com uma dada string ou que case exatamente com a string. Retorna true se o item com o ndice especificado for selecionado. Fixa ou limpa a seleo para um item na caixa de lista.

A classe ComboBox tem conjuntos de mtodos e propriedades similares, como as mostradas nas Tabelas 16.8 e 16.9. Tabela 16.8: Propriedades mais comuns da classe ComboBox.
PROPRIEDADE DropDownStyle DropDownWidth DroppedDown IntegralHeight DESCRIO Representa o estilo da caixa combo. Representa a largura em pixels da caixa drop-down. Fixa true se a poro da lista atualmente mostrada. Fixa true se a caixa combo deve se ajustar de modo que no mostre itens parciais. Items Representa a coleo dos itens contida na caixa combo. MaxDropDownItems Representa o nmero mximo de itens na lista drop-down. O valor deve ser entre 1 e 100. MaxLength Representa o comprimento mximo do texto na caixa de texto. SelectedIndex Obtm ou fixa o ndice baseado em zero do item atualmente selecionado. O valor 1 se no houver seleo. SelectedItem Obtm ou fixa o item atualmente selecionado. SelectedText Representa o texto atualmente selecionado na poro da caixa de texto do controle da caixa combo. SelectionStart, Obtm ou fixa a posio inicial e o comprimento do texto selecionado na poro da caixa de texto do controle da caixa SelectionLength combo. Sorted Determina se a lista na caixa combo ordenada. Text Obtm ou fixa o texto na poro da caixa de texto do controle da caixa combo.

Tabela 16.9: Mtodos mais comuns da classe ComboBox.


MTODO BeginUpdate, EndUpdate FindString, FindStringExact Select SelectAll DESCRIO BeginUpdate impede que a caixa combo seja redesenhada at que EndUpdate seja chamada. Isto melhora a performance quando adicionamos muitos itens. Encontra o primeiro item na caixa combo que comea com uma dada string ou que case exatamente com a string. Seleciona um intervalo de texto na poro da caixa de texto da caixa combo. Seleciona todo o texto na poro da caixa de texto da caixa combo.

A caixa combo pode ter um dos seguintes estilos: ComboBoxStyle::DropDown: A caixa de texto pode ser editada e o usurio deve clicar na seta para mostrar a lista. Este o estilo padro.

280

Microsoft Visual C++ .NET Passo-a-Passo ComboBoxStyle::DropDownList: Idntica DropDown, exceto que a caixa de texto no pode ser editada. ComboBoxStyle::Simple: A caixa de texto pode ser editada e a lista sempre est visvel.

Como no caso da ListBox, o principal evento disparado pela classe ComboBox o SelectedIndexChanged, que enviado quando um usurio seleciona um novo item na lista.

16.4.5.1.

EXERCCIO: CONFIGURANDO UMA CAIXA COMBO E RESPONDENDO AOS EVENTOS QUE ELA DISPARA

O exerccio abaixo mostrar como configurar uma caixa combo e responder aos eventos que ela envia. Voc ver que as caixas de lista funcionam de modo muito parecido com os das caixas combo. PASSO 1: Abra o projeto CppForm. PASSO 2: Arraste uma ComboBox da caixa de utilitrios para a forma e mude seu nome para combo1. PASSO 3: Fixe DropDownStyle para DropDownList, o que significa que o usurio tem que clicar no boto direito da caixa de texto para exibir a lista e que o texto na caixa de texto no pode ser editado. A propriedade Text torna-se, automaticamente, vazia. PASSO 4: Adicione algumas strings caixa combo usando o editor de propriedades. Primeiro, localize a propriedade Items e clique no boto de browse direita da string (Collection), que aparecer o editor de coleo de strings (Figura 16.15). Digite as palavras Iniciante, Intermedirio e Avanado, uma em cada linha, e pressione OK para esconder o editor.

Figura 16.15: Editor de coleo de strings da classe ComboBox. PASSO 5: Para mostrar a primeira string na caixa combo quando o aplicativo inicializa, voc precisa fixar a propriedade SelectedIndex. Isto no pode ser feito no editor de propriedades. Assim, 281

Jos Wilson Vieira adicione a seguinte linha de cdigo ao construtor Form1 no arquivo de cabealho Form1.h, logo aps a chamada funo InitializeComponent:
combo1->SelectedIndex = 0;

PASSO 6: O evento SelectedIndexChanged ser enviado sempre que o usurio selecionar um novo item na lista drop-down. Use o editor de propriedades para mostrar os eventos para a caixa combo pressionando o boto Event no topo do editor. Encontre o evento SelectedIndexChanged e d um clique duplo na coluna em branco direita. Uma funo de manipulador ser adicionada classe da forma. Edite o cdigo do manipulador como abaixo:
private: System::Void combo1_SelectedIndexChanged(System::Object * sender, System::EventArgs * e) { if(sender == combo1) { String *ps = String::Concat(S"O novo ndice ", __box(combo1->SelectedIndex)->ToString()); MessageBox::Show(ps, S"Mudana de ndice"); } }

A funo checa se o remetente foi combo1 e, em caso afirmativo, ela constri uma string contendo o ndice da nova seleo. A string construda usando a funo static Concat da classe String e, para converter o valor SelectedIndex de um inteiro para uma String, primeiro o inteiro encaixotado de modo que ToString possa ser chamada no objeto resultante. A string final mostrada em uma caixa de mensagem de modo que voc veja que ocorreu a seleo. PASSO 7: Construa e execute o projeto e voc ver uma caixa combo na forma que contm trs itens. Voc tambm obtm uma caixa de mensagem sempre que seleciona um novo item, como mostrado na Figura 16.16.

Figura 16.16: Sada atual do programa CppForm.

16.4.6.

CAIXAS DE TEXTO

O namespace System::Windows::Forms tem duas classes de controle de edio, ambas derivadas de TextBoxBase. Agora veremos a TextBox e, no captulo 17, voc ver a classe mais avanada RichTextBox.

282

Microsoft Visual C++ .NET Passo-a-Passo A classe TextBox um controle de edio Windows que fornece alguns mtodos e propriedades para manipular a entrada de texto no controle. Na verdade, a TextBox herda a maioria dos seus mtodos e propriedades de TextBoxBase e as Tabelas 16.10 e 16.11 listam os membros mais comumente usados. Tabela 16.10: As propriedades mais comuns da classe TextBox.
PROPRIEDADE AcceptsTab DESCRIO Se true, a tecla Tab inserir um caractere de tabulao no controle ao invs de deslocar para o prximo controle na ordem de tabulao. O padro false. Se true, o controle automaticamente se ajusta para acomodar o texto. O padro true. Representam as cores do fundo e do primeiro plano. Representa o estilo da borda. O padro Fixed3D. Fixa para true se a ltima operao pode ser desfeita. Se true, o texto selecionado no controle escurecido quando o foco passa para outro controle. Obtm ou fixa a coleo de linhas em uma caixa de texto como uma matriz de strings. Representa o nmero mximo de caracteres que pode ser digitado em um controle. O padro 0, o que significa que o comprimento limitado apenas pela memria disponvel. Obtm ou fixa um valor booleano indicando se o contedo do controle foi modificado. Se true, o controle uma caixa de texto de mltiplas linhas. Obtm a altura preferida em pixels para a fonte atual. Isto lhe permite dimensionar a caixa de modo que ela mostre corretamente o texto. Obtm ou fixa o status somente para leitura do controle. Representa o texto atualmente selecionado. Obtm ou fixa o comprimento da seleo. Obtm ou fixa o comeo da seleo. Obtm ou fixa o texto mostrado no controle. Obtm o comprimento do texto no controle. Se true, as caixas de texto de mltiplas linhas faro automaticamente a marginao quando necessrio. Se false, as linhas prosseguiro horizontalmente at que um caractere de nova linha seja encontrado.

AutoSize BackColor, ForeColor BorderStyle CanUndo HideSelection Lines MaxLength

Modified Multiline PreferredHeight

ReadOnly SelectedText SelectionLength SelectionStart Text TextLength WordWrap

Tabela 16.11: Os mtodos mais comuns da classe TextBox.


MTODO AppendText Clear ClearUndo Copy Cut Paste ScrollToCaret Select SelectAll Undo DESCRIO Anexa texto ao controle. Limpa o texto do controle. Limpa a operao mais recente do buffer undo (desfazer) do controle. Copia o texto selecionado para a rea de transferncia. Corta o texto selecionado da rea de transferncia. Troca a seleo atual pelo contedo da rea de transferncia. Rola o controle de modo que o pino fique visvel. Seleciona o texto no controle. Seleciona todo o texto no controle. Desfaz a ltima operao na rea de transferncia ou de mudana de texto.

283

Jos Wilson Vieira As caixas de texto podem ser de uma nica ou de mltiplas linhas. Isto controlado pela propriedade Multiline. Os controles de texto de mltiplas linhas usaro caracteres de nova linha para quebrlas, enquanto os de uma nica linha mostraro caracteres de nova linha como caracteres de controle (que, usualmente, uma pequena barra vertical). A propriedade Lines suporta uma matriz de strings que usada para representar as linhas em um controle de edio de mltiplas linhas. Os controles de texto mantm um buffer undo. Assim, possvel desfazer mudanas. Como possvel limpar o buffer undo, voc deve checar a propriedade CanUndo antes de tentar desfazer operaes. A classe TextBox adiciona vrias propriedades s que herda de TextBoxBase, como as mostradas na Tabela 16.12. Tabela 16.12: Algumas propriedades da classe TextBox no herdadas de TextBoxBase.
PROPRIEDADE AcceptsReturn DESCRIO Se true, a tecla Enter criar uma nova linha em um texto de mltiplas linhas ao invs de ativar o boto padro para a forma. O padro true. CharacterCasing Determina se o controle modifica a caixa dos caracteres quando so digitados. Os valores podem ser CharacterCasing::Normal (o padro), CharacterCasing::Upper ou CharacterCasing::Lower. PasswordChar Se fixado para um valor diferente de 0, mascara os caracteres com o valor especificado quando eles so digitados. O padro 0. ScrollBars Determina se uma caixa de texto de mltiplas linhas exibe barras de rolagem. O padro sem barras. TextAlign Representa o alinhamento de texto. O padro HorizontalAlignment::Left.

16.4.6.1.

EXERCCIO: ADICIONANDO UM CONTROLE DE EDIO PARA MANIPULAO DE TEXTO

Este exemplo mostra como adicionar um controle de edio a uma forma e como manipular o texto nele contido. PASSO 1: Abra o projeto CppForm. A forma foi criada com o tamanho padro de 300 por 300 pixels. Para criar mais espao para exibir uma caixa de texto, fixe a propriedade Size da forma para 456;400. Assim, teremos bastante espao para mostrar a caixa de texto prxima caixa de grupo. Reposicione adequadamente no canto inferior direito da forma os botes OK e Cancelar. Tambm reposicione a caixa combo combo1 na Location 256;72. PASSO 2: Arraste uma TextBox da caixa de utilitrios para a forma. PASSO 3: Posicione a caixa de texto direita da caixa de grupo e abaixo da caixa combo, e use o editor de propriedades para fixar seu Size para 100;150. Fixe a propriedade Multiline para true de modo que a caixa possa mostrar mais de uma linha de texto. PASSO 4: Vamos ajustar para que o texto na caixa seja preenchido automaticamente quando o usurio clicar nos botes de rdio. Para fazer isto, voc precisa adicionar um manipulador para os botes de rdio. Selecione os trs objetos de boto de rdio e, ento, abra o editor de propriedades. Clique no boto Event para exibir os eventos, e d um clique duplo no espao em branco prximo ao evento Click. Um manipulador de evento ser adicionado e aplicado aos trs botes. NOTA: As funes manipuladoras sempre tm um nome no formato control_event (no caso acima radioButton1_Click) Dependendo da ordem na qual seleciona os botes de rdio, voc 284

Microsoft Visual C++ .NET Passo-a-Passo ver o nome de um dos botes usado no nome do manipulador do evento. No nosso caso, comeamos a seleo pelo primeiro boto de rdio, mas o manipulador ainda se aplicar a todos os trs botes. PASSO 5: Digite o cdigo abaixo para a funo manipuladora:
private: System::Void radioButton1_Click(System::Object * sender, System::EventArgs * e) { if(sender == radioButton1) textBox1->Text = "Voc selecionou a opo Visual Basic."; else if(sender == radioButton2) textBox1->Text = "Voc selecionou a opo C#."; else if(sender == radioButton3) textBox1->Text = "Voc selecionou a opo C++.\r\n\r\n" "Aqui comea a outra linha"; }

O manipulador checa qual dos botes de rdio originou o evento e colocar um texto apropriado na caixa. O terceiro boto de rdio coloca duas linhas de texto na caixa, separadas por uma linha em branco (A seqncia \r\n corresponde a uma quebra de linha). NOTA: Se voc no viu isto antes, colocar duas strings literais prximas uma da outra em linhas adjacentes no constitui um erro. Se o pr-processador ver duas literais prximas uma da outra, automaticamente as concatenar em uma antes de enviar a string para o compilador. Esta funcionalidade propicia um modo organizado de dividir strings longa por linhas. PASSO 6: Construa e execute o programa. Como visto na Figura 16.17, se voc clicar, por exemplo, no boto do C++, ver a mensagem a ele relacionada na caixa de texto.

Figura 16.17: Sada atual do programa CppForm.

16.5.

USANDO MENUS

Os menus so caractersticas bem conhecidas da maioria dos programas GUI e o namespace Forms contm um conjunto completo de classes para construir e trabalhar com menus. 285

Jos Wilson Vieira Os menus nos aplicativos Windows Forms so representados por duas classes principais: MainMenu representa a barra de menu que se localiza no topo de uma forma, enquanto MenuItems representa todos os itens que compem os menus anexados a um MainMenu. Um MainMenu tem uma coleo de MenuItems e, para modelar a natureza hierrquica dos menus, os MenuItems podem ter suas prprias colees de outros MenuItems. No uso, os itens de menu tm alguma similaridade com os botes: ambos so distinguidos por seu texto e ambos disparam um evento Click quando so selecionados. Provavelmente no lhe causar surpresa saber que voc configura manipuladores para itens de menus exatamente do mesmo modo como faz com os botes. PROJETANDO MENUS H vrias diretrizes bem estabelecidas instruindo como os menus devem ser construdos e apresentados aos usurios e, aqui, faremos um resumo de algumas das mais importantes. Primeiro, conveno que o menu mais esquerda na barra de menu se chame File e que o ltimo item no menu File seja o que promove a sada do programa. Usualmente se chama Exit. Tambm, o item mais direita na barra de menu deve ser o menu Help, que conter entradas que permitam ao usurio acessar o sistema de ajuda e provavelmente uma entrada para mostrar a caixa About do programa. Os itens de menu que vo exibir uma caixa de dilogo devem terminar com reticncias (...). Por exemplo, About.... Os itens de menu que no vo ter uma ao imediata, tais como finalizar o aplicativo, no devem conter as reticncias. Para orientar os usurios na navegao pelos menus, podemos atribuir teclas de acesso aos mesmos colocando, na legenda do menu, um & antes das letras escolhidas para atalho. Por exemplo, &File associa a tecla de acesso F com o item de menu File. Os usurios podem navegar atravs dos menus segurando a tecla Alt e pressionando as teclas de acesso. Voc tambm pode atribuir atalhos individuais a itens de menu por exemplo Ctrl + P, para imprimir. Isto deve ser feito para os itens de menu usados mais freqentemente. Os menus drop-down no devem ser muito longos. Mais 10 entradas j constituem provavelmente um excesso. Considere o uso de menus hierrquicos. Os menus no devem deixar o usurio fazer algo que no seja sensato. Por exemplo, se no houver arquivos abertos, um aplicativo no deve permitir que o usurio selecione a opo Print, pois nada h para imprimir. Assim, permitir a seleo do item, s causar confuso. boa prtica escurecer (desabilitar) os itens de menu que no se aplicam ao contexto atual. Alguns aplicativos adicionam e removem itens de menus automaticamente, mas isto tende a confundir porque os usurios podem no relembrar como fazer um dado menu reaparecer!

16.5.1.

EXERCCIO: ADICIONANDO UM MENU SIMPLES FORMA PRINCIPAL DO APLICATIVO

O exemplo abaixo mostrar como adicionar um menu simples forma principal do aplicativo. PASSO 1: Abra o projeto CppForm. PASSO 2: Arraste um MainMenu da caixa de utilitrios para a forma. Voc ver que um cone mostrado em uma rea especial na base da tela de desenho (Figura 16.18). Os controles que no tm presena visual na forma so exibidos em uma rea separada na base da rea de desenho. Apenas os controles que podem ser vistos em tempo de execuo so exibidos na forma. 286

Microsoft Visual C++ .NET Passo-a-Passo

Figura 16.18: rea de desenho mostrando o MainMenu inserido no projeto CppForm. Voc ver que algo parecido com um menu foi adicionado no topo da forma, com um retngulo contornando o texto Type Here. Clique no retngulo e digite &File para adicionar um item de menu (Figura 16.19).

Figura 16.19: Inserindo o item de menu &File. PASSO 3: Uma vez digitada a entrada, voc ver que surgem mais duas caixas Type Here. A da direita para adio de mais itens barra de menu, enquanto a Type Here abaixo para adicionar mais itens ao menu File. Adicione os itens &About... e E&xit ao menu File. PASSO 4: Selecione o item de menu About e abra o editor de propriedades. Mude o nome do menu About para aboutMenuItem. Mude o nome do menu Exit para exitMenuItem. PASSO 5: Voc fornece itens de menu para que o usurio possa selecion-los e executar cdigo. D um clique duplo no item de menu About para adicionar um manipulador, e insira o cdigo abaixo funo gerada:
private: System::Void aboutMenuItem_Click(System::Object * System::EventArgs * e) { MessageBox::Show(S"Item de menu About", S"Menu"); } sender,

287

Jos Wilson Vieira Adicione uma segunda funo manipuladora para o item de menu Exit, mas, desta vez, use-a para sair do aplicativo.
private: System::Void ExitMenuItem_Click(System::Object * System::EventArgs * e) { Application::Exit();//Sada do aplicativo } sender,

PASSO 6: Construa e execute o programa. Voc ver que, agora, a forma tem uma barra de menu no topo, onde voc pode selecionar itens de menu (Figura 16.20).

Figura 16.20: Sada atual do programa CppForm.

16.5.2.

MAIS SOBRE MENUS

Agora que voc dominou os fundamentos da adio de suporte de menus para programas, mencionaremos brevemente outras caractersticas das classes de menu. Primeiro, voc pode criar menus hierrquicos. Cada item de menu tem uma caixa Type Here sua direita, e voc pode usar esta caixa para iniciar um novo menu hierrquico (Figura 16.21).

Figura 16.21: Menus hierrquicos. Para adicionar uma barra de separao a um menu, selecione o item de menu acima do qual voc quer o separador. Clique com o boto direito do mouse no item e selecione Insert Separator no menu de contexto (Figura 16.22). 288

Microsoft Visual C++ .NET Passo-a-Passo

Figura 16.22: Inserindo uma barra de separao entre itens de menu. As propriedades Checked e Enabled podem ser usadas para mostrar uma marca de checagem prxima a um item de menu e escurecer itens de menu que no esto ativos no momento. Simplesmente fixe a propriedade de requisito para true ou false no editor de propriedades, conforme seu desejo (Figura 16.23). Note que o editor de propriedades lhe permite um modo de fixar o conjunto de valores iniciais das propriedades. Voc pode sempre mud-lo posteriormente no cdigo.

Figura 16.23: Usando as propriedades Checked e Enabled para o estado inicial dos itens de menu.

16.5.3.

EXERCCIO: MOSTRANDO UM MENU DE CONTEXTO

A maioria dos aplicativos atuais usa menus de contexto pequenos menus pop-up que abrem quando voc clica com o boto direito do mouse sobre uma janela e que so usados para fornecer itens de menu especficos para a parte da GUI clicada. Voc pode adicionar um menu de contexto a uma forma criando um objeto ContextMenu, adicionando os itens de menu a ele e, ento, lhe atribuindo a propriedade ContextMenu da forma. O exerccio abaixo mostra como adicionar um menu de contexto a uma forma. PASSO 1: Abra o CppForm. PASSO 2: Arreste um ContextMenu da caixa de utilitrios para a forma. Ele aparecer na base da forma, aps o MainMenu. PASSO 3: Adicione alguns itens ao ContextMenu do mesmo modo utilizado no MainMenu. PASSO 4: Atribua o menu de contexto propriedade ContextMenu da forma, usando o editor de propriedades. PASSO 5: Construa e execute o cdigo. Dando um clique com o boto direito do mouse na forma, voc ver algo como a Figura 16.24.

Figura 16.24: Programa CppForm mostrando um menu de contexto. 289

Jos Wilson Vieira Voc usa manipuladores com os itens nos menus de contexto exatamente como faz nos menus principais.

16.6.

RPIDAS REFERNCIAS SOBRE O CAPTULO 16

A Tabela 16.13 mostra um resumo deste captulo. Tabela 16.13: Resumo do captulo 16.
PARA Criar uma forma Adicionar controles a uma forma Manipular eventos FAA ISTO Derive uma classe de System::Windows::Forms::Form. Arraste os controles da caixa de utilitrios e solte-os na forma. Use o editor de propriedades para adicionar uma funo manipuladora de evento. Adicionar um menu a Arraste um item MainMenu na forma e adicione itens de menu uma forma usando o editor de menu grfico. Adicionar um menu de Arraste um ContextMenu para a forma e proceda exatamente com contexto fez para adicionar um MainMenu.

290

Microsoft Visual C++ .NET Passo-a-Passo 17. CAPTULO 17: CAIXAS DE DILOGO E CONTROLES

Neste captulo, voc aprender como Criar e usar caixas de dilogo; Usar as caixas de dilogo comuns do Microsoft Windows; Tirar mais proveitos dos controles fornecidos pelo Microsoft .NET Framework.

O captulo 16 lhe introduziu no mundo das formas do Windows e lhe mostrou como usar classes no namespace System::Windows::Forms para construir aplicativos GUI. Este captulo observa mais caractersticas das formas do Windows. Comea lhe mostrando como criar e usar caixas de dilogo em aplicativos GUI e, ento, lhe conduz a tirar mais proveitos dos controles fornecidos pelo namespace System::Windows::Forms.

17.1.

USANDO CAIXAS DE DILOGO

Voc j se deparou com caixas de dilogo por exemplo, a caixa de dilogo About (Figura 17.1) que quase todo aplicativo possui ou a caixa de dilogo Insert Table no Microsoft Word.

Figura 17.1: A caixa de dilogo About. No .NET Framework, a diferena entre caixas de dilogo e outros tipos de janelas bastante nebulosa e uma caixa de dilogo apenas uma forma a mais. Porm, isto nem sempre foi o caso em programao com o Microsoft Windows. Antes do .NET, as caixas de dilogo tinham vrias propriedades especiais: Eram janelas otimizadas para funcionar como controles. Usualmente tinham uma borda fixa de modo que voc no podia redimension-las. Podiam ser descritas em dados usando um modelo de caixa de dilogo e podiam automaticamente ser criadas e preenchidas com controles em tempo de execuo. Estes dados eram anexados ao executvel como um recurso binrio do Windows. Podiam ser criadas como caixas de dilogo modais. Neste caso, o aplicativo era bloqueado at a caixa ser despachada. Tambm podiam ser criadas como caixas de dilogo modeless. Neste caso, a janela da caixa de dilogo podia flutuar na tela e voc podia us-la e/ou sua janela-me.

As formas do Windows j tm algumas destas propriedades. Nelas, voc pode usar controles e pode fixar a borda de modo que ela no possa ser redimensionada. Porm, voc no pode usar modelos de caixa de dilogo porque o .NET no usa recursos. Voc tem que construir caixas de dilogo em cdigo, exatamente como qualquer outra forma. Para lhe auxiliar a desenhar formas que funcionem como caixas de dilogo modais, a classe Form tem uma funo chamada ShowDialog que mostra 291

Jos Wilson Vieira uma modalidade de forma. Assim, voc tem que fechar a forma antes de usar o resto do aplicativo, i.e., sua forma vira uma caixa de dilogo modal.

17.1.1.

EXERCCIO: CRIANDO UM CAIXA DE DILOGO About

O exerccio a seguir ainda usa o projeto CppForm do captulo 16 e lhe mostra com criar uma caixa de dilogo About para seu aplicativo. PROJETANDO UMA CAIXA DE DILOGO Como no caso dos menus, voc deve ficar atento a algumas diretrizes quando projetar caixas de dilogo. Se a caixa de dilogo existe simplesmente para exibir alguns dados tais como um aviso para o usurio, deve conter um boto que ele possa usar para fechar a caixa de dilogo. Geralmente a legenda deste boto OK, Done, ou outra palavra indicando que o usurio est finalizando a caixa de dilogo. Se o usurio entrar com dados na caixa de dilogo, deve haver dois botes: um, que aceitar as mudanas e fechar a caixa de dilogo, convencionalmente rotulado OK; o outro boto, que descartar as mudanas antes de fechar a caixa de dilogo, normalmente rotulado Cancel. Estes botes usualmente so colocados em uma linha horizontal na base da caixa de dilogo. De um modo geral, no faa caixas de dilogo muito confusas. melhor ter vrias caixas onde cada uma executa bem uma funo do que uma nica caixa de dilogo gigante que uma baguna de controles. Voc tambm pode considerar caixas de dilogo com abas que ajudam a reduzir a desordem. PASSO 1: Abra o projeto CppForm. PASSO 2: Clique com o boto direito do mouse no nome do projeto, CppForm, no Solution Explorer, e escolha o item Add Add New Item no menu de contexto. Selecione Windows Forms (.NET)em Templates e digite AboutBox no campo Name (Figura 17.2). A caixa de dilogo simplesmente outra forma e a principal diferena da forma do aplicativo est no modo como voc a exibe, como veremos em breve.

Figura 17.2: Caixa de dilogo Add New Item. 292

Microsoft Visual C++ .NET Passo-a-Passo PASSO 3: Quando a janela de desenho para a caixa de dilogo aparecer, fixe as suas propriedades de modo que a legenda seja About CppForm, o estilo das bordas seja Fixed3D e o tamanho seja 300;150. O estilo fixado para Fixed3D porque usualmente as caixas de dilogo no so redimensionveis. PASSO 4: A caixa de dilogo vai mostrar dois rtulos e um boto OK. Arraste um Label para a forma, posicionando-o em 20;30 e digitando nele o texto O Aplicativo CppForm. Altere o tamanho do rtulo de modo que todo o texto aparea em uma linha. PASSO 5: Arraste um segundo rtulo para a forma, posicionando-o abaixo do primeiro e digitando-lhe o texto Julian Templeman, 2001. PASSO 6: Adicione um boto forma, com nome OKButton e texto OK. Fixe seu tamanho para 40;25. Posicione-o no canto inferior direito da forma. PASSO 7: Adicione um manipulador de eventos para o boto. Nas propriedades do boto, faa o editor de propriedades mostrar os eventos pressionando o boto Event no topo do editor. Localize o evento Click e d um clique duplo no espao em branco direita da palavra Click. Complete a funo manipuladora com o cdigo abaixo:
private: System::Void OKButton_Click(System::Object * e) { Close(); //Fecha a forma } sender, System::EventArgs *

A funo manipuladora simplesmente fecha a forma quando o boto clicado. PASSO 8: Vamos organizar para que a caixa de dilogo seja mostrada quando o item de menu About for selecionado. Como precisamos acessar a classe AboutBox da forma principal, abra o arquivo de cabealho Form1.h e insira #include AboutBox.h no topo do arquivo. Localize a funo System::Void aboutMenuItem_Click e troque seu cdigo antigo pelo dado abaixo:
private: System::Void aboutMenuItem_Click(System::Object * System::EventArgs * e) { AboutBox *box = new AboutBox(); box->ShowDialog(); } sender,

Quando o item de menu About for escolhido, a funo cria um objeto AboutBox e, ento, chama sua funo ShowDialog, que tem o efeito de mostrar a forma como uma caixa de dilogo modal, de modo que ela tem que ser fechada antes que voc possa continuar trabalhando com o aplicativo. PASSO 9: Construa e execute o aplicativo. Quando voc selecionar o item About no menu File, ver a caixa de dilogo mostrado na Figura 17.3.

Figura 17.3: Caixa de dilogo About.

293

Jos Wilson Vieira 17.1.2. A PROPRIEDADE DialogResult

comum as caixas de dilogo conterem um conjunto de botes tais como Yes, No e Cancel ou Abort, Retry e Ignore que o usurio pode usar para passar informaes de volta para o aplicativo. A propriedade DialogResult usada para passar um valor de volta para o cdigo de chamada indicando qual o boto que foi clicado para fechar a caixa de dilogo. A Tabela 17.1 mostra os valores possveis que a propriedade pode tomar da enumerao DialogResult. Tabela 17.1: Valores da enumerao DialogResult.
MEMBRO Abort Ignore No None OK Retry Yes DESCRIO Representa o valor de retorno Cancel e usualmente fixado por um rotulado por Cancel. Representa o valor de retorno Ignore e usualmente fixado pelo um rotulado por Ignore. Representa o valor de retorno No e usualmente fixado pelo um rotulado por No. Nada retornado da caixa de dilogo. Assim, a caixa de dilogo continua executando. Representa o valor de retorno OK e usualmente fixado pelo um rotulado por OK. Representa o valor de retorno Retry e usualmente fixado pelo um rotulado por Retry. Representa o valor de retorno Yes e usualmente fixado pelo um rotulado por Yes. boto boto boto modal boto boto boto

Voc pode estar ligeiramente confuso com a descrio da entrada None. Por isso precisamos explicar como a DialogResult funciona. Um usurio tipicamente fechar uma caixa de dilogo clicando em um boto e voc j viu como o manipulador para o boto OK usado para fechar a caixa About no exerccio anterior. Se quiser retornar um valor de uma caixa de dilogo modal, atribua um dos valores da Tabela 17.1 propriedade DialogResult da forma. Porm, fazer isto significa atribuir o valor e imediatamente fechar a forma. Assim, voc pode trocar o manipulador do boto OK no exerccio anterior pelo seguinte cdigo:
private: System::Void OKButton_Click(System::Object * sender, System::EventArgs * e) { //Close(); //Fecha a forma DialogResult = DialogResult::OK;//Envia OK de volta e fecha a forma }

O chamador pode checar o valor retornado de ShowDialog para descobrir qual a caixa de dilogo retornada. Na verdade, pode ser at mais fcil fechar a forma e enviar um valor de volta para o chamador. Os controles de boto tm uma propriedade DialogResult. Se esta propriedade for fixada para um valor diferente de None, clicar no boto implicar em fechar a forma-me e enviar o resultado apropriado de volta. Uma vez fixada a DialogResult para o boto, voc pode remover o manipulador Click para este. Selecione o boto, abra o editor de propriedades, exiba os eventos e delete a entrada OKButton_Click. Note que isto no deleta a prpria funo manipuladora, mas simplesmente a desvincula do boto. Voc pode organizar o cdigo deletando manualmente a funo OKButton_Click de AboutBox.h.

294

Microsoft Visual C++ .NET Passo-a-Passo 17.1.3. EXERCCIO: USANDO DADOS COM CAIXAS DE DILOGO comum usar caixas de dilogo para obter informaes do usurio. Voc freqentemente tem que carregar dados na caixa de dilogo antes de exibi-la e, ento, extrai dados que o usurio digitou, usualmente quando este clica no boto OK. O exerccio abaixo um exemplo de uma caixa de dilogo que mostra informaes, permite que o usurio as modifique e, ento, l as mudanas quando fechada. PASSO 1: Abra o CppForm. Clique com o boto direito do mouse no nome do projeto no Solution Explorer e escolha Add Add New Item no menu de contexto. Em Templates, selecione Windows Forms (.NET) e adicione uma nova forma chamada MyDialog. PASSO 2: Abra a janela de cdigo para a forma principal, Form1.h, e adicione a linha #include MyDialog.h de modo que a nova forma possa ser referenciada da forma principal. PASSO 3: A caixa de dilogo vai conter controles para recolher os detalhes pessoais do usurio, que neste caso sero o nome, o nmero do telefone e o departamento. Cada um destes itens precisar de um controle e um rtulo, e voc tambm precisar dos botes OK e Cancelar para fechar a caixa de dilogo. Adicione os oito controles para a forma MyDialog, organizando-os como na Figura 17.4.

Figura 17.4: Caixa de dilogo para o programa CppForm. Os campos para o nome e o telefone so representados por controles TextBox chamados nameBox e phoneBox, e o departamento representado por uma ComboBox chamada deptCombo. Remova o texto padro da propriedade Text dos controles TextBox e ComboBox de modo que eles fiquem em branco. PASSO 4: Use o editor de propriedades para configurar a forma e os controles. Fixe o tamanho da forma para 280;200 com um estilo de borda Fixed3D e legenda Detalhes Pessoais. Fixe a propriedade TextAlign dos rtulos para MiddleRight. PASSO 5: A caixa combo suportar uma lista de departamentos e voc pode adicionar estas strings usando o editor de propriedades. Selecione a ComboBox, encontre a propriedade Items e clique no boto com as reticncias direita da propriedade. O editor de coleo de strings, que voc pode usar para especificar as strings a serem exibidas na caixa combo, se abrir. Adicione as strings Diretoria, Administrao, Recursos Humanos, Servios de Limpeza e Almoxarifado, uma em cada linha, e feche o editor, aps. PASSO 6: Aos botes OK e Cancelar, d os nomes OKButton e CancelButton, respectivamente, e fixe suas propriedades DialogResult para DialogResult::OK e DialogResult::Cancel. 295

Jos Wilson Vieira PASSO 7: Agora fixe as propriedades AcceptButton e CancelButton da forma. Se fixada, a AcceptButton suporta uma referncia para o boto padro na forma. Fixe a propriedade AcceptButton para OKButton. Assim, o ato de pressionar a tecla Enter produz o mesmo efeito que clicar no boto padro e, como voc fixou a DialogResult para OKButton para OK, pressionar Enter implica em fechar a caixa de dilogo e enviar DialogResult::OK de volta para o chamador. A propriedade CancelButton da forma suporta uma referncia para o boto que mapeia a tecla Esc, assim dando ao usurio um modo de fechar a caixa de dilogo pelo teclado. Note que a propriedade CancelButton parece no funcionar corretamente em verses beta anteriores ao Visual Studio .NET 7.1, a verso que foi usada neste texto. PASSO 8: Para permitir que o cdigo de chamada obtenha e fixe os valores nos controles, adicione as propriedades abaixo classe MyDialog:
//Propriedades para acessar dados dos controles public: __property void set_Name(String *n)//+++++++++++++++++++++++++++++ { nameBox->Text = n; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __property String* get_Name()//+++++++++++++++++++++++++++++++++++ { return nameBox->Text; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __property void set_Phone(String *p)//++++++++++++++++++++++++++++ { phoneBox->Text = p; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __property String* get_Phone()//++++++++++++++++++++++++++++++++++ { return phoneBox->Text; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __property void set_Dept(int d)//+++++++++++++++++++++++++++++++++ { deptCombo->SelectedIndex = d; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ __property int get_Dept()//+++++++++++++++++++++++++++++++++++++++ { return deptCombo->SelectedIndex; }//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

H um par de mtodos get e set para cada campo na caixa de dilogo que permitiro ao usurio da classe MyDialog manipular os dados sem permitir acesso completo aos objetos TextBox e ComboBox. PASSO 9: Adicione o cdigo para mostrar a caixa de dilogo. Exiba a forma principal, adicione um novo item de menu ao menu File e lhe d como legenda Mostrar Dilogo.... Lembre-se que as reticncias em um item de menu indicam que o item vai mostrar uma caixa de dilogo. No editor do menu, arraste o novo item de modo que ele fique entre os itens About e Exit. D-lhe o nome dialogMenuItem. PASSO 10: D um clique duplo no novo item de menu para criar um manipulador para ele e edite o cdigo abaixo para exibir a caixa de dilogo.
private: System::Void dialogMenuItem_Click(System::Object * sender, System::EventArgs * e) { MyDialog *box = new MyDialog();//Cria a caixa de dilogo

296

Microsoft Visual C++ .NET Passo-a-Passo


//Preenche a caixa com dados iniciais box->Name = S"Jos Wilson"; box->Phone = S"3453-0705"; box->Dept = 1; //Mostra a caixa de dilogo if(box->ShowDialog() == DialogResult::OK) { //Se o dilogo retornar OK, mostrar o nome MessageBox::Show(box->Name, S"O nome ..."); } }

Depois que o objeto da caixa de dilogo criado, voc usa as propriedades para colocar dados iniciais nos controles. Ento, a caixa de dilogo mostrada usando ShowDialog e, quando ela retorna, voc pode checar o valor de retorno e ver se foi DialogResult::OK ou DialogResult::Cancel. Se foi OK, voc pode usar a propriedade Name para extrair os dados finais do objeto da caixa de dilogo. PASSO 11: Construa e execute o cdigo, e, quando selecionar o item de menu MyDialog, voc dever ver a caixa de dilogo mostrada na Figura 17.4.

17.1.4.

FIXANDO A ORDEM DE TABULAO

Voc pode usar a tecla Tab para se deslocar de controle para controle na forma. Por padro, a ordem na qual voc se desloca entre controles segue a ordem em que eles foram criados, mas voc pode impor sua prpria ordem atribuindo um valor baseado em 0 para a propriedade TabIndex de qualquer controle. Dois controles terem o mesmo valor TabIndex no constitui um erro, mas a ordem na qual eles so selecionados depender da ordem na qual eles so mostrados na tela.

17.2.

USANDO AS CAIXAS DE DILOGO COMUNS

H algumas caixas de dilogo que muitos programas precisam usar. Os exemplos incluem as caixas de dilogo File Open e File Save, a caixa para escolha de fontes e de cores. O Windows sempre forneceu um conjunto destas caixas de dilogo comuns e elas esto disponveis para voc usar com o Windows Forms. H vrias vantagens em usar as caixas de dilogo comuns: Os programadores no precisam continuar a reinventar as mesmas caixas de dilogo; Voc pode obter caixas de dilogo completamente funcionais e algumas delas so difceis de escrever; Os usurios vem um conjunto familiar de caixas de dilogo. Assim eles no s tm que entender com a caixa de dilogo File Open funciona no seu programa, por exemplo; As caixas de dilogo so exibidas para servir ao sistema operacional. Mostre uma caixa de dilogo File Open em um computador executando o Windows 2000 e voc ver a verso para o Windows 2000. Faa a mesma coisa em um sistema Windows XP e a verso Windows XP ser mostrada.

O Windows Forms lhe abastece com as caixas de dilogo comuns listadas na Tabela 17.2. 297

Jos Wilson Vieira Tabela 17.2: Caixas de dilogo comuns disponveis no Windows Forms.
CLASSE ColorDialog PROPSITO Mostra uma caixa de dilogo que permite ao usurio escolher uma cor. FileDialog A classe-base abstrata para as classes OpenFileDialog e SaveFileDialog. FolderBrowserDialog Mostra uma caixa de dilogo que permite ao usurio escolher uma pasta. Esta classe foi adicionada na verso 1.1 do .NET Framework. FontDialog Mostra uma caixa de dilogo que permite ao usurio escolher uma fonte. OpenFileDialog Mostra uma caixa de dilogo File Open padro. PageSetupDialog Mostra uma caixa de dilogo que permite ao usurio manipular configuraes de pginas, como, por exemplo, margens e orientao da pgina. PrintDialog Mostra uma caixa de dilogo que permite ao usurio selecionar uma impressora e a poro do documento a ser impressa. SaveFileDialog Mostra uma caixa de dilogo File Save padro.

Todas estas caixas de dilogo so herdeiras de CommonDialog, que fornece membros protected que so usados internamente pelas classes herdeiras. Voc no usa tais membros no seu cdigo. Estas classes so usadas exatamente do mesmo modo que qualquer outra caixa de dilogo: voc cria um objeto da caixa de dilogo, fixa suas propriedades e, ento, o exibe chamando ShowDialog.

17.2.1.

EXERCCIO: ESCOLHENDO UMA FONTE E FIXANDO-A EM UM CONTROLE DE RTULO

O exerccio a seguir mostra com usar a FontDialog para escolher uma fonte e, ento, us-la para fixar a fonte do controle de rtulo na forma. PASSO 1: Abra o projeto CppForm. PASSO 2: Adicione um membro Font classe Form1.
System::Drawing::Font *labelFont;

Voc tem que d o nome completo do qualificador da classe Font ou, ento, teramos conflito com a propriedade Font da forma. PASSO 3: Adicione um novo item ao menu File com o texto Escolher Fonte... e nome fontDlgMenuItem. Coloque-o acima do item de menu Exit. PASSO 4: O objeto Font vai ser usado para fixar a fonte do rtulo na forma principal. Para fazer isto, voc precisa criar um objeto Font e associ-lo com a propriedade Font do rtulo. Coloque o cdigo abaixo no construtor da classe Form1, assegurando-se de que ele esteja aps a chamada funo InitializeComponent:
//Fixa a fonte labelFont = new System::Drawing::Font(S"Verdana", 16, FontStyle::Italic); label1->Font = labelFont; label1->ForeColor = Color::Black;

Note como o ponteiro para a Font armazenado longe do membro labelFont. PASSO 5: Edite a funo manipuladora de modo que ela crie e mostre uma FontDialog:
private: System::Void fontDlgMenuItem_Click(System::Object * System::EventArgs * e) sender,

298

Microsoft Visual C++ .NET Passo-a-Passo


{ FontDialog *fd = new FontDialog(); fd->Font = labelFont; if(fd->ShowDialog() == DialogResult::OK) { MessageBox::Show(fd->Font->Name, S"O nome ..."); labelFont = fd->Font; label1->Font = labelFont; } }

O cdigo cria um objeto FontDialog e, ento, o inicializa com a atual fonte representada por labelFont. ShowDialog mostra a caixa de dilogo e, se ela retornar um status DialogResult::OK, o cdigo fixa a fonte do rtulo para refletir a escolha do usurio. O controle Label imediatamente se atualizar para usar a nova fonte. PASSO 6: Construa e execute o cdigo e voc ver a caixa de dilogo comum Font mostrada na Figura 17.5.

Figura 17.5: Caixa de dilogo comum Font.

17.3.

MAIS SOBRE CONTROLES

O namespace System::Windows::Forms lhe abastece com um grande nmero de controles que voc pode usar para construir GUIs de programas. A Tabela 17.3 lista os controles principais neste namespace. H muitos controles para que se possa falar, detalhadamente, sobre cada um. Assim, mostraremos como usar alguns deles.

299

Jos Wilson Vieira Tabela 17.3: Principais controles disponveis no namespace System::Windows::Forms.
CONTROLE Button CheckBox CheckedListBox Clipboard ComboBox Cursor DataGrid DateTimePicker DomainUpDown ErrorProvider Help ImageList DESCRIO Um boto de controle Windows. Um controle d caixa de checagem Windows. Um controle ListBox com uma CheckBox em cada item. No estritamente um controle, mas uma classe que lhe permite interagir com a rea de transferncia do Windows. Uma caixa combo Windows. Representa a imagem usada para o cursor do mouse. Um controle de grade que funciona com os bancos de dados do ADO.NET. Um controle que permite que o usurio selecione uma data. Um controle up-down que mostra strings. Funciona com outros controles para mostrar que um controle tem erros a ele associados. Encapsula a mquina de ajuda HTML. Um controle que gerencia uma lista de imagens. Freqentemente usado com barras de utilitrios e outros controles que precisem de mltiplas imagens. Um controle de rtulos Windows. Um rtulo que pode mostrar hiper-vnculos. Um controle de caixa de lista Windows. Mostra uma lista de itens em um dos quatro modos: cones pequenos, cones grandes, lista e detalhes. Por exemplo, o painel da direita do Windows Explorer. A classe-base para todos os tipos de menus. Representa uma caixa de mensagem Windows. Permite que o usurio selecione uma data usando uma tela de visualizao. Um controle up-down que mostra nmeros. Um controle que pode conter outros controles. Um controle que mostra imagens de arquivos de imagem. Um controle que mostra o progresso de uma operao. Um controle que lhe permite navegar atravs das propriedades de um objeto. Um boto de rdio Windows. Um controle TextBox que suporta formatao e outras caractersticas de processamento de texto. Um controle que encapsula uma barra de rolagem padro Windows. Se voc quiser um controle para atuar como slides, use um TrackBar ao invs de um ScrollBar. Um controle que redimensiona controles ancorados em tempo de execuo. Um controle que representa a barra de status na base de uma janela. e Controles que lhe permitem criar e gerenciar caixas de dilogo com abas. Um controle de caixa de texto Windows. Um controle que implementa um cronmetro. Um controle que implementa uma barra de utilitrios padro. Um controle que implementa um slide. Um controle para mostrar uma coleo hierrquica de itens como uma rvore. Por exemplo, o painel esquerdo do Windows Explorer.

Label LinkLabel ListBox ListView

Menu MessageBox MonthCalendar NumericUpDown Panel PictureBox ProgressBar PropertyGrid RadioButton RichTextBox ScrollBar

Splitter StatusBar TabControl TabPage TextBox Timer ToolBar TrackBar TreeView

17.3.1.

USANDO O CONTROLE TreeView

Os controles de vista de rvore, que mostram uma hierarquia de itens como uma rvore, familiar a quem j usou o Windows Explorer. A classe System::Windows::Forms::TreeView agasalha 300

Microsoft Visual C++ .NET Passo-a-Passo o controle de vista de rvore Windows e simplifica a criao e manipulao de rvores de itens. Vamos dar uma observada nos mtodos e propriedades fornecidas pela classe e, ento, um exerccio mostrar como criar e usar uma TreeView. Uma vista de rvore formada por ns, representados pelos objetos TreeNode, que existem em uma relao me/filha. Os ns-razes no tm me e possvel que uma TreeView contenha mais de um n-raiz. A Figura 17.6 mostra uma TreeView tpica.

Figura 17.6: TreeView do Windows Explorer. No exemplo da Figura 17.6, o Desktop n-raiz. Cada n pode ter uma legenda e um cone a ele associado, e tambm poder ter linhas conectando-o a ns-irmos e ao n-me. Se um n tem filhos, ele pode exibir um boto sua esquerda com um sinal +. Clicando no sinal +, ou dando um clique duplo sobre a legenda ou o cone do n, a rvore se expandir para mostrar os ns-filhos. Ento, o sinal + muda para . Os ns podem ter um par de imagens associadas a eles, que so usadas para mostrar ns expandido e recolhido. A criao de uma TreeView consiste em fixar as propriedades de controle e, ento, criar os objetos TreeNode, vinculando-os forma da rvore. Tanto a classe TreeView quanto a TreeNode tm uma propriedade Nodes que suporta uma coleo de ns-filhos. Os controles TreeView so complexos. Assim, a classe tem diversas propriedades, mtodos e eventos. Os comumente mais utilizados so apresentados nas Tabelas 17.4, 17.5 e 17.6. 301

Jos Wilson Vieira Tabela 17.4: Propriedades mais comuns da classe TreeView.
PROPRIEDADE BorderStyle CheckBoxes DESCRIO Representa o estilo da borda do controle. O padro Fixed3D. Determina se as caixas de checagem so mostradas prximas aos ns. O padro false. FullRowSelect Determina se a seleo de um n reala toda a extenso do controle ou apenas a legenda. O padro false. HideSelection Determina se o n selecionado permanecer realado quando o controle perder o foco. O padro true. HotTracking Determina se as legendas assumem um aspecto de um hiper-vnculo quando o ponteiro do mouse estiver sobre elas. O padro false. ImageIndex Representa o ndice baseado em zero da imagem na ImageList associada que usada como a imagem padro para os ns. ImageList Representa o controle ImageList que suporta a lista de imagens usada pela TreeView. Indent Representa a distncia de recuo (em pixels) para cada legenda dos ns-filhos. LabelEdit Determina se o usurio pode editar as legendas nos ns. O padro false. Nodes Representa a coleo dos ns no controle. Scrollable Determina se o controle mostra barras de rolagem quando for necessrio. O padro true. SelectedImageIndex Representa o ndice baseado em zero da imagem na ImageList associada que usada como a imagem para os ns selecionados. SelectedNode Obtm ou fixa o n atualmente selecionado. ShowLines Determina se so desenhadas linhas entre os ns na rvore. ShowPlusMinus Determina se os sinais + e so mostrados juntos aos ns que tm ns-filhos. ShowRootLines Determina se so desenhadas linhas entre ns na raiz da rvore. Sorted Determina se os ns na rvore so ordenados alfabeticamente. TopNode Retorna uma referncia para o primeiro ns completamente visvel. VisibleCount Obtm o nmero de ns que esto completamente visveis na rvore.

Tabela 17.5: Mtodos mais comuns da classe TreeView.


MTODO BeginUpdate DESCRIO Desabilita o redesenho da TreeView. Use este mtodo se voc for adicionar vrios itens porque ele evita que o controle se ajuste aps a adio de cada item. Desmancha todos os ns na rvore. Habilita o redesenho da TreeView. Expande todos os ns na rvore. Obtm o n em um conjunto particular de coordenadas. Obtm o nmero de ns anexados ao controle.

CollapseAll EndUpdate ExpandAll GetNodeAt GetNodeCount

302

Microsoft Visual C++ .NET Passo-a-Passo Tabela 17.6: Eventos mais comuns da classe TreeView.
EVENTO AfterCheck AfterCollpse AfterExpand AfterLabelEdit AfterSelect BeforeCheck BeforeCollapse BeforeExpand BeforeLabelEdit BeforeSelect ItemDrag DESCRIO Ocorre depois que uma caixa de checagem TreeNode ser marcada. Ocorre aps o desmanche de um TreeNode. Ocorre aps a expanso de um TreeNode. Ocorre aps a edio de uma legenda num TreeNode. Ocorre aps a seleo de um TreeNode. Ocorre antes que uma caixa de checagem TreeNode ser marcada. Ocorre antes do desmanche de um TreeNode. Ocorre antes da expanso de um TreeNode. Ocorre antes da edio de uma linguagem num TreeNode. Ocorre antes da seleo de um TreeNode. Ocorre quando um item arrastado no controle TreeView.

17.3.1.1.

EXERCCIO: CRIANDO E PREENCHENDO UMA TreeView

O exerccio a seguir mostra como criar e preencher um controle TreeView. Temos vrios controles a discutir no restante deste captulo. Assim, usaremos os controles para construir um programa similar ao Windows Explorer como um browser de arquivos. No um programa muito sofisticado, mas servir para lhe mostrar os fundamentos da manipulao destes controles em um aplicativo real. PASSO 1: Crie um novo projeto Visual C++ Windows Forms Application (.NET) e chamado CppControls. PASSO 2: Arraste uma TreeView da caixa de utilitrios para a forma. Fixe sua propriedade Dock para Left selecionando a propriedade no editor e escolhendo a barra mais esquerda no mostrador grfico que aparece. A TreeView ficar ancorada no lado esquerdo da forma e se ajustar para ocupar toda a altura da mesma. Fixe a propriedade Text da forma para Navegador. PASSO 3: Uma TreeView exibe uma coleo de objetos TreeNode, que voc pode fixar usando o editor de propriedades. Selecione a TreeView, exiba o editor de propriedades e clique no boto das reticncias direita da propriedade Nodes para mostrar o editor de TreeNode (Figura 17.7).

Figura 17.7: Editor de TreeNode do controle TreeView. 303

Jos Wilson Vieira Adicione um n-raiz para a TreeView clicando no boto Add Root, e digite como legenda Raiz. Quando fechar o editor, voc dever ver o n-raiz na TreeView. PASSO 4: Construa e execute o programa. A Figura 17.8 mostra a forma atual. A rvore ancorada no lado esquerdo da forma e tem um membro, que est realado.

Figura 17.8: Forma atual do programa CppControls.

17.3.1.2.

EXERCCIO: ADICIONANDO PASTAS AO NAVEGADOR

Agora que a TreeView foi fixada, vamos faz-la exibir algumas informaes teis. A adio de pastas funcionais ao navegador lhe mostrar como adicionar ns ao controle e como responder aos eventos da TreeView. PASSO 1: Abra o projeto CppControls. Agora que voc conhece a TreeView atualmente exibida, use o editor de TreeNode para remover a amostra de n Raiz. PASSO 2: O primeiro passo para exibir informaes de pasta adicionar uma lista de letras de drive para o controle, que voc precisar fazer no cdigo fonte da forma. Voc usar classes do namespace System::IO. Assim, adicione uma diretiva using lista no topo do arquivo de cabealho Form1.h:
using namespace System::IO;

PASSO 3: Adicione o cdigo abaixo no final do construtor Form1, aps a chamada funo InitializeComponent:
//Obtm os drives lgicos String *drives[] = Directory::GetLogicalDrives(); for(int i = 0; i < drives->Count; i++) { String *name = dynamic_cast<String*>(drives->get_Item(i)); TreeNode *tn = new TreeNode(name); treeView1->Nodes->Add(tn); tn->Nodes->Add(new TreeNode("<filho>")); }

A classe Directory uma das classes de acesso ao sistema de arquivos fornecidas em System::IO e ela tem diversos mtodos static que lhe ajudam a trabalhar com pastas (diretrios). O primeiro membro de Directory que voc usar GetLogicalDrives, que retorna um vetor de strings contendo os nomes de todos os drives lgicos. Note que a funo retorna 304

Microsoft Visual C++ .NET Passo-a-Passo um vetor gerenciado .NET ao invs de um vetor nativo C++. Assim, voc usa a propriedade Count para saber quantos elementos contm o vetor e o mtodo get_Item para acessar um elemento. Como get_Item retorna um Object* genrico, dynamic_cast usado para converter o ponteiro para um String*. NOTA: Na maioria dos casos, voc deve checar o ponteiro retornado por dynamic_cast em tempo de execuo, quando a converso falhar. Aqui, sabemos que GetLogicalDrives sempre retorna um vetor de strings. Assim, no h previso de falha. Construmos um novo TreeNode para cada item no vetor, inicializado com o nome do drive e adicionado TreeView. Ao adicionar cada objeto TreeNode diretamente TreeView, criamos mltiplos ns-razes, o que bastante razovel neste aplicativo. A linha final do cdigo adiciona um elemento-filho com legenda <filho> a cada um dos novos ns. Voc j viu controles de rvore mostrando os sinais mais e menos, usados para expandir e contrair a rvore. Estes sinais no so mostrados a menos que o TreeNode tenha ns-filhos. O problema que no queremos criar as entradas-filhas at que o usurio clique em um sinal mais, mas o n-me no obtm um sinal mais a no ser que j exista ns-filhos. Uma soluo rpida e simples adicionar um n-filho bobo. Se codificar corretamente, o usurio nunca ver este n e o n-me ter um sinal mais. Quando o usurio expandir o n, o n bobo deletado e substitudo pelos ns-filhos reais. PASSO 4: Se voc construir e executar o cdigo a esta altura, dever ver a TreeView contendo uma lista de drives lgicos, cada um dos quais com um sinal mais (Figura 17.9).

Figura 17.9: Sada atual do programa CppControls. PASSO 5: Obviamente, o prximo passo adicionar outra legenda de detalhe quando o usurio clicar em um dos sinais mais. Observando a Tabela 17.6 dos eventos, voc concordar que o melhor evento para manipular o que precisamos BeforeExpand, que disparado antes que um TreeNode seja expandido. Use o editor de propriedades para adicionar um manipulador de eventos para o BeforeExpand. Selecione a TreeView e clique no boto Event. Depois, localize o evento BeforeExpand e d um clique duplo no campo em brando direita do nome do evento. O esqueleto da funo manipuladora deste evento adicionada classe Form1. Adicione-lhe o cdigo abaixo:
private: System::Void treeView1_BeforeExpand(System::Object * System::Windows::Forms::TreeViewCancelEventArgs * e) { //Primeiro destrua o n bobo, supondo que exista um sender,

305

Jos Wilson Vieira


if(e->Node->Nodes->Count != 0) e->Node->Nodes->RemoveAt(0); }

Como usual, o nome da funo reflete o controle e o evento, e a funo manipuladora recebe dois argumentos. Ao contrrio dos manipuladores de evento anteriores para botes e menus que voc viu, este aqui recebe um objeto TreeViewCancelEventArgs, que contm informaes extras sobre o evento (especialmente, sobre o TreeNode que vai ser expandido). Antes de adicionar quaisquer ns-filhos rvore, voc precisa remover o n bobo que foi adicionado para forar a exibio do sinal mais. A propriedade Node do objeto TreeViewCancelEventArgs suporta um ponteiro para o n ao qual o evento se refere. Esta coleo de ns-filhos acessada via a propriedade Nodes e um elemento pode ser removido passando um ndice baseado em zero para o mtodo RemoveAt. Porm, antes de fazer isto, voc precisa checar se h um n a ser removido comparando o nmero de ns-filhos com zero. PASSO 6: Adicione o cdigo abaixo para mostrar os contedos de uma pasta:
private: System::Void treeView1_BeforeExpand(System::Object * System::Windows::Forms::TreeViewCancelEventArgs * e) { //Primeiro destrua o n bobo, supondo que exista um if(e->Node->Nodes->Count != 0) e->Node->Nodes->RemoveAt(0); sender,

//Obtm uma lista das sub-pastas String *dirs[]; try { dirs = Directory::GetDirectories(e->Node->FullPath); } catch(System::Exception *pe) { MessageBox::Show(pe->Message, "Erro"); return; } //Adiciona um n-filho a cada sub-pasta for(int i = 0; i < dirs->Count; i++) { String *dirName = dynamic_cast<String*>(dirs->get_Item(i)); TreeNode *tn = new TreeNode(Path::GetFileName(dirName)); e->Node->Nodes->Add(tn); //Adiciona um n-filho bobo tn->Nodes->Add(new TreeNode("<neto>")); } }

Dado um caminho que aponte para uma pasta, a funo Directory::GetDirectories retorna uma matriz de strings contendo os nomes de todas as sub-pastas. Como antes, esta matriz gerenciada e no nativa C++. A propriedade FullPath do TreeNode retorna uma string que representa o caminho da raiz para o n e ela consegue isto concatenando todos os rtulos dos ns-pais, separados por um caractere separador. Esta string do caminho muito til quando lidamos com rvores de pastas porque, se todo rtulo for um nome de pasta e o caractere separador for uma barra \, o caminho completo para a pasta pode ser construdo sem muita dificuldade quando o usurio expandir um n. Note os blocos try e catch em torno da chamada funo GetDirectories. O que acontece se voc clicar em uma letra de drive para um CD ou um disquete, que no contenha disco? 306

Microsoft Visual C++ .NET Passo-a-Passo GetDirectories lanar uma exceo como a mostrada na Figura 17.10. Assim, voc precisa se prevenir se no quiser que o programa falhe.

Figura 17.10: Exceo lanada quando o CppControls executado e clicamos em A. O lao for cria um TreeNode para cada nome de pasta e o adiciona como um filho do n atual. Os nomes retornados por GetDirectories so nomes de caminhos completos, mas voc quer usar o caminho completo como um nome. A funo Path::GetFileName descartar tudo at (e inclusive) a barra \ final, deixando apenas o nome da pasta. Finalmente, um n bobo adicionado a cada filho de modo que o sinal mais seja apropriadamente mostrado. Voc pode compilar o aplicativo e test-lo, pesquisando as pastas contidas nos seus discos rgidos. NOTA: Naturalmente, voc pode remover TreeNode da coleo de ns usando Remove. Se for adicionar ou remover muitos ns de uma vez, utilize as funes BeginUpdate e EndUpdate para impedir que o controle seja redesenhado toda vez que houver mudana na coleo Nodes. PASSO 7: O aplicativo j funciona como est, mas vamos adicionar imagens aos itens na rvore para lhe dar um aspecto mais profissional. Para isto, voc precisa usar um controle ImageList. Como o nome sugere, um ImageList simplesmente um controle que gerencia uma srie de imagens e usado por outros controles que utilizam muitas imagens, tais como TreeView, ListView e ToolBar. Arraste um controle ImageList da caixa de utilitrios para a forma, onde ela aparecer na seo localizada na base do ambiente de desenho, reservada para controles no-grficos. PASSO 8: Selecione o ImageList e, ento, localize a propriedade Images no editor de propriedades. Clique no boto das reticncias direita da propriedade para exibir o editor de coleo de imagens (Figura 17.11). Adicione os bitmaps Folder.bmp e Folder_Open.bmp lista. Estes arquivos so dois pequenos cones para representar as pastas abertas e fechadas e foram copiados dos cones do Windows Explorer. Se quiser, voc pode criar seus prprios bitmaps. PASSO 9: Agora, selecione a TreeView e fixe a propriedade ImageList para se referir ao controle ImageList. Voc tambm precisa fixar a propriedade ImageIndex para 0 ou 1, dependendo de qual entrada representa o bitmap da pasta fechada, para garantir que os bitmaps sejam corretamente relacionados com os atos de abrir e fechar pastas. PASSO 10: Construa e execute o aplicativo. A Figura 17.12 mostra a sada atual do CppControls.

307

Jos Wilson Vieira

Figura 17.11: Editor de coleo de imagens.

Figura 17.12: Sada atual do programa CppControls.

17.3.2.

USANDO O CONTROLE ListView

Uma vista de lista um controle usado para mostrar uma lista de itens. Ao contrrio da vista de rvore, no h estrutura hierrquica nos dados que esto sendo mostrados. Um item em uma vista de lista pode ter vrios itens de texto e cones associados com ele e pode ser mostrado em um dos seguintes modos: 308 Vista de cones grandes: Mostra cones grandes com texto abaixo. Os itens so organizados em uma grade. Vista de cones pequenos: Mostra cones pequenos com texto ao lado. Os itens so organizados em colunas. Vista de lista: Mostra o texto associado com o item, um item por linha. Vista com detalhes: Mostra colunas, cada uma contendo um item de texto.

Microsoft Visual C++ .NET Passo-a-Passo O Windows Explorer utiliza estas vistas e voc pode usar o menu View deste programa para mudar a aparncia da vista de lista. Como TreeView, ListView uma complexa classe com muitas propriedades, mtodos e eventos. As Tabelas 17.7, 17.8 e 17.9 resumem os mais importantes. Tabela 17.7: Propriedades mais importantes da classe ListView.
PROPRIEDADE DESCRIO Alignment Representa como os itens se alinham na vista de lista. AllowColumnReorder Determina se o usurio pode arrastar cabealhos de coluna para reordenar as colunas. Esta propriedade s significante em vista com detalhes. AutoArrange Determina se os itens so automaticamente organizados de acordo com o alinhamento. O padro true. BackColor Representa a cor do fundo do controle. BorderStyle Representa o estilo da borda do controle. O padro Fixed3D. CheckBoxes Determina se cada item ter uma caixa de checagem prxima a ele. O padro false. CheckedIndices Obtm os ndices os itens da lista atualmente marcados. CheckedItems Obtm os itens da lista atualmente marcados. Columns A coleo de colunas usadas na vista com detalhes. FocusedItem Obtm o item que atualmente possui o foco. ForeColor Representa a cor do primeiro plano do controle. FullRowSelect Determina se o clique em um item ir selecionar apenas o item ou a linha inteira a que pertence. Esta propriedade usada apenas na vista com detalhes. O padro false. GridLines Determina se so desenhadas linhas de grade entre os itens. O padro false. HeaderStyle Obtm ou fixa o estilo do cabealho da coluna: Clicvel, noclicvel ou sem cabealho. HideSelection Determina se o realce dos itens selecionados desaparece quando o controle perde o foco. O padro true. HoverSelection Determina se os itens podem ser selecionados pairando sobre eles o mouse. O padro false. Items A coleo de itens pertencentes ListView. LabelEdit Determina se o usurio pode editar legendas dos itens. O padro false. LabelWrap Determina se as legendas dos itens encobrem a viso do cone. O padro true. LargeImageList A ImageList usada na vista de cones grandes. MultiSelect Determina se a ListView permite seleo de mais de um item ao mesmo tempo. O padro true. Scrollable Determina se as barras de rolagem so visveis. O padro true. SelectedIndices Uma coleo que suporta os ndices dos itens atualmente selecionados. SelectedItems Uma coleo que suporta ponteiros para os itens atualmente selecionados. SmallImageList A ImageList usada na vista de cones pequenos. Sorting Representa o tipo de ordenao dos itens: ascendente, descendente ou nenhuma. StateImageList A ImageList usada para os estados definidos do aplicativo. TopItem Retorna o item no topo da lista. View Representa a vista que a ListView mostra atualmente: cone grande, cone pequeno, lista ou detalhes.

309

Jos Wilson Vieira Tabela 17.8: Mtodos mais importantes da classe ListView.
MTODO ArrangeIcons BeginUpdate DESCRIO Organiza os cones em vistas de cone pequeno ou grande. Desabilita o redesenho da ListView. Use este mtodo se voc for adicionar vrios itens porque ele impede o redenho do controle aps a adio de cada item. Clear Remove todos os itens e colunas da ListView. EndUpdate Reabilita o redesenho da ListView. EnsureVisible Assegura a visualizao de um dado item, rolando a lista se necessrio. GetItemAt Obtm o item nas coordenadas x, y especificadas. GetItemRect Obtm o retngulo de contorno para um item. IsInputKey Um mtodo protected que manipula teclas de entrada especiais, como Page Down e as teclas de seta.

Tabela 17.9: Eventos mais importantes da classe ListView.


EVENTO AfterLabelEdit BeforeLabelEdit ColumnClick ItemActivate ItemCheck DESCRIO Ocorre aps a edio de uma legenda. Ocorre antes da edio de uma legenda. Ocorre quando o usurio clica em uma coluna. Ocorre quando um item ativado. Ocorre quando o usurio marca um item. aplicado apenas se a propriedade CheckBoxes estiver fixada para true. ItemDrag Ocorre quando um item arrastado e solto. SelectedIndexChanged Ocorre quando o usurio clica em um item.

Os itens mostrados em uma vista de lista so representados pela classe ListViewItem. Uma ListViewItem pode conter alguns itens de dados: Uma legenda; Um ndice de imagem, que determina qual a imagem de vistas de cone grande e pequeno usar este item. Cores de primeiro plano e de fundo. Uma etiqueta, que pode ser de qualquer objeto .NET que voc queira anexar ao item. Uma coleo de subitens, que usada para fornecer os dados para as colunas quando o controle vista com detalhes.

17.3.2.1.

EXERCCIO: ADICIONANDO UM CONTROLE DE VISTA DE LISTA FORMA PRINCIPAL

Para lhe mostrar como usar uma ListView, o exerccio abaixo continua trabalhando com o projeto CppControls e adiciona um controle de vista de lista no lado direito da forma principal. Esta ListView exibir os detalhes de qualquer pasta que voc selecionar no controle de vista de rvore da forma. PASSO 1: Abra o projeto CppControls. Arraste uma ListView da caixa de utilitrios para a forma. PASSO 2: Fixe a propriedade Dock para Fill selecionando esta propriedade no editor e clicando da barra do centro do mostrador grfico que aparece. Esta propriedade far com que a vista de lista preencha a rea no usada da forma. 310

Microsoft Visual C++ .NET Passo-a-Passo PASSO 3: Para testar, crie um par de objetos ListViewItem e os adicione ListView. Selecione a ListView e selecione a propriedade Items no editor de propriedades. Clique no boto com as reticncias direita da propriedade e o editor de coleo ListViewItem aparecer (Figura 17.13). Pressione Add para adicionar um novo item e fixe sua propriedade Text para Foo. Adicione um segundo item e fixe Text para Bar.

Figura 17.13: Editor de coleo de ListViewItem. PASSO 4: Construa e execute o aplicativo. A Figura 17.14 mostra o resultado.

Figura 17.14: Sada atual do programa CppControls. Os itens foram mostrados na vista padro, i.e., cone grande, mas como no h cones, voc v apenas as legendas. 311

Jos Wilson Vieira


17.3.2.2. EXERCCIO: MOSTRANDO DETALHES DE UMA PASTA

Agora, que voc viu como criar uma ListView, vamos us-la para mostrar os detalhes da pasta que estiver selecionada na TreeView. Por simplicidade, este exemplo suportar apenas a vista com detalhes, mas voc pode adicionar cdigo para suportar outras vistas. PASSO 1: Abra o projeto CppControls. Abra o editor de coleo ListViewItem e delete os dois itens-amostra. Agora que voc sabe como o aplicativo funciona, j no precisa de amostras. PASSO 2: Fixe a propriedade View da ListView para Details para trocar o controle para a vista com detalhes. PASSO 3: Para usar a vista com detalhes, voc precisa configurar colunas para mostrar os dados. Estas informaes so organizadas na coleo Columns da ListView, que suporta objetos ColumnHeader. Voc pode abrir um editor de coleo de cabealhos no modo usual, selecionando a propriedade Columns e clicando no boto das reticncias. PASSO 4: Adicione trs cabealhos para a coleo, com o texto Type, Name e Size, e larguras de 40, 85 e 65 pixels, respectivamente. Cada ColumnHeader tem uma legenda fixada atravs da propriedade Text e uma largura em pixels. Neste exemplo, mostramos apenas o tipo do item (arquivo ou pasta), seu nome e seu tamanho. Se quiser, voc pode adicionar mais colunas para mostrar mais informaes. Quando fechar o editor, voc ver as colunas na vista de desenho. PASSO 5: Esta toda a configurao. Voc pode executar o programa e ver uma ListView vazia na forma. O prximo passo adicionar o cdigo para preencher a ListView com informaes sobre arquivos e pastas. Adicione uma funo-membro private chamada Fill_ListView classe Form1, como abaixo:
void Fill_ListView(String *path)//+++++++++++++++++++++++++++ { }//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

O argumento o caminho da pasta cujos contedos queremos mostrar. PASSO 6: Adicione uma chamada funo Fill_ListView no final da funo manipuladora do evento BeforeExpand, treeView1_BeforeExpand, como abaixo:
//Preenche a ListView com detalhes do n atual Fill_ListView(e->Node->FullPath);

Quando os ns-filhos forem construdos e adicionados TreeView, o caminho completo da pasta passado para Fill_ListView de modo que ela pode preencher a ListView. PASSO 7: Agora, vamos comear a codificar a funo Fill_ListView. A primeira tarefa limpar os itens existentes para constituir a nova listagem de pastas.
//Limpa toda a ListView listView1->Items->Clear();

Voc deve chamar Clear na propriedade Items da ListView e no diretamente, i.e., listView1->Clear(), para no limpar toda a ListView, incluindo os cabealhos de coluna que voc colocou h pouco. PASSO 8: No verdadeiro estilo Windows Explorer, a ListView listar primeiro as pastas, seguidas pelos arquivos. Adicione o cdigo abaixo para obter as sub-pastas do caminho atual:
//Comea com as pastas String *dirs[];

312

Microsoft Visual C++ .NET Passo-a-Passo


try { dirs = Directory::GetDirectories(path); } catch(System::Exception *pe) { MessageBox::Show(pe->Message, "Erro"); return; }

Este cdigo usa a mesma funo Directory::GetDirectories utilizada para criar as entradas da TreeView. Novamente, a chamada envolvida em um bloco try e catch, embora, neste caso, haja muito menos chance de que uma exceo seja lanada. PASSO 9: Uma vez tendo a lista dos nomes das pastas, crie ListViewItem para represent-los e os adicione para a ListView, como segue:
//Cria novos objetos ListViewItem para representar as pastas for(int i = 0; i < dirs->Count; i++) { String *pathName = dynamic_cast<String*>(dirs->get_Item(i)); String *dirName = Path::GetFileName(pathName); //Cria uma matriz de String* para suportar os subitens String *subItems[] new String*[3]; subItems[0] = S"Dir"; subItems[1] = dirName; subItems[2] = S" "; //Cria os ListViewItem dos subitens ListViewItem *itm = new ListViewItem(subItems); listView1->Items->Add(itm); }

A funo get_Item retira um item da matriz e GetFileName obtm a componente final do caminho. Precisamos de um ListViewItem que tenha trs campos de texto, um para cada uma das trs colunas na ListView. H um construtor da classe ListViewItem que recebe uma matriz de String. Assim, torna-se fcil criar uma matriz de trs strings, inicializando-as para o tipo do item (Dir para diretrio), o nome da pasta e uma string vazia para o tamanho porque no informamos tamanhos para as pastas. NOTA: Se voc quiser obter mais sofisticao, investigue a criao de objetos ListViewSubitem e os utilize para construir um ListViewItem. A classe ListViewSubitem lhe permite atribuir cones, fontes e cores a cada item individualmente. As duas ltimas linhas criam um ListViewItem da matriz de strings e, ento, o adiciona ListView. PASSO 10: Para adicionar os detalhes dos arquivos, seguimos o mesmo processo. Eis o cdigo:
//Agora, segue com os arquivos--------------------------String *files[]; try { files = Directory::GetFiles(path); } catch(System::Exception *pe) { MessageBox::Show(pe->Message, "Erro"); return; }

313

Jos Wilson Vieira


//Cria novos objetos ListViewItem para representar os arquivos for(int i = 0; i < files->Count; i++) { String *pathName = dynamic_cast<String*>(files->get_Item(i)); String *dirName = Path::GetFileName(pathName); //Cria uma matriz de String* para suportar os subitens String *subItems[] = new String*[3]; subItems[0] = S"File"; subItems[1] = dirName; //Encontra o tamanho do arquivo FileInfo *f = new FileInfo(pathName); subItems[2] = __box(f->Length)->ToString(); //Cria os objetos ListViewItem dos subitens ListViewItem *itm = new ListViewItem(subItems); //Adiciona o ListViewItem ListView listView1->Items->Add(itm); }//------------------------------------------------------

O cdigo quase igual ao dos detalhes para pastas, exceto para as linhas que encontram o tamanho do arquivo. A classe System::IO::FileInfo representa um caminho de arquivo e usada para obter o tamanho do arquivo em bytes. O valor long retornado pela propriedade encaixotado de modo que voc possa chamar ToString nele e a string resultante adicionada lista dos subitens. PASSO 11: Se construir e executar o aplicativo, voc poder listar os contedos das pastas como na Figura 17.15.

Figura 17.15: Sada atual do programa CppControls. PASSO 12: A ListView s atualizada quando voc expande um n da rvore, mas seria bom que fosse atualizada sempre que voc selecionasse um n. Para fazer isto, selecione a TreeView e adicione um manipulador para o evento BeforeSelect, mas, desta vez, no d um clique duplo no espao em branco prximo ao nome do evento. Use o boto drop-down para listar os manipuladores de evento para a TreeView e selecione o manipulador BeforeExpand. Ambos os eventos ativaro a mesma funo manipuladora. Vincular os dois eventos para o mesmo manipulador o que desejamos aqui porque queremos que ambos faam a mesma ao. Voc ver agora que a ListView atualizada sempre que selecionar ou expandir um n. 314

Microsoft Visual C++ .NET Passo-a-Passo 17.3.3. USANDO DIVISORES Agora o aplicativo funciona adequadamente, mas seria melhor se voc pudesse redimensionar os controles TreeView e ListView ao invs de ter que usar as barras de rolagem. O controle divisor (Splitter) permite que os usurios redimensionem controles ancorados em tempo de execuo e voc ver brevemente como adicionar um forma principal de modo que os usurios possam redimensionar os dois controles de vistas. O controle Splitter no tem muitas propriedades ou mtodos onde voc possa codificar. Uma vez adicionado um Splitter a uma forma, o usurio interage com ele arrastando-o de um lado para outro. Quando o mouse se desloca sobre o controle divisor, o cursor muda para uma seta com duas cabeas para mostrar que o divisor pode ser arrastado. possvel manipular os eventos que so disparados quando o divisor est se movendo, mas isto freqentemente no necessrio.

17.3.3.1.

EXERCCIO: ADICIONANDO UM CONTROLE Splitter A UMA FORMA

PASSO 1: Abra o projeto CppControls. Arraste um Splitter da caixa de utilitrios para a forma. Voc ver que, automaticamente, ele se posiciona entre a ListView e a TreeView. PASSO 2: Construa e execute o aplicativo. Voc ver que, agora, consegue deslocar a barra divisora para redimensionar o controle da vista de rvore (Figura 17.16).

Figura 17.16: Sada atual do programa CppControls.

17.3.4.

USANDO BARRAS DE UTILITRIOS

As barras de utilitrios so uma caracterstica padro de quase todo aplicativo GUI e a classe ToolBar fornece tudo que voc precisa para adicionar a funcionalidade destas barras a um aplicativo. A classe ToolBar tem muitas propriedades, como as apresentadas na Tabela 17.10. Os botes em uma barra de utilitrios so representados por objetos ToolBarButton e esta classe tambm tem diversas propriedades teis, que so sumarizadas na Tabela 17.11. Os botes da barra de utilitrios podem ser mostrado utilizando-se um dos estilos: PushButton: O boto simplesmente atua como um boto que se empurra (padro). DropDownButton: Clicando no boto abre-se um menu drop-down. 315

Jos Wilson Vieira ToggleButton: Os pinos de boto entre seus estados acima e abaixo. Separator: O boto atua como um separador entre dois outros botes. Como o separador em um menu, este estilo de boto no pode ser selecionado. Tabela 17.10: Principais propriedades da classe ToolBar.
PROPRIEDADE Anchor DESCRIO Determina a qual extremidade do continer a barra de utilitrios ancorada. Appearance Representa a aparncia da ToolBar, que pode ser Normal ou Flat. O padro Normal. AutoSize Determina se a ToolBar se redimensiona automaticamente para ajustar botes. O padro true. BorderStyle Representa o estilo das bordas da ToolBar. O padro None. Buttons Obtm a coleo de objetos ToolBarButton apresentada pela ToolBar. ButtonSize Um valor Size que representa o tamanho dos botes na ToolBar. O padro 24 pixels de largura por 22 pixels de altura, ou suficientemente grande para suportar a imagem do boto e o texto. Divider Determina se a ToolBar mostra um divisor. O padro true. DropDownArrows Determina se as setas so mostradas prximas aos botes drop-down. O padro true. ImageList A lista de imagens usada na ToolBar. ImageSize Obtm um objeto Size representando o tamanho das imagens na ImageList. ShowToolTips Determina se as ToolTips (dicas) so mostradas para os botes. TextAlign Representa como o texto se alinha com relao s imagens nos botes. O padro ToolBarTextAlign::Underneath. Wrappable Determina se os botes envolvero a prxima linha se a ToolBar se tornar muito estreita. O padro true.

Tabela 17.11: Algumas propriedades teis da classe ToolBarButton.


PROPRIEDADE DropDownMenu Enabled ImageIndex Parent PartialPush Pushed Rectangle Style Tag Text ToolTipText DESCRIO Representa o menu associado com um boto drop-down. Determina se o boto est habilitado. O padro true. Representa o ndice da imagem na ImageList da ToolBar que ser usado para o boto. O padro 1 (nenhuma imagem). Obtm um ponteiro para a ToolBar que hospeda um boto. Determina se um boto estilo pino foi parcialmente empurrado. O padro false. Determina se um boto empurrado atualmente. O padro false. Obtm o retngulo de contorno para um boto. Representa o estilo do boto. O padro PushButton. Representa qualquer objeto que voc quer associar ao boto. Representa a legenda no boto, se houver. Representa o texto a ser mostrado pelo utilitrio de dicas do boto. A propriedade ShowToolTips da ToolBar me deve ser true para que as dicas sejam mostradas. Determina se o boto visvel. O padro true.

Visible

A classe ToolBar tem dois eventos comumente usados: O ButtonClick, que disparado quando um boto clicado, e o ButtonDropDown, que disparado quando o menu de um boto dropdown est para ser mostrado.

316

Microsoft Visual C++ .NET Passo-a-Passo


17.3.4.1. EXERCCIO: ADICIONANDO UMA ToolBar A UMA FORMA

Este exerccio adicionar uma barra de utilitrios forma do CppControls. A barra hospedar dois botes: o primeiro um drop-down com um menu e o segundo um PushButton padro. Voc tambm adicionar um manipulador para o segundo boto para aprender como os manipuladores so usados com barras de utilitrios. PASSO 1: Abra o projeto CppControls. Arraste um controle ToolBar para a forma. No importa onde o solta, voc ver que ele no muda o tamanho para se ajustar completamente ao longo do topo da forma, mas, ao invs disto, se localiza apenas no topo da ListView, encobrindo os cabealhos das colunas (Figura 17.17).

Figura 17.17: Forma do CppControls mostrando o controle ToolBar encobrindo os cabealhos das colunas. O problema que o ambiente de desenho decide quem tem prioridade com base na ordem na qual os controles foram adicionados forma. Se voc tivesse adicionado primeiro a ToolBar e, ento, a TreeView e a ListView, todos se ajustariam juntos. Resolva o problema abrindo o cdigo fonte da forma e observando o cdigo da funo InitializeComponent. Prximo ao fim da funo, voc ver as seguintes linhas de cdigo:
this->Controls->Add(this->toolBar1); this->Controls->Add(this->splitter1); this->Controls->Add(this->listView1); this->Controls->Add(this->treeView1);

Desloque a entrada toolBar1 para o fim desta lista de declaraes. Quando voltar ao ambiente de desenho, voc ver, apropriadamente posta no topo da forma, a barra de utilitrios, com a TreeView e a ListView abaixo dela (Figura 17.18). PASSO 2: A prxima tarefa criar um objeto ImageList que suportar os bitmaps mostrados nos botes da barra de utilitrios. Arraste uma segunda ImageList para a forma e use o editor de propriedades para adicionar os arquivos View.bmp e Exit.bmp (coloque-os na pasta do projeto) coleo Images. Fixe a propriedade ImageList da ToolBar para se referir nova ImageList. PASSO 3: Verifique se a propriedade ShowToolTips da ToolBar est fixada para true de modo que as dicas de utilitrios sejam mostradas quando o usurio passar o cursor sobre os botes. 317

Jos Wilson Vieira

Figura 17.18: Corrigindo a posio da ToolBar em relao Figura 17.17. PASSO 4: Um dos botes da barra de utilitrios vai mostrar um menu drop-down quando clicado. Assim, crie um novo menu arrastando um ContextMenu da caixa de utilitrios para a forma. Adicione um ou dois itens ao menu de modo que voc possa ver como o boto funciona em tempo de execuo. PASSO 5: Crie os botes utilizando a propriedade Buttons da ToolBar no editor de propriedades e, clicando no boto das reticncias para exibir o editor da coleo de ToolBarButton. PASSO 6: Pressione o boto Add para adicionar um novo boto coleo. Fixe seu estilo para DropDownButton, seu ToolTipText para View e seu ImageIndex para 0. Fixar um ndice 0 significa que o boto usar a primeira imagem da ImageList, que deve ser o arquivo View.bmp. Fixe a propriedade DropDownMenu para se referir ao ContextMenu que voc acaba de criar. PASSO 7: Adicione um segundo boto, que funcionar como um separador, fixando seu estilo para Separator. A fixao deste estilo permite que este boto seja exibido como uma lacuna no boto fixado e este objeto no exerce mais nenhum outro papel na interface de usurio do programa. PASSO 8: Adicione um terceiro boto que ser exibido como um PushButton normal. Deixe seu estilo como PushButton, fixe a ToolTipText para Exit e fixe o ImageIndex para 1. Logo mais voc adicionar um manipulador para este boto que provocar a sada do aplicativo. PASSO 9: Para manipular os eventos de boto, use o editor de propriedades para localizar o evento ButtonClick da ToolBar e d um clique duplo no espao em branco direita do nome do evento. PASSO 10: A funo toolBar1_ButtonClick manipula eventos vindos de todos os botes na ToolBar. Assim, voc precisa checar qual o boto enviou o evento:
private: System::Void toolBar1_ButtonClick(System::Object * System::Windows::Forms::ToolBarButtonClickEventArgs * e) { if(e->Button == toolBarButton3) { Application::Exit(); } } sender,

A funo manipuladora recebe um objeto ToolBarButtonClickEventArgs que contm detalhes de qual o boto foi clicado. A funo checa o membro Button deste objeto, que suporta um ponteiro para o boto que originou o evento. Se ele for o terceiro boto, que tem o nome padro toolBarButton3, o aplicativo termina. 318

Microsoft Visual C++ .NET Passo-a-Passo Note que voc no checa o argumento *sender. Este argumento suporta um ponteiro para a prpria ToolBar, no para o boto. Ele seria til se o aplicativo contivesse mais de uma ToolBar. NOTA: Para manipular uma seleo do usurio de um item do menu associado com um boto dropdown, simplesmente anexe um manipulador de eventos ao item de menu, exatamente como voc faz para qualquer item de menu (reveja o captulo 16). PASSO 11: Construa e execute o aplicativo. A Figura 17.19 mostra o resultado. Clicando no boto da direita voc provoca a sada do aplicativo.

Figura 17.19: Sada atual do programa CppControls.

17.3.5.

USANDO BARRAS DE STATUS

A classe StatusBar representa a barra de status padro que voc v na base de muitas janelas de aplicativo. Como seu nome sugere, o propsito de uma barra de status apresentar informaes de status ao usurio. As barras de status simples mostram um ou mais itens de texto e, se voc quiser um visualizador mais avanado, pode usar parte ou todo o desenho da barra de status para exibir bitmaps, controles de progresso ou outros elementos UI. Como este captulo est apresentando uma introduo aos controles, nos concentraremos em mostrar como exibir texto em uma barra de status. NOTA: Embora voc possa adicionar barras de status a qualquer forma, elas usualmente so adicionadas s caixas de dilogo.

17.3.5.1.

EXERCCIO: ADICIONANDO UMA BARRA DE STATUS A UMA FORMA

O exerccio a seguir mostra como adicionar uma barra de status a uma forma e como us-la para exibir informaes. PASSO 1: Abra o projeto CppControls. Arraste um controle StatusBar da caixa de utilitrios para a forma. Como acontece com o controle ToolBar, voc achar que o StatusBar no se ajusta corretamente na base da forma. A soluo a mesma j usada: edite o cdigo em InitializeComponent de modo que o objeto StatusBar seja o ltimo controle adicionado na coleo Controls, e no o primeiro. PASSO 2: Voc pode usar a barra de status para, por exemplo, mostrar o caminho cujos contedos esto sendo listados atualmente na ListView. Para fazer isto, adicione a linha abaixo no comeo da funo Fill_ListView: 319

Jos Wilson Vieira


//Coloca o caminho na barra de status statusBar1->Text = path;

O que for atribudo propriedade Text da StatusBar ser mostrado justificado esquerda no controle. PASSO 3: Construa e execute o aplicativo, e selecione uma pasta. Voc ver o caminho mostrado na barra de status como na Figura 17.20.

Figura 17.20: Sada atual do programa CppControls. Uma StatusBar pode mostrar um poder de redimensionamento encurtando para a direita de acordo com o redimensionamento da janela pelo usurio. Este poder de redimensionamento habilitado como padro, mas voc pode deslig-lo configurando a propriedade SizingGrip para false. NOTA: Note que o caminho mostrado na StatusBar tem duas barras inclinadas para a esquerda (\\) como o primeiro separador. Elas aparecem porque o nome da pasta raiz retornado como C:\ e, quando a TreeView constri o caminho completo para voc, ela usa uma \ como separador de caminho. Ter duas ao invs de uma barra inclinada no faz nenhuma diferena para usar o caminho, mas se voc quiser organiz-lo, pode remover a barra do nome da raiz. Voc pode mostrar mais do que uma simples informao de texto em uma barra de status, como vemos na Figura 17.21.

Figura 17.21: Barra de status do Windows Explorer. Cada uma das reas demarcadas chamada painel e voc pode adicionar tantos quantos queira sua barra de status. Um objeto StatusBar mostrar texto ou painis, e voc usa a propriedade ShowPanels para determinar o que vai ser mostrado. O padro para ShowPanels false. Assim, mostrado texto.

17.3.5.2.

EXERCCIO: MODIFICANDO A BARRA DE STATUS PARA EXIBIR PAINIS

320

Microsoft Visual C++ .NET Passo-a-Passo Eis como modificar a StatusBar para suportar dois painis e mostrar o caminho atual em um deles. PASSO 1: Use o editor de propriedades para localizar a propriedade Panels da StatusBar, e clique no boto das reticncias para exibir o editor de coleo StatusBarPanel. PASSO 2: Adicione dois painis coleo, o primeiro com largura de 200 pixels e o segundo com 100. Delete o texto padro para ambos. Feche o editor e fixe a propriedade ShowPanels da StatusBar para true. PASSO 3: Exiba o caminho no primeiro painel mudando o cdigo na funo Fill_ListView para fixar a propriedade Text do StatusBarPanel e no da StatusBar.
//Coloca o caminho no primeiro painel da barra de status statusBarPanel1->Text = path;

PASSO 4: Se construir e executar o aplicativo, agora, voc ver a StatusBar mostrando dois painis, como na Figura 17.22.

Figura 17.22: Sada do programa CppControls.

321

Jos Wilson Vieira 17.4. RPIDAS REFERNCIAS SOBRE O CAPTULO 17

A Tabela 17.12 mostra um resumo deste captulo. Tabela 17.12: Resumo do captulo 17.
PARA Criar e mostrar uma caixa de dilogo modal Retornar um cdigo de status de uma caixa de dilogo FAA ISTO Crie uma forma e a exiba usando a funo ShowDialog. Fixe a propriedade DialogResult para a forma. Ex.: DialogResult = DialogResult::OK; Alternativamente, fixe a propriedade DialogResult dos botes que voc quer usar fechar a forma. Crie um objeto de caixa de dilogo do tipo apropriado, fixe suas propriedades e, ento, o exiba usando ShowDialog. Arraste um controle TreeView para a forma e, ento, edite a coleo de controles Nodes para adicionar ns vista de rvore. Arraste um objeto ListView para a forma e, ento, edite a coleo de controles Items. Arraste um objeto Splitter da caixa de utilitrios para a forma. Ele se localizar na posio correta. Arraste um objeto ToolBar para a forma. Ele ficar ancorado no topo. Use o editor de propriedades para editar a coleo Buttons do objeto ToolBar. Arraste um objeto StatusBar da caixa de utilitrios para a forma. Ento, voc pode atribuir strings para a propriedade Text exibi-las na barra de status. Fixe a propriedade ShowPanels do objeto StatusBar para true. Ento, use a propriedade Panels do StatusBar para criar objetos StatusBarPanel. Voc pode, agora, atribuir strings propriedade Text de um painel para exibi-las neste.

Criar uma caixa de dilogo comum Criar e usar uma TreeView Criar e usar uma ListView Adicionar um divisor a uma forma Adicionar uma barra de utilitrios a uma forma Adicionar botes a uma barra de utilitrios Adicionar uma barra de status a uma forma Adicionar painis a uma barra de status

322

Microsoft Visual C++ .NET Passo-a-Passo 18. CAPTULO 18: SADAS GRFICAS

Neste captulo, voc aprender como: usar a GDI+ para desenhar em formas; Usar cores; Usar fontes; Mostrar imagens em formas; Imprimir de aplicativos.

Os captulos 16 e 17 lhe introduziram no mundo do Windows Forms e lhe mostraram como usar as classes do namespace System::Windows::Forms para construir aplicativos GUI. Este captulo introduz os mecanismos de desenho que permeiam toda a funcionalidade do Windows Forms. Voc vai aprender como desenhar em formas, como exibir imagens e como imprimir.

18.1.

GRFICOS COM GDI+

O subsistema do Microsoft .NET que manipula sadas grficas chamado GDI+. A GDI (Graphical Device Interface) foi o modelo original de grficos do Windows introduzido com o Microsoft Windows 3. Ela se manteve praticamente a mesma ao longo das verses sucessivas do Windows, mas foi sensivelmente melhorada para o .NET Framework. Da a adio do sinal + ao nome. A GDI+ fornece uma biblioteca de classes para construes grficas bidimensionais simples, como, por exemplo, desenhar linhas e formas simples, exibir texto e bitmaps, e imprimir. Embora a GDI+ seja muito melhor do que a GDI original, ela ainda , essencialmente, uma biblioteca para grficos bidimensionais simples, sem nenhuma caracterstica avanada como animao ou efeitos tridimensionais.

18.1.1.

OS namespaces System::Drawing

A funcionalidade GDI+ distribuda por vrios namespaces: System::Drawing: Fornece a funcionalidade bsica; System::Drawing::Drawing avanados; 2D: Fornece grficos bidimensionais e vetoriais mais

System::Drawing::Imaging: Fornece funcionalidade para processamento de imagens; System::Drawing::Text: System::Drawing; Fornece mais funcionalidade tipogrfica do que

System::Drawing::Printing: Manipula impresso; System::Drawing::Design: Fornece funcionalidade para estender o tempo de design UI no Visual Studio .NET.

Neste captulo, estaremos observando principalmente as caractersticas oferecidas pelo namespace bsico System::Drawing, bem como a funcionalidade para impresso fornecida pelo System::Drawing::Printing. As principais classes no namespace System::Drawing so listadas na Tabela 18.1. 323

Jos Wilson Vieira Tabela 18.1: Principais classes do namespace System::Drawing.


CLASSE Bitmap DESCRIO Representa um bitmap. Esta classe pode funcionar com diversos formatos comuns, incluindo BMP, GIF e JPG. Brush Os pincis so usados para preencher os interiores das formas. Brushes Uma coleo de pincis predefinidos. Color Estrutura que representa uma cor. Font Representa uma fonte. FontFamily Representa uma famlia de fontes. Graphics Representa uma superfcie de desenho. Icon Representa um cone. Image A base para as outras classes de imagem, como Bitmap e Metafile. Pen Usada para desenhar o contorno das formas. Pens Uma coleo de canetas predefinidas. Point, PointF Estruturas para inteiro e ponto-flutuante que representam um ponto X, Y. Rectangle, Estruturas para inteiro e ponto-flutuante que representam um RectangleF retngulo. Region Representa uma regio. As regies no tm que ser retangulares. Size, SizeF Estruturas para inteiro e ponto-flutuante que representam uma dimenso. SolidBrush Um pincel que preenche uma forma com uma cor slida. StringFormat Encapsula um conjunto de informaes de layout de texto, como, por exemplo, alinhamento e espaamento entre linhas. SystemBrushes Uma coleo de objetos SolidBrush representando as cores do sistema. SystemColors Uma coleo de objetos Color representando as cores do sistema. SystemIcons Uma coleo de cones, representando principalmente aqueles usados nas caixas de mensagem. SystemPens Uma coleo de objetos Pen representando as cores do sistema. TextureBrush Um pincel que usa uma imagem para preencher formas.

18.1.2.

A CLASSE Graphics

Se quiser desenhar em uma forma, voc precisa considerar alguns detalhes iniciais. Por exemplo, seu programa pode executar em um PDA (Personal Digital Assistant) ou atravs de um telefone WAP (Wireless Application Protocol)? O visualizador monocromtico ou colorido? Quantas cores o visualizador pode suportar? Qual a sua resoluo? E sobre sadas para impressoras elas podem ser colorida ou preta e branco, suportar diferentes tamanhos de papel em diferentes orientaes, e ter uma variedade de resolues. Porm a GDI+ lhe poupa a preocupao com estes assuntos atravs da classe Graphics, que representa uma superfcie de desenho idealizada. Como voc ver em breve, a classe Graphics suporta diversas operaes de desenho. Voc usa estas operaes para desenhar e o objeto Graphics as renderiza no atual dispositivo de sada, efetivamente lhe isolando das caractersticas do dispositivo. NOTA: Se j ouviu falar de um contexto de dispositivo Windows, voc sabe o que um objeto Graphics porque a classe Graphics o modo .NET de encapsular um contexto de dispositivo. Alm de ser o lugar para onde voc envia a sada grfica, um objeto Graphics tambm pode lhe informar sobre o dispositivo que ele est usando. Deste modo, um objeto Graphics uma ponte entre voc e o dispositivo de sada. O restante deste captulo lhe mostrar como usar a classe Graphics para produzir sadas grficas.

324

Microsoft Visual C++ .NET Passo-a-Passo 18.1.3. CRIANDO OBJETOS Graphics

Se olhar na documentao para a classe Graphics, voc ver que ela no tem construtores. Voc no cria um objeto Graphics diretamente usando um construtor, mas, ao invs disto, usa a funo CreateGraphics do namespace Form para obter um.
//Cria um objeto Graphics Graphics *pg = myForm->CreateGraphics();

Voc no cria um objeto Graphics diretamente porque ele representa uma estrutura de dados oriunda do Windows chamada contexto de dispositivo. Se voc obtm um novo objeto ou um objeto convertido isto no problema seu porque o uso de um mtodo de fabricao como o proporcionado pela funo CreateGraphics esconde os detalhes de como o objeto Graphics foi de fato obtido. Um objeto Graphics usa recursos do sistema, que sero liberados quando o objeto coletado pela lixeira. Como no sabe quando isto ocorrer, uma boa idia voc mesmo liberar estes recursos, quando no mais precisar deles, chamando o mtodo Dispose.
//Libera os recursos retidos pelo objeto Graphics pg->Dispose();

boa prtica de programao reter objetos Graphics o menor tempo possvel, porque o suporte para recursos grficos em alguns sistemas pode ser limitado.

18.1.4.

DESENHANDO OBJETOS

As operaes bsicas so realizadas utilizando-se os objetos Pen e Brush. Os objetos Pen so usados para desenhar linhas e contornos de formas, e os Brush para preencher o interior das formas.

18.1.4.1.

OBJETOS Pen

Os objetos Pen simples so criados especificando-se uma largura de linha em pixels e uma cor.
//Cria uma caneta preta com largura padro de 1 pixel Pen *pen1 = new Pen(Color::Black); //Cria uma caneta vermelha com 2 pixels de largura Pen *pen2 = new Pen(Color::Red, 2.0);

Os argumentos Color so especificados usando os membros static da estrutura Color. Discutiremos com mais detalhes a estrutura Color ainda neste captulo. Se quiser criar objetos Pen mais avanados, voc pode especificar um Brush ao invs de uma Color. A especificao de um Brush lhe permite preencher as linhas com imagens e padres.

18.1.4.2.

OBJETOS Brush

H uma grande variedade de pinceis, todos eles herdeiros da classe-base Brush. O mais simples o SolidBrush, que preenche uma rea com uma cor slida.
//Cria um pincel azul SolidBrush *br1 = new SolidBrush(Color::Blue);

A classe TextureBrush usa uma imagem para preencher formas. Se a imagem for menor do que a forma, ela pode ser usada como ladrilho.
//Cria uma TextureBrush como ladrilho

325

Jos Wilson Vieira


TextureBrush *br2 = new TextureBrush(new Bitmap("brush.bmp"), WrapMode::Tiled);

18.1.5.

CANETAS E PINCIS PADRONIZADOS

A classe Pens tem propriedades que definem mais de 140 canetas representando o intervalo completo das cores definidas pelo sistema operacional. Cada caneta tem largura de 1 pixel. A classe Brushs funciona exatamente do mesmo jeito para pincis.
//Cria uma caneta vermelha com 1 pixel de largura Pen *redPen = Pens::Red; //Cria um pincel azul padro Brush *blueBrush = Brushes::Blue;

As classes SystemPens e SystemBrushes so bastante similares, mas suas propriedades refletem as cores padronizadas do sistema que so usadas para desenhar componentes UI.
//Cria uma caneta da com das molduras da janela Pen *pen = SystemPens::WindowFrame; //Cria um pincel que tem a cor padro dos controles 3D Brush *brush = SystemBrushes::Control;

Os valores associados com estas canetas e pincis podem mudar se o usurio mudar o esquema de cores do desktop atravs do painel de controle.

18.1.6.

OPERAES DE DESENHO

Agora vamos reunir o que voc aprendeu at aqui e fazermos algum desenho em uma forma. A classe Graphics tem diversos mtodos de desenho. Os mais comuns so mostrados na Tabela 18.2. Tabela 18.2: Mtodos mais comuns da classe Graphics.
MTODO Clear DrawArc DrawBezier DrawClosedCurve, FillClosedCurve DrawCurve DrawEllipse, FillEllipse DrawIcon DrawImage DrawLine DrawLines DrawPie, FillPie DrawPolygon, FillPolygon DrawRectangle, FillRectangle DrawString FillRegion DESCRIO Preenche a rea de desenho inteira com uma cor. Desenha um arco representando uma poro de uma elipse. Desenha uma curva Bezier. Desenha um preenche uma curva fechada definida por um vetor de pontos. Desenha uma curva. Desenha ou preenche uma elipse definida por um retngulo de contorno. Desenha um cone. Desenha uma imagem. Desenha uma linha. Desenha um conjunto de linhas conectando um vetor de pontos. Desenha ou preenche uma fatia de torta definida por uma elipse e duas linhas radiais. Desenha ou preenche um polgono definido por um vetor de pontos. Desenha ou preenche um retngulo. Desenha uma string de texto. Preenche uma regio definida por um objeto Region.

Note como h chamadas separadas para desenhar e preencher. Se quiser desenhar um retngulo com contorno preto e preench-lo com vermelho, voc ter que chamar as funes DrawRectangle e FillRectangle. 326

Microsoft Visual C++ .NET Passo-a-Passo


18.1.6.1. EXERCCIO: DESENHANDO FORMAS SIMPLES

A seguir, voc ver como desenhar algumas figuras simples em uma forma. PASSO 1: Crie um novo projeto C++ Windows Forms Application (.NET) chamado CppDraw. PASSO 2: Use o editor de propriedades para fixar o ttulo da forma para Desenho. Se examinar as propriedades da forma, voc ver que o tamanho da rea cliente fixado para o valor padro 300 x 300 pixels. NOTA: A rea cliente a parte da forma de cuja manuteno voc responsvel e representa a rea da forma dentro das barras de rolagem e da moldura. PASSO 3: Arraste um boto da caixa de utilitrios para a forma. Usando o editor de propriedades, fixe seu nome para drawBtn, sua propriedade Location para 200;200, e sua legenda para Desenhar. Agora, adicione um manipulador para o evento Click do boto. Voc viu como adicionar botes e configurar manipuladores no captulo 16. Se necessrio, refresque a sua memria. O boto posicionado em baixo e direita na janela e, quando clicado, dever executar o cdigo grfico. PASSO 4: Eis o cdigo para o manipulador:
private: System::Void drawBtn_Click(System::Object * e) { //Obtm um objeto Graphics Graphics *pg = CreateGraphics(); //Obtm uma caneta Pen *pen1 = new Pen(Color::Black); //Desenha uma linha pg->DrawLine(pen1, 20, 20, 100, 100); //Libera o objeto Graphics pg->Dispose(); } sender, System::EventArgs *

Quando o boto clicado, o objeto Graphics criado, junto com uma caneta preta de 1 pixel de largura. O mtodo DrawLine recebe um ponteiro para uma Pen e as coordenadas iniciais e finais da linha, em pixels. Uma vez desenhada a linha, voc j no precisa do objeto Graphics. Assim, voc pode chamar seu mtodo Dispose. As coordenadas esto em pixels, com (0, 0) no canto superior esquerdo da forma. Assim, a coordenada X cresce para a direita e a Y para baixo. PASSO 5: Construa e execute o programa. A Figura 18.1 mostra a sada atual do programa CppDraw. PASSO 6: Agora, voc pode adicionar algumas chamadas aos mtodos de Graphics. Antes, voc deve adicionar uma diretiva using para o namespace System::Drawing::Drawing2D, no topo do arquivo Form1.h. Voc precisar desta diretiva porque algumas estruturas de dados usadas no cdigo esto no namespace Drawing2D. Aps isto, coloque o cdigo abaixo aps a primeira chamada funo DrawLine, no mtodo drawBtn_Click, e antes da chamada funo Dispose.

327

Jos Wilson Vieira

Figura 18.1: Sada atual do programa CppDraw.


//Desenha uma linha estilizada Pen *pen2 = new Pen(Color::Blue, 3.0); pen2->DashStyle = DashStyle::Dash; pg->DrawLine(pen2, 20, 120, 100, 60); //Desenha um retngulo preenchido SolidBrush *sb1 = new SolidBrush(Color::Red); pg->FillRectangle(sb1, 60, 30, 40, 40); //Desenha um retngulo preenchido e contornado SolidBrush *sb2 = new SolidBrush(Color::Yellow); pg->FillRectangle(sb2, 90, 40, 45, 45); pg->DrawRectangle(pen1, 90, 40, 45, 45); //Desenha uma elipse preenchida SolidBrush *sb3 = new SolidBrush(Color::Green); pg->FillEllipse(sb3, 30, 100, 65, 50);

A linha desenhada com uma nova caneta que faz linhas tracejadas. H diversos membros da enumerao DashStyle e voc tambm pode produzir linhas tracejadas personalizadas definindo o padro do trao. Note que o segundo retngulo desenhado tem um fundo amarelo e um contorno preto. PASSO 7: Construa e execute o aplicativo. A Figura 18.2 mostra a sada atual do CppDraw.

Figura 18.2: Sada atual do programa CppDraw.

328

Microsoft Visual C++ .NET Passo-a-Passo 18.1.7. EVENTOS Paint

Experimente executar o programa, minimize a janela pela barra de tarefas e clique novamente no mesmo boto. Voc ver que todos os grficos desapareceram e tambm ver a mesma coisa se passar outra janela por sobre a janela do seu aplicativo. Voc pode regenerar os grficos clicando novamente no boto Desenhar, mas por que os desenhos desaparecem? O que acontece aqui bastante normal. O Windows tem o cuidado de redesenhar os componentes na forma a prpria forma, a barra de ttulo e o boto mas sua responsabilidade redesenhar qualquer coisa que voc desenhe na rea cliente da forma. Sempre que uma janela precisa ser redesenhada, o sistema operacional a enviar um evento Paint. Se manipular este evento Paint, voc pode assegurar que a forma sempre mostrar seus dados.

18.1.7.1.

EXERCCIO: MANIPULANDO EVENTOS Paint E INTERAGINDO COM O MOUSE

O exerccio a seguir mostra como manipular eventos Paint e tambm como interagir com o mouse. O programa ir capturar os eventos MouseDown e MouseUp quando voc clica e quando libera o boto do mouse; e desenhar uma linha entre o ponto onde voc clicou o boto e o ponto onde o liberou. PASSO 1: Abra o projeto CppDraw. Voc pode remover o boto e a funo manipuladora porque no sero utilizados neste exerccio, mas se quiser pode deix-los como esto. PASSO 2: Adicione dois membros Point classe Form1.
Point p1, p2;

Como Point um tipo de valor, estes membros so declarados como variveis e no como ponteiros. PASSO 3: O evento MouseDown disparado quando qualquer boto do mouse pressionado enquanto o mouse est sobre um controle; o evento MouseUp disparado quando o boto liberado. Organize para que funes manipuladoras sejam chamadas para os eventos MouseDown e MouseUp na forma usando o editor de propriedades para adicionar manipuladores para estes eventos. Agora, edite o cdigo para o manipulador do evento MouseDown de modo que ele salvar a posio do ponteiro do mouse no instante em que o boto foi clicado.
private: System::Void Form1_MouseDown(System::Object * System::Windows::Forms::MouseEventArgs * e) { p1.X = e->X; p1.Y = e->Y; } sender,

As funes manipuladoras para eventos relacionados MouseEventArgs que tm diversas propriedades teis: Button: Informa-lhe qual o boto do mouse foi clicado;

com

mouse

recebem

objetos

Clicks: Diz-lhe o nmero de cliques que o evento disparou; Delta: Diz-lhe o nmero de giros que a roda de um mouse com este equipamento realizou; X e Y: Diz-lhe as coordenadas X e Y do mouse quando o evento disparado.

Neste exemplo, voc est salvando os X e Y na varivel p1.

329

Jos Wilson Vieira NOTA: Como p1 um tipo de valor, voc tem que usar a notao com o operador ponto (.) para acessar seus membros. O manipulador do evento MouseUp similar, i.e., tambm armazena coordenadas.
private: System::Void Form1_MouseUp(System::Object * System::Windows::Forms::MouseEventArgs * e) { p2.X = e->X; p2.Y = e->Y; Graphics *gr = CreateGraphics(); Pen *pen1 = new Pen(Color::Black); gr->DrawLine(pen1, p1.X, p1.Y, p2.X, p2.Y); gr->Dispose(); } sender,

Uma vez armazenadas as coordenadas, a funo desenha uma linha entre os dois pontos. Se construir e executar o aplicativo neste ponto, voc pode desenhar linhas na forma usando o mouse. Entretanto, voc ver que elas desaparecem sempre que a forma precisar ser redesenhada, exatamente como no exerccio anterior. No restante deste exerccio, voc salvar as coordenadas das linhas quando as desenhar e, ento, redesenhar a coleo de linhas sempre que a forma precisar ser redesenhada. PASSO 4: Voc vai armazenar as informaes de linha em uma ArrayList, que pertence ao namespace System::Collections. Este namespace j est referenciado nos projetos Windows Forms, de modo que voc no precisa adicionar uma diretiva using para seu cdigo. Adicione um membro private ArrayList para a classe Form1.
ArrayList *list;

PASSO 5: Crie um objeto ArrayList no construtor Form1, adicionando a linha aps a chamada funo InitializeComponent:
list = new ArrayList();

PASSO 6: Adicione uma nova estrutura gerenciada chamada Line no topo arquivo de cabealho Form1.h, aps as diretivas e antes da declarao da classe Form1.
__gc struct Line { Point p1; Point p2; };

Esta estrutura simplesmente suporta dois pontos que representam as extremidades da linha. Voc pode estender este tipo de suporte para outras informaes, como, por exemplo, cor e estilo de linha. PASSO 7: Toda vez que desenhar uma linha, voc cria um novo objeto Line e o adiciona ArrayList. Adicione o cdigo abaixo no final da funo Form1_MouseUp, aps a chamada DrawLine:
//Adiciona uma nova linha na lista Line *pline = new Line(); pline->p1 = p1; pline->p2 = p2; list->Add(pline);

O objeto Line simplesmente suporta os pontos que representam as extremidades da linha. PASSO 8: O use o editor de propriedades para adicionar um manipulador para eventos Paint. 330

Microsoft Visual C++ .NET Passo-a-Passo PASSO 9: Adicione o cdigo abaixo para o manipulador do evento Paint:
private: System::Void Form1_Paint(System::Object * System::Windows::Forms::PaintEventArgs * e) { Graphics *gr = e->Graphics; Pen *pen1 = new Pen(Color::Black); sender,

for(int i = 0; i < list->Count; i++) { Line *pline = dynamic_cast<Line*>(list->get_Item(i)); gr->DrawLine(pen1, pline->p1.X, pline->p1.Y, pline->p2.X, pline->p2.Y); } }

Inicialmente a funo obtm um objeto Graphics e, ento, cria uma Pen. Quando estiver manipulando um evento Paint, voc obtm um objeto Graphics como parte do argumento PaintEventArgs. Use-o, i.e., no crie um novo objeto Graphics. Outra coisa importante: no chame Dispose neste objeto quando voc no mais utiliz-lo. Como voc no o criou, no tem a responsabilidade de destru-lo. Ento, o cdigo percorre todos os itens na ArrayList, retendo cada um e o convertendo de um Object* para um Line*. Uma vez feita a converso, voc pode usar as coordenadas no objeto Line para desenhar as linhas. PASSO 10: Construa e execute o programa agora. Voc ver que as linhas que voc desenhar persistem mesmo que voc provoque o envio de eventos de janela que cause seu redesenho, como, por exemplo, minimiz-la ou maximiz-la.

18.1.8.

USANDO CORES

As cores so representadas pela estrutura System::Drawing::Color e so formadas por 4 componentes: R, G, B e A. As 3 primeiras representam as propores das cores vermelha, verde e azul (Red, Green, Blue) e so especificadas como um inteiro variando de 0 (nada) a 255 (mximo). Assim, a cor vermelha pura (255, 0, 0). A componente A representa a transparncia, ou o valor alfa, da cor e pode variar de 0 (completamente transparente) at 255 (completamente opaco). Como Graphics, a estrutura Color no tem um construtor. Voc cria instncias Color usando um dos trs mtodos static de fabricao: FromArgb: Cria uma Color de um conjunto de valores de alpha, red, green e blue. FromKnownColor: Cria uma Color a partir de um membro da enumerao KnownColor (discutida em breve). FromName: Cria uma cor a partir de um nome.

A classe Color define um grande nmero de propriedades que representam cores definidas pelo sistema, variando de AliceBlue at YellowGreen. A enumerao KnownColor define uma lista de cores definidas pelo sistema. Estas cores incluem as cores padres definidas em Color (como, por exemplo, AliceBlue) e as cores representando valores usados no Windows GUI, como ActiveCaption (a cor da barra de ttulo na janela ativa) e WindowsText (a cor do texto em uma janela). Os valores destas cores podem ser mudados se o usurio alterar o esquema de cores do desktop, atravs do painel de controle. 331

Jos Wilson Vieira Eis alguns exemplos mostrando como criar cores:
//Vermelha pura, completamente opaca Color c1 = Color::FromArgb(255, 255, 0, 0); //A cor padro 'Cornsilk' Color c2 = Color::FromKnownColor(KnownColor::Cornsilk); //Outro modo de obter a Cornsilk Color c3 = Color::Cornsilk;

Note que uma vez construdo o objeto Color, voc no pode mudar seus valores. Se precisar mudar uma Color, voc ter que criar uma nova Color baseada na existente.
//Vermelha pura, completamente opaca Color c4 = Color::FromArgb(255, 255, 0, 0); //Vermelha pura, semitransparente Color c5 = Color::fromArgb(127, c4.R, c4.G, c4.B);

As propriedades A, R, G e B podem ser usadas para acessar as componentes da cor.

18.1.9.

USANDO FONTES

Duas classes em System::Drawing so usadas para representar fontes: Font e FontFamily. Um objeto Font representa uma nica fonte e especifica o nome da fonte, seu tamanho e seus atributos de estilo. Uma FontFamily define um conjunto de fontes que compartilham muitas caractersticas mas que podem ter estilo de fonte diferente. A Tabela 18. lista as propriedades principais da classe Font. Note que todas elas so somente para leitura; uma vez criada, uma propriedade de Font no pode ser mudada. Tabela 18.3: Principais propriedades da classe Font.
PROPRIEDADE Bold FontFamily Height Italic Name Size SizeInPoints Strikeout Style Underline Unit DESCRIO O valor true se a fonte tiver estilo negrito. Obtm a famlia a que dada fonte pertence. Obtm a altura da fonte nas unidades atuais. O valor true se a fonte tiver estilo itlico. Obtm o nome da fonte. Obtm o tamanho da fonte em unidades atuais. Obtm o tamanho da fonte em pontos. Um ponto igual a 1/72 polegadas. O valor true se a fonte tiver estilo strikeout. Obtm as informaes do estilo para a fonte. O valor true se a fonte tiver estilo sublinhado. Obtm a unidade de medida da fonte atual. Por padro, este valor ser em pixels.

O Style composto de um ou mais valores da enumerao FontStyle, que tem os membros Bold, Italic, Regular, Strikeout e Underline.

18.1.9.1.

EXERCCIO: CRIANDO E USANDO UMA Font

332

Microsoft Visual C++ .NET Passo-a-Passo O exerccio abaixo mostra como criar e usar uma Font para desenhar texto em uma forma. Voc ir melhorar o aplicativo CppDraw de modo que com o boto esquerdo do mouse possa desenhar linhas e com o direito desenhar texto. PASSO 1: Abra o projeto CppDraw e adicione um membro Font classe Form1.
private: System::Drawing::Font* font1;

Voc precisar usar o nome completo do qualificador, ou a declarao ir conflitar com a propriedade Font da forma. PASSO 2: Crie um objeto Font no construtor Form1.
font1 = new System::Drawing::Font("Verdana", 8, FontStyle::Regular, GraphicsUnit::Millimeter);

Mais uma vez voc precisa usar o nome completo do qualificador para evitar colises de nomes. O primeiro argumento o nome da fonte, seguido pelo tamanho. O que o tamanho representa depende do ltimo argumento, que a unidade a ser usada. A enumerao GraphicsUnit contm todas as unidades vlidas, que so: Display: Usa 1/75 de polegada como a unidade de medida; Document: Usa unidades de documento (3/100 de polegada); Inch: Polegadas; Milimeter; Pixel; Point: Pontos de impressora (1/72 de polegada); World: Coordenadas do mundo real.

No exemplo, especificamos 8 mm, que permite boa visibilidade na forma. O estilo pode ser qualquer um dos valores da enumerao FontStyle, combinados com o operador OR bit-a-bit (|), se voc quiser usar mais de um deles. PASSO 3: Edite a funo Form1_MouseUp de modo que ela diferencie entre os botes esquerdo e direito do mouse.
private: System::Void Form1_MouseUp(System::Object * System::Windows::Forms::MouseEventArgs * e) { Graphics *gr = CreateGraphics(); Pen *pen1 = new Pen(Color::Black); if(e->Button == MouseButtons::Left) { //Desenha linhas p2.X = e->X; p2.Y = e->Y; gr->DrawLine(pen1, p1.X, p1.Y, p2.X, p2.Y); //Adiciona uma nova linha na lista Line *pline = new Line(); pline->p1 = p1; pline->p2 = p2; list->Add(pline); } else if(e->Button == MouseButtons::Right) sender,

333

Jos Wilson Vieira


{ //Desenha texto gr->DrawString(S"texto", font1, Brushes::Black, (float)e->X, (float)e>Y); } gr->Dispose(); }

Agora, a funo checa qual o boto do mouse disparou o evento. Se foi o esquerdo, uma linha desenhada; se o direito, uma string desenhada na posio do mouse usando a fonte que voc criou. O terceiro argumento para DrawString um Brush, que define a cor de preenchimento ou o padro para o texto. PASSO 4: Construa e execute o aplicativo. Voc obter resultados como o mostrado na Figura 18.3.

Figura 18.3: Sada atual do programa CppDraw. Note que o texto no persistente. Ao contrrio das linhas, ele desaparece quando a janela redesenhada.

18.1.9.2.

EXERCCIO: CRIANDO UMA LISTA PARA TEXTO

Neste exerccio voc criar uma ArrayList com os pontos de insero de textos para permitir o redesenho dos mesmos. PASSO 1: Abra o projeto CppDraw. Define (ou redefina) os seguintes membros de dados para a classe Form1:
static bool ArrayList Point bTeste = false; *listRetas, *listTextos; p1, p2, ptText;

PASSO 2: No construtor Form1, digite:


listRetas = new ArrayList(); listTextos = new ArrayList();

PASSO 3: Ajuste o cdigo da funo manipuladora de eventos Form1_MouseUp para:


private: System::Void Form1_MouseUp(System::Object * System::Windows::Forms::MouseEventArgs * e) sender,

334

Microsoft Visual C++ .NET Passo-a-Passo


{ Graphics *gr = CreateGraphics(); Pen *pen1 = new Pen(Color::Black); if(e->Button == MouseButtons::Left) { //Desenha linhas p2.X = e->X; p2.Y = e->Y; gr->DrawLine(pen1, p1.X, p1.Y, p2.X, p2.Y); //Adiciona uma nova linha na lista Line *pline = new Line(); pline->p1 = p1; pline->p2 = p2; listRetas->Add(pline); } else if(e->Button == MouseButtons::Right) { //Desenha texto ptText.X = e->X; ptText.Y = e->Y; gr->DrawString(S"texto", font1, Brushes::Black, (float)ptText.X, (float)ptText.Y); //Adiciona um novo ponto na lista listTextos->Add(__box(ptText)); bTeste = true; } gr->Dispose(); }

PASSO 4: Ajuste o cdigo da funo manipuladora Form1_Paint para:


private: System::Void Form1_Paint(System::Object * System::Windows::Forms::PaintEventArgs * e) { Graphics *gr = e->Graphics; Pen *pen1 = new Pen(Color::Black); //Desenha a imagem Bitmap *bmp = new Bitmap(S"ramp1.gif"); gr->DrawImage(bmp, 10, 10); for(int i = 0; i < listRetas->Count; i++) { Line *pline = dynamic_cast<Line*>(listRetas->get_Item(i)); gr->DrawLine(pen1, pline->p1.X, pline->p1.Y, pline->p2.X, pline>p2.Y); } if(bTeste) for(int i = 0; i < listTextos->Count; i++) { Point *pt = dynamic_cast<Point*>(listTextos->get_Item(i)); gr->DrawString(S"texto", font1, Brushes::Black, (float)pt->X, (float)pt->Y); } } sender,

335

Jos Wilson Vieira PASSO 5: Construa e execute o aplicativo.

18.2.

MANIPULANDO IMAGENS

Image a classe-base para todas as classes de imagem em System::Drawing, especialmente a classe Bitmap, que usada para representar e manipular imagens em diversos formatos. A classe Bitmap tem vrias propriedades e mtodos teis, que so resumidos nas Tabelas 18.4 e 18.5. Tabela 18.4: Algumas propriedades teis da classe Bitmap.
PROPRIEDADE Height HorizontalResolution Palette PixelFormat Size VerticalResolution Width DESCRIO Obtm a altura do bitmap em pixels. Obtm a resoluo horizontal em pixels/polegada. Obtm ou fixa a paleta de cores usada na imagem atual. Obtm ou fixa o valor PixelFormat que descreve o formato da cor dos pixels. Obtm a largura e a altura do bitmap como um objeto Size. Obtm a resoluo vertical em pixels/polegada. Obtm a largura do bitmap em pixels.

Tabela 18.5: Alguns mtodos teis da classe Bitmap.


MTODO FromFile FromHbitmap FromStream RotateFlip Save DESCRIO Cria um bitmap a partir de dados em um arquivo. Cria um bitmap a partir de um HBITMAP Windows. Cria um bitmap a partir de dados de um fluxo. Rotaciona ou vira a imagem. Salva a imagem em um dado formato.

Voc pode ver os formatos que so suportados observando a enumerao ImageFormat.

18.2.1.1.

EXERCCIO: MOSTRANDO UM BITMAP EM UMA FORMA

O exerccio a seguir ilustra como exibir um bitmap em uma forma. PASSO 1: Abra o projeto CppDraw. Localize a funo Form1_Paint que manipula eventos Paint. Voc precisa exibir o bitmap no manipulador de eventos Paint de modo que ele possa ser re-exibido quando a forma for redesenhada. Adicione o seguinte cdigo funo Form1_Paint:
//Desenha a imagem Bitmap *bmp = new Bitmap(S"ramp1.gif"); gr->DrawImage(bmp, 10, 10);

O cdigo cria um objeto Bitmap que l dados de um arquivo GIF. Ento, a chamada funo DrawImage desenha a imagem na posio (10, 10) na forma. A imagem que usamos uma faixa vertical colocada do lado esquerdo da forma (Figura 18.4). NOTA: Voc achar o arquivo ramp1.gif na pasta do projeto CppDraw. PASSO 2: Construa e execute o aplicativo. A Figura 18.4 mostra a atual sada.

336

Microsoft Visual C++ .NET Passo-a-Passo

Figura 18.4: Sada atual do programa CppDraw.

18.3.

IMPRESSES

O namespace System::Drawing::Printing contm as classes que implementam a funcionalidade de impresso dentro da GDI+. Imprimir no particularmente difcil, mas pode tornarse um processo bastante intrincado por causa do nmero de classes envolvidas. A primeira classe que voc encontrar quando imprimir PrintDocument. Um objeto PrintDocument representa seu vnculo com uma impressora e pode ser usado para mais de uma impressora em funcionamento. PrintDocument tem quatro propriedades principais: DefaultPageSettings: Obtm e fixa um objeto PageSettings que representa a configurao padro de pgina; DocumentName: Representa o nome do documento; PrintController: Obtm ou fixa um objeto PrintController que controla o processo de impresso; PrinterSettings: Obtm ou fixa um objeto PrinterSettings que controla onde e como o documento impresso.

As classes PrinterSettings e PageSettings suportam dados sobre a impressora a ser usada como, por exemplo, se h diferentes tipos de bandeja de papel e PageSettings tambm suporta dados sobre a configurao do documento, como, por exemplo, a orientao da pgina e o nmero de cpias.

18.3.1.1.

EXERCCIO: USANDO A FUNCIONALIDADE DE IMPRESSORA DENTRO DA GDI+

O exerccio a seguir mostra como usar a funcionalidade de impresso dentro da GDI+. Voc usar o aplicativo CppDraw e adicionar um item de menu que lhe permitir imprimir os contedos da forma. PASSO 1: Abra o projeto CppDraw e adicione uma diretiva using para o namespace Printing ao conjunto de diretivas no topo do namespace CppDraw:
using namespace System::Drawing::Printing;

337

Jos Wilson Vieira PASSO 2: Adicione um menu forma arrastando um controle MainMenu da caixa de utilitrios. Crie um menu File e adicione dois itens de menu: um chamado printItem, com legenda &Print, e o outro com nome exitItem e legenda E&xit. Coloque um separador entre printItem e exitItem. NOTA: Se precisar rever seus conhecimentos sobre configurao de menus, veja o captulo 16. PASSO 3: Use o editor de propriedades para configurar manipuladores de evento para os itens de menu Print e Exit. PASSO 4: Adicione o cdigo funo manipuladora printItem_Click.
private: System::Void printItem_Click(System::Object * sender, System::EventArgs * e) { //O objeto PrintDocument suporta as configuraes PrintDocument *pdoc = new PrintDocument(); //Cria um dilogo e o anexa ao documento PrintDialog *pd = new PrintDialog(); pd->Document = pdoc; //Mostra o dilogo if(pd->ShowDialog() == DialogResult::OK) { //Adiciona o manipulador de pgina pdoc->PrintPage += new PrintPageEventHandler(this, &Form1::PrintAPage); //Imprime a pgina pdoc->Print(); } else MessageBox::Show("Impresso cancelada", "Informao"); } private: System::Void exitItem_Click(System::Object * e) { Application::Exit(); } sender, System::EventArgs *

O cdigo do manipulador determina qual o item de menu enviou o evento. Se foi o item Print, uma caixa de dilogo comum PrintDialog exibida. Ela abrir a familiar caixa de dilogo para seleo da impressora mostrada na Figura 18.5. O propsito desta caixa de dilogo obter as informaes necessrias para o processo de impresso. Estas informaes so armazenadas no objeto PrintDocument para posterior uso. Assim, antes de exibir a PrintDialog, voc precisa criar um objeto PrintDocument e atribu-lo propriedade Document da caixa de dilogo. Ento, voc pode mostrar a PrintDialog e, quando o usurio fechar a caixa de dilogo, verifique se o valor de retorno lhe permite continuar a impresso. O objeto PrintDocument controla o processo de impresso. Assim, voc chama sua funo Print para iniciar a impresso. Tambm precisa providenciar uma funo callback que PrintDocument chamar uma vez para cada pgina a ser impressa. Adicione a callback como se adiciona um manipulador de eventos ao PrintDocument. Eis o cdigo para a funo callback:
void PrintAPage(Object *pSender, PrintPageEventArgs *pe) { Graphics *gr = pe->Graphics;

338

Microsoft Visual C++ .NET Passo-a-Passo


Pen *pen1 = new Pen(Color::Black); //Desenha a imagem Bitmap *bmp = new Bitmap(S"ramp1.gif"); gr->DrawImage(bmp, 10, 10); for(int i = 0; i < listRetas->Count; i++) { Line *pline = dynamic_cast<Line*>(listRetas->get_Item(i)); gr->DrawLine(pen1, pline->p1.X, pline->p1.Y, pline->p2.X, pline>p2.Y); } if(bTeste) for(int i = 0; i < listTextos->Count; i++) { Point *pt = dynamic_cast<Point*>(listTextos->get_Item(i)); gr->DrawString(S"texto", font1, Brushes::Black, (float)pt->X, (float)pt->Y); } }

Figura 18.5: Caixa de dilogo comum Imprimir. Note que o cdigo exatamente o mesmo do manipulador de eventos Paint. Neste caso, adotamos uma abordagem direta para impresso: Simplesmente transportamos todo o contedo da forma para a impressora. Em situaes reais, voc provavelmente ir acrescentar cabealhos e rodaps, e talvez formatar a sada de modo diferente. NOTA: O que acontece se sua sada ocupar mais de uma pgina? A funo manipuladora de pgina vai ser chamada uma vez para cada pgina e ela assim procede para manter o traado de onde voc est e o que precisa ser impresso. Se houver mais pginas a serem impressas, fixe a propriedade HasMorePages do objeto PrintPageEventArgs para true antes de retornar da funo manipuladora e ela ser chamada novamente. PASSO 5: Construa e execute o aplicativo. Desenhe algumas linhas e texto na tela, e veja o que consegue imprimir.

339

Jos Wilson Vieira 18.4. RPIDAS REFERNCIAS SOBRE O CAPTULO 18

A Tabela 18.6 mostra um resumo deste captulo. Tabela 18.6: Resumo do captulo 18.
PARA FAA ISTO Desenhar em uma Crie um objeto Graphics usando a funo CreateGraphics. forma Ex.: Graphics *gr =myForm->CreateGraphics(); Lembre-se de chamar Dispose no objeto Graphics, quando no mais precisar dele. gr->Dispose(); Desenhar linhas Crie objetos Pen e os utilize em chamadas s funes de desenho da e contornos de classe Graphics. Use as classes Pens e SystemPens para obter Pens figuras com cores padronizadas. Preencher Crie objetos Brush e os utilize em chamadas s funes de desenho figuras da classe Graphics. Use as classes Brushes e SystemBrushes para obter pincis com cores padronizadas. Usar cores Crie objetos Color e os utilize quando construir objetos Pen e Brush. Exibir imagens Crie um objeto Bitmap, passando-lhe o nome de um arquivo de imagem. Ento, use a funo DrawImage da classe Graphics para renderizar a imagem. Imprimir de um Crie um PrintDialog. Crie um PrintDocument e o atribua aplicativo propriedade Document do PrintDialog. Mostre a caixa de dilogo e verifique se ela retorna DialogResult::OK. Crie uma funo callback, anexe-a ao evento PrintPage da PrintDocument e, ento, chame a funo Print de PrintDocument para imprimir a pgina.

340

Microsoft Visual C++ .NET Passo-a-Passo 19. CAPTULO 19: TRABALHANDO COM ARQUIVOS

Neste captulo, voc aprender Como o Microsoft Windows .NET Framework realiza entrada/sada (I/O); Quais as classes que compem o namespace System::IO; Como realizar I/O de texto; Como ler e escrever arquivos; Como trabalhar com arquivos e pastas; Como realizar I/O binria.

Voc j usou a classe Console para realizar I/O para o e do console. Este captulo lhe introduzir no namespace System::IO, que contm as classes, estruturas e enumeraes que implementam o modelo Microsoft .NET de I/O. NOTA: Se conhecer algo sobre o mecanismo Java de I/O implementado no pacote java.io, voc no ter dificuldades com o .NET I/O, pois h muitas similaridades entre dos dois modelos.

19.1.

O namespace System::IO

O namespace System::IO contm todas as classes que so usadas para I/O de arquivos binrios e de texto, bem como as classes que lhe auxiliam na lida com arquivos e pastas. A Tabela 19.1 lista as principais classes no namespace. Tabela 19.1: Principais classes do namespace System::IO.
CLASSE BinaryReader BinaryWriter BufferedStream Directory DirectoryInfo File FileInfo FileStream FileSystemInfo FileSystemWatcher IOException MemoryStream Path Stream StreamReader StreamWriter StringReader StringWriter TextReader TextWriter DESCRIO L tipos de dados primitivos como valores binrios. Escreve tipos de dados primitivos como valores binrios. Uma classe de fluxo que l buffers e escreve para outro fluxo. Possui mtodos static para trabalhar com pastas (diretrios). Possui mtodos no static para trabalhar com pastas. Possui mtodos static para trabalhar com arquivos. Possui mtodos no static para trabalhar com arquivos. Uma classe que l e escreve em arquivos usando um fluxo. A classe-base abstrata para DirectoryInfo e FileInfo. Auxilia nas mudanas do sistema de arquivos e cria eventos quando estas mudanas ocorrem. A exceo lanada pelas classes no namespace System::IO. Uma classe de fluxos que l e escreve na memria. Ajuda-lhe a trabalhar com strings de pasta de um modo independente da plataforma. A classe-base abstrata para todas as classes de fluxo. Uma TextReader que l caracteres de um fluxo de bytes. Uma TextWriter que escreve caracteres para um fluxo de bytes. Uma TextReader que l de uma string. Uma TextWriter que escreve para uma string. A classe-base abstrata para StreamReader e StringReader. A classe-base abstrata para StreamWriter e StringWriter.

As classes orientadas para I/O em System::IO podem ser divididas nos seguintes grupos: 341

Jos Wilson Vieira As classes Stream: So designadas para I/O de fluxos de bytes. As classes BinaryReader e BinaryWriter: So usadas para entrada e sada dos tipos primitivos .NET, como, por exemplo, Int32 e Double, no formato binrio. As classes TextReader e TextWriter: So usadas para I/O no modo de caracteres.

Este captulo focalizar os dois ltimos grupos.

19.2.

I/O DE TEXTO USANDO LEITORES E ESCRITORES

TextReader e TextWriter so as classes-bases abstratas para um grupo de classes que so usadas para l e escrever caracteres. H quatro classes em System::IO que se derivam destas duas bases StreamReader, StreamWriter, StringReader e StringWriter justas com diversas outras classes mais especficas para escrita e leitura em outros namespaces.

19.2.1.

USANDO TextWriter

A classe TextWriter tem diversos mtodos teis como os sumarizados na Tabela 19.2. Tabela 19.2: Alguns mtodos teis da classe TextWriter.
MTODO Close Dispose DESCRIO Fecha o escritor e libera quaisquer recursos que ele usados. Libera todos os recursos no gerenciados usados pelo escritor e opcionalmente libera recursos gerenciados tambm. Flush Faz com que todos os dados de buffer sejam escritos no dispositivo subjacente. Synchronized Cria uma envoltura de enfileiramento seguro para o escritor. Write Escreve texto sem uma nova linha. WriteLine Escreve texto com uma nova linha.

Como voc pode adivinhar pela incluso das funes Write e WriteLine na Tabela 19.2, a classe Console usa um objeto TextWriter para realizar sadas. Para mostrar como as classes de I/O funcionam juntas, vamos observar o uso da classe StreamWriter. Entretanto, antes de comear, importante que voc compreenda como o .NET Framework implementa I/O. Ao invs de criar diversas classes, cada uma para realizar uma dada tarefa de I/O, o .NET implementa diversas pequenas classes de carter geral que voc pode juntar para obter o efeito desejado. Assim, o .NET no tem uma classe para escrever caracteres em um arquivo e uma para escrever caracteres na tela. Ao invs disto, ele tem uma classe para escrever caracteres para um fluxo de bytes e uma classe para ler bytes de um fluxo e escrev-los para um arquivo. Se voc usar a sada da primeira classe como entrada da segunda, acabar escrevendo caracteres para um arquivo. Este modelo flexvel porque voc pode tomar dados binrios ou de caracteres, convert-los em bytes e, ento, passar os bytes para qualquer uma das diversas classes de sada para arquivos, memria ou para uma string. Os dados so transferidos entre as classes como fluxos de bytes, um mtodo que fornece uma base flexvel para construo. A funcionalidade bsica para manipular fluxos de bytes fornecida pela classe Stream e voc pode construir suas classes de I/O especializadas no topo de Stream, se precisar. 342

Microsoft Visual C++ .NET Passo-a-Passo


19.2.1.1. EXERCCIO: ESCREVENDO DADOS DE CARACTERES PARA UM ARQUIVO DE TEXTO

Com estas informaes em mente, o exerccio abaixo lhe mostrar como escrever dados de caracteres para um arquivo de texto usando uma TextWriter. Usando o modelo Plug and Play para I/O que o .NET Framework dispe, voc precisa criar os seguintes objetos: Um objeto FileStream para receber bytes como entrada e escrev-los para um arquivo; Um objeto StreamWriter que recebe texto e o converte para um fluxo de bytes.

Eis o exerccio: PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado CppWriter. PASSO 2: A TextWriter e as classes de I/O de arquivos so partes do namespace System::IO. Assim, inclua a declarao using no incio do arquivo CppWriter.cpp:
using namespace System::IO;

PASSO 3: Na funo _tmain, crie um objeto FileStream para escrever bytes para um arquivo:
//Cria um FileStream try { FileStream *fs = new FileStream(S"output.txt", FileMode::Create); } catch(System::Exception *pe) { Console::WriteLine(pe->ToString()); }

O construtor FileStream recebe um nome de arquivo e um modo. Neste caso, o arquivo vai ser criado se ainda no existir ou sobrescrito, caso j exista. Usamos output.txt como o nome do arquivo, mas voc pode especificar qualquer caminho e nome de arquivo que desejar. NOTA: Veja a seo A CLASSE FileStream no final deste captulo para mais detalhes sobre como construir objetos FileStream. O cdigo est dentro de um bloco try porque muitas coisas podem no funcionar na hora de abrir um arquivo. PASSO 4: Crie um objeto StreamWriter que use o FileStream:
try { //Cria um FileStream FileStream *fs = new FileStream(S"output.txt", FileMode::Create); //Cria um StreamWriter StreamWriter *sw = new StreamWriter(fs); } catch(System::Exception *pe) { Console::WriteLine(pe->ToString()); }

O construtor StreamWriter recebe um ponteiro para um objeto Stream como argumento. PASSO 5: Agora, voc pode usar as funes Write e WriteLine para colocar o texto de sada no arquivo. Coloque as seguintes linhas dentro do bloco try: 343

Jos Wilson Vieira


//Escreve algum texto sw->WriteLine(S"Primeira linha"); sw->WriteLine(S"Segunda linha"); sw->WriteLine(S"Terceira linha");

PASSO 6: Depois de colocar tudo que voc quiser que saia no arquivo, feche o fluxo.
//Fecha o arquivo sw->Flush(); sw->Close();

NOTA: WriteLine executa sada de buffer, o que significa que ela necessariamente no escrever linhas para o arquivo toda vez que voc a chama. Ao invs disto, ela mantm um buffer interno e escreve este buffer para o disco, quando necessrio. Um acesso de disco por buffer mais eficiente do que escrever linhas individuais, mas voc precisa chamar a funo Flush no final do cdigo para garantir que o que estiver atualmente no buffer seja transferido para o arquivo. PASSO 7: Construa e execute o aplicativo. O arquivo de texto chamado output.txt dever aparecer na pasta do projeto CppWriter. O arquivo contm as trs linhas de texto escritas pelo aplicativo CppWriter.

19.2.2.

A CLASSE FileStream

FileStream usada para passar bytes de alguma outra classe tais como StreamWriter para um arquivo. H vrios construtores sobrecarregados para esta classe que lhe permitem especificar combinaes com: O nome do arquivo; O modo do arquivo, que determina como o arquivo vai ser aberto; O tipo de acesso requerido; As opes compartilhadas.

O modo do arquivo representado pelos membros da enumerao FileMode, listados na Tabela 19.3. O acesso representado pelos membros da enumerao FileAccess, listados na Tabela 19.4. Tabela 19.3: Membros da enumerao FileMode.
MEMBRO Append DESCRIO Abre um arquivo existente ou cria um novo arquivo e anexa texto no fim. Create Cria um novo arquivo ou abre um existente, sobrescrevendo-o. CreateNew Cria um novo arquivo, lanando uma exceo se o arquivo j existir. Open Abre um arquivo existente. OpenOrCreate Abre um arquivo existente ou cria um novo. Truncate Abre um arquivo existente e trunca seu tamanho para 0 byte. Uma exceo ser lanada se o arquivo no existir.

Tabela 19.4: Membros da enumerao FileAccess.


MEMBRO Read ReadWrite Write DESCRIO Representa acesso para leitura. Representa acesso para leitura/escrita. Representa acesso para escrita.

344

Microsoft Visual C++ .NET Passo-a-Passo Similarmente, o acesso compartilhado especificado pela enumerao FileShare, cujos membros so listados na Tabela 19.5. Tabela 19.5: Membros da enumerao FileShare.
MEMBRO None Read ReadWrite Write DESCRIO Nenhum compartilhamento. Representa acesso para leitura compartilhada. Representa acesso para leitura/escrita compartilhas. Representa acesso para escrita compartilhada.

O exemplo abaixo mostra como construir um objeto FileStream usando estas permisses:
FileStream *fs2 = new FileStream( S"foo.txt", FileMode::Create, FileAccess::ReadWrite, FileShare::Read); //O nome do arquivo //Cria ou sobrescreve //Requer acesso para leitura/escrita //Permite leitura compartilhada

NOTA: Embora usualmente v utilizar a classe FileStream junta com outras classes escritoras, voc pode usar seus mtodos Read e Write para entrada e sada diretas de bytes.

19.2.3.

USANDO TextReader

A estrutura e o modo de operao da classe TextReader se compara aos de TextWriter. A Tabela 19.6 lista os mtodos fornecidos por TextReader. Tabela 19.6: Mtodos da classe TextReader.
MTODO Close Dispose DESCRIO Fecha o leitor e libera quaisquer recursos por ele usados. Libera todos os recursos no gerenciados usados pelo leitor e, opcionalmente, libera recursos gerenciados tambm. Peek Retorna o prximo caractere do fluxo de entrada sem remov-lo. Read L um ou mais caracteres do fluxo de entrada. ReadBlock L um bloco de caracteres. ReadLine L uma linha. ReadToEnd L o fim do fluxo de entrada. Synchronized fornece uma envoltura de enfileiramento seguro para objetos TextReader.

Como no caso do TextWriter, voc usar TextReader conectando um leitor a um objeto que vai atuar como uma fonte de bytes. H vrios destes, incluindo o que voc j encontrou, o FileStream.

19.2.3.1.

EXERCCIO: PROGRAMA SIMILAR AO UNIX more

O exerccio abaixo lhe mostrar como escrever um programa similar em funcionalidade ao comando UNIX more, que ir ler um arquivo e exibir seus contedos na tela, algumas linhas de cada vez. Depois de mostrar algumas linhas, o usurio deve escolher uma opo: pressionar a tecla Enter para continuar ou Q para sair do programa. 345

Jos Wilson Vieira PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado CppReader. PASSO 2: Inclua a declarao using para o namespace System::IO no topo do arquivo CppReader.cpp.
using namespace System::IO;

PASSO 3: Como o usurio vai entrar com o nome do arquivo a ser listado na linha de comando, mude a declarao da funo _tmain para incluir os argumentos dos parmetros para a linha de comando:
int _tmain(int argc, char *argv[])

NOTA: Se at aqui voc no tinha lidado com os argumentos de linhas de comando em um programa C ou C++, saiba que a funo _tmain pode, opcionalmente, recebe dois argumentos. O primeiro tradicionalmente chamado argc o nmero de argumentos da linha de comando; e o segundo tradicionalmente chamado argv um vetor de strings. PASSO 4: Adicione cdigo para _tmain forar o usurio a entrar com um nome de arquivo.
//Cheque para requerer os argumentos if(argc < 2) { Console::WriteLine(S"Use: CppReader [caminho]"); return 0; } String *path = new String(argv[1]);

Se o usurio no passar dois argumentos, uma mensagem de erro impressa no console e o programa termina; se passar, o segundo argumento salvo para uso posterior. PASSO 5: prudente conferir se o caminho representa um arquivo existente antes de continuar. Assim, adicione o cdigo:
if(!File::Exists(path)) { Console::WriteLine(S"Nome de arquivo invlido!"); return -1; }

O mtodo File::Exists checa se o arquivo com o nome especificado existe, retornando false se no existir. Tambm retornar false se voc passar um nome de pasta ao invs de nome de arquivo. Note o valor de retorno igual a 1. uma conveno comum para aplicativos C/C++ retornar 0 para indicar sucesso e valores negativos para indicar condies de erro. PASSO 6: Comece a listar o arquivo. O primeiro passo criar um FileStream e, ento, conect-lo a um StreamReader:
try { FileStream *fs = new FileStream(path, FileMode::Open); StreamReader *sr = new StreamReader(fs); } catch(System::Exception *pe) { Console::WriteLine(pe->ToString()); }

Neste caso, voc est abrindo o arquivo usando FileMode::Open, que lanar uma exceo se o arquivo no existir.

346

Microsoft Visual C++ .NET Passo-a-Passo PASSO 7: A listagem do arquivo feita no lao abaixo, que voc deve colocar aps a criao do objeto StreamReader:
int count = 0; for(;;) { String *line = sr->ReadLine(); count++; //Se no houver mais linhas, sair do lao. if(line == 0) break; Console::WriteLine(line); if(!(count % 20))//Se count for divisvel por 20 { Console::Write(S"--Mais--"); String *resposta = Console::ReadLine(); if(resposta->Equals(S"q")) break; count = 0; } } Console::WriteLine("--Fim--");

A varivel count vai ser usada para contar as linhas quando elas so lidas de modo que o programa saiba onde parar. O lao l uma linha em uma String usando a funo ReadLine de StreamReader. Se no houver mais linhas para serem lidas, um ponteiro null ser retornado de ReadLine. Ento, a linha impressa no console e o contador checado. Fixamos o nmero de linhas mostradas de cada vez para um valor arbitrrio 20. Quando o contador for divisvel por 20, o programa escreve Mais no console e espera uma ao do usurio. Se o usurio pressionar a tecla q minscula, o programa pra; caso contrrio, ele segue para o prximo conjunto de 20 linhas. PASSO 8: Construa e execute o programa, passando-lhe o nome de um arquivo de texto vlido (ver exemplo da Figura 19.1, com o CppReader.cpp) como argumento.

Figura 19.1: Executando o programa CppReader. 19.3. TRABALHANDO COM ARQUIVOS E PASTAS

O namespace System::IO contm vrias classes para manipulao de arquivos e pastas. 347

Jos Wilson Vieira 19.3.1. OBTENDO INFORMAES SOBRE ARQUIVOS E PASTAS As classes Directory e DirectoryInfo fornecem funes que lhe ajudam a trabalhar com pastas. A diferena entre elas que a classe Directory contm somente mtodo static, enquanto a DirectoryInfo contm mtodos de instncia no static. Qual a necessidade de duas classes diferentes? Para que o .NET realize checagens de segurana antes de lhe permitir acesso a uma pasta ou a um arquivo. A classe Directory faz este cheque toda vez que voc usa um dos seus mtodos static, o que pode consumir algum tambm. Por outro lado, os objetos da classe DirectoryInfo funcionam com uma pasta e a checagem de segurana feita uma vez, quando o objeto construdo. Portanto, pode ser mais eficiente usar DirectoryInfo se voc vai realizar mltiplas operaes em uma pasta. A Tabela 19.7 lista os principais mtodos da classe Directory. Tabela 19.7: Principais mtodos da classe Directory.
MTODO CreateDirectory Delete Exists GetCreationTime GetCurrentDirectory DESCRIO Cria uma pasta. Deleta uma pasta e, opcionalmente, suas sub-pastas. Checa se uma pasta existe. Obtm o tempo de criao de uma pasta. Retorna uma string representando o caminho para a pasta atual do aplicativo. GetDirectories Obtm um vetor de strings representando os nomes das subpastas em uma dada pasta. GetDirectoryRoot Retorna a poro da raiz de um caminho. GetFiles Obtm um vetor de strings representando os nomes dos arquivos em uma dada pasta. GetFileSystemEntries Obtm um vetor de strings representando os nomes dos arquivos e pastas em uma dada pasta. GetLastAccessTime Obtm o ltimo tempo de acesso para uma pasta. GetLastWriteTime Obtm o ltimo tempo de escrita para a pasta. GetLogicalDrives Obtm uma lista dos drives lgicos no computador. GetParent Obtm a pasta-me de uma pasta especfica. Move Move uma pasta e seus contedos. SetCreationTime Fixa o tempo de criao para uma pasta. SetCurrentDirectory Fixa a pasta atual do aplicativo. SetLastAccessTime Fixa o ltimo tempo de acesso para a pasta. SetLastWriteTime Fixa o ltimo tempo de escrita para a pasta.

As Tabelas 19.8 e 19.9 listam as propriedades e mtodos da classe DirectoryInfo. Tabela 19.8: Propriedades da classe DirectoryInfo.
PROPRIEDADE Attibutes CriationTime Exists Extension FullName LastAccessTime LastWriteTime Name Parent Root DESCRIO Obtm ou fixa o FileAttributes para a pasta. Obtm ou fixa o tempo de criao para a pasta. O valor true se o caminho da pasta existir. Obtm a extenso que faz parte do nome da pasta. Obtm o caminho completo da pasta. Obtm ou fixa o tempo em que a pasta foi acessada pela ltima vez. Obtm ou fixa o tempo em que se escreveu na pasta pela ltima vez. Representa o nome da pasta. Obtm um objeto DirectoryInfo representando a me a pasta atual. Obtm um objeto DirectoryInfo representando a raiz que faz parte do caminho de uma pasta.

348

Microsoft Visual C++ .NET Passo-a-Passo Tabela 19.9: Mtodos da classe DirectoryInfo.
MTODO Create CreateSubdirectory Delete GetDirectories GetFiles GetFileSystemInfos MoveTo ToString DESCRIO Cria uma pasta. Cria uma ou mais sub-pastas. Deleta uma pasta e seus contedos. Obtm um vetor de objetos DirectoryInfo representando as subpastas da pasta atual. Obtm um vetor de objetos FileInfo representando os arquivos na pasta atual. Obtm um vetor de objetos FileSystemInfo representando as pastas e arquivos na pasta atual. Move a pasta e seus contedos. Retorna o caminho completamente qualificado como uma string.

Duas classes, File e FileInfo, so usada para trabalhar com arquivos. Como no caso das classes Directory e DirectoryInfo discutidas acima, File contm mtodos static e FileInfo mtodos de instncia no static. A Tabela 19.10 lista os mtodos fornecidos pela classe File. Tabela 19.10: Mtodos da classe File.
MTODO AppendText Copy Create Delete Exists GetAttributes GetCreationTime GetLastAcessTime GetLastWriteTime Move DESCRIO Anexa texto a um arquivo, criando-o se ele no existir. Copia um arquivo. Cria um novo arquivo. Deleta um arquivo. Retorna true se um dado arquivo existir. Retorna os atributos do arquivo. Retorna o tempo de criao do arquivo. Retorna o tempo de acesso do ltimo arquivo. Retorna o tempo de escrita do ltimo arquivo. Move um arquivo para uma nova localizao, com a opo de renome-lo. Open Abre um FileStream para acesso a leitura/escrita para um arquivo. OpenRead Abre um FileStream para acesso a um arquivo somente para leitura. OpenText Abre um FileStream para ler de um arquivo de texto. OpenWrite Abre um FileStream para acesso a leitura/escrita para um arquivo. SetAttributes Fixa os atributos do arquivo. SetCreationTime Fixa o tempo de criao do arquivo. SetLastAccessTime Fixa o ltimo tempo de acesso do arquivo. SetLastWriteTime Fixa o ltimo tempo de escrita do arquivo.

As Tabelas 19.11 e 19.12 listam as propriedades e os mtodos da classe FileInfo. Tabela 19.11: Propriedades da classe FileInfo.
PROPRIEDADE Directory DESCRIO Retorna um objeto DirectoryInfo representando a pasta-me arquivo. DirectoryName Retorna uma string representando o caminho completo do arquivo. Exists Retorna true se o arquivo existir. Length Retorna o tamanho do arquivo em bytes. Name Retorna o nome do arquivo. do

349

Jos Wilson Vieira Tabela 19.12: Mtodos da classe FileInfo.


MTODO AppendText CopyTo Create CreateText Delete MoveTo Open OpenRead OpenText OpenWrite ToString DESCRIO Cria um StreamWriter para anexar texto a um arquivo. Copia um arquivo para outra localizao. Cria um novo arquivo e um FileStream para escrever para ele. Cria um StreamWriter para escrever para um novo arquivo de texto. Deleta um arquivo. Move um arquivo para uma nova localizao. Retorna um FileStream com um rtulo especificado para acessar um arquivo. Retorna um FileStream com acesso de leitura para um arquivo. Cria um StreamReader para ler de um arquivo existente. Retorna um FileStream com acesso de leitura/escrita para um arquivo. Retorna um caminho de arquivo como uma string.

19.3.1.1.

EXERCCIO: USANDO AS CLASSES DE MANIPULAO DE PASTAS E ARQUIVOS

O exerccio abaixo ilustra o uso das classes de pastas e arquivos. Voc construir um programa de listagem de pasta similar em funcionalidade ao comando dir do MS-DOS. Eis como ele funciona: Se o caminho representa um arquivo, os detalhes do arquivo sero impressos; Se o caminho representa uma pasta, os contedos da pasta sero listados; Alm do nome, o usurio pode optar por exibir o tamanho, a data da ltima modificao e os atributos. S para pastas, aplica a ltima data de modificao.

PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado CppFiles. PASSO 2: Como todas as classes de arquivo e pasta esto no namespace System::IO, inclua uma declarao using no incio do arquivo-fonte CppFiles.cpp:
using namespace System::IO;

PASSO 3: Para entrar com as opes e um caminho pela linha de comando, edite _tmain para incluir os parmetros de argumento de linha de comando, como no exerccio sobre o aplicativo CppReader:
int _tmain(int argc, char* argv[])

PASSO 4: O usurio pode chamar o programa com um caminho ou com opes mais um caminho. Adicione o cdigo abaixo funo _tmain:
//Checa os argumentos requeridos if(argc < 2) { Console::WriteLine(S"Use: CppFiles [opes] [caminho]"); return 0; }

O vetor de argumentos de linha de comando inclui tudo na linha de comando. Assim, o primeiro item sempre ser o nome do programa. Portanto sempre teremos, pelo menos, dois argumentos, se queremos que o usurio inclua um caminho. PASSO 5: Se o usurio especificar opes, precisamos descobrir o que so elas. Cada opo especificada por uma nica letra e o conjunto de opes escolhido representado por uma string de letras. As opes suportadas neste programa so: 350 s: Para o tamanho;

Microsoft Visual C++ .NET Passo-a-Passo d: Para a data da ltima modificao, e a: para atributos.

No importa qual a ordem em que as opes so digitadas. Assim, as e as significam, ambas, imprimir o tamanho e os atributos. Eis o cdigo que checa os argumentos e salva o caminho e as opes que o usurio tiver especificado:
//Separa os argumentos if(argc == 3) { bGotOptions = true; options = new String(argv[1]); path = new String(argv[2]); } else if(argc == 2) path = new String(argv[1]);

Se houve trs argumentos de linha de comando, o cdigo acima interpreta que h uma string de opo. PASSO 6: Cheque quais as opes que o usurio selecionou, usando o cdigo:
//Se houve opes, devemos chec-las. O padro listar apenas o nome. //As opes possveis so: // v Listagem verbal: nome, tamanho e tempo de acesso // s Lista o tamanho // d Data do ltimo acesso // a Atributos if(bGotOptions) { options = options->ToLower(); if(options->IndexOf('v') != -1) { bSize = true; bDate = true; bAtts = true; } else { if(options->IndexOf('s') != -1) bSize = true; if(options->IndexOf('d') != -1) bDate = true; if(options->IndexOf('a') != -1) bAtts = true; } }

Trs variveis booleanas representam as opes escolhidas. Se v for digitado, todas as opes so true. Caso contrrio, as letras opcionais so checadas individualmente e cada varivel booleanas adequadamente fixada. PASSO 7: Determine se o caminho que foi digitado um arquivo ou uma pasta. Eis o cdigo:
//Checa se o usurio entrou com um arquivo ou uma pasta bool bItsAFile = false; bool bItsADirectory = false; FileInfo *fi = new FileInfo(path); DirectoryInfo *di = new DirectoryInfo(path); if(fi->Exists)

351

Jos Wilson Vieira


bItsAFile = true; else if(di->Exists) bItsADirectory = true; if(!bItsAFile && !bItsADirectory) { Console::WriteLine(S"O caminho digitado nem arquivo nem pasta"); return -1; }

No to direto como voc poderia supor checar se o usurio inseriu um caminho para um arquivo ou uma pasta. Voc tem que usar a propriedade Exists das classes FileInfo e DirectoryInfo para checar se o caminho para um arquivo ou pasta. Se for para um arquivo, Exists retornar true para FileInfo e false para DirectoryInfo, e vice-versa se for para uma pasta. Tanto bItsAFile quanto bItsADirectory pode ser fixado. Se nenhum deles for, o caminho especificado na linha de comando no foi encontrado; o aplicativo mostra uma mensagem e finaliza, retornando 1 como um cdigo de erro. PASSO 8: Agora que sabe como ordenar o objeto que tem e quais as opes que o usurio dispe, voc pode imprimir os detalhes. Primeiramente, vamos imprimir os detalhes para um nico arquivo.
//Processa arquivos if(bItsAFile) { Console::Write(fi->Name); if(bSize) Console::Write(S" {0}", __box(fi->Length)); if(bDate) Console::Write(S" {0}", File::GetLastAccessTime(fi>ToString()).ToString()); if(bAtts) { FileAttributes fa = File::GetAttributes(fi->ToString()); Console::Write(S" "); if(fa & FileAttributes::Normal) Console::Write(S"<normal>"); else { if(fa & FileAttributes::Archive) Console::Write(S"a"); if(fa & FileAttributes::Hidden) Console::Write(S"h"); if(fa & FileAttributes::System) Console::Write(S"s"); if(fa & FileAttributes::ReadOnly) Console::Write(S"r"); } } Console::WriteLine(); }

O programa imprime o nome do arquivo e, ento, mostra outras informaes com base nas opes requeridas pelo usurio. A propriedade Length precisa ser encaixotada para que se possa imprimi-la usando Write. Voc tem que obter o tempo do ltimo acesso usando um dos mtodos static da classe File, que recebe o caminho do arquivo como um argumento. O modo mais fcil de obter um caminho usar o mtodo ToString de FileInfo. Se o usurio tiver requerido atributos, use o membro static GetAttributes da classe File para obter um objeto FileAttributes e, ento, use o operador bit-a-bit AND (&) para compar-lo 352

Microsoft Visual C++ .NET Passo-a-Passo com as vrias permisses definidas em FileAttributes. O cdigo acima checa apenas quatro atributos, mas fcil estend-lo para realizar mais checagens. PASSO 9: Se o usurio entrar com um caminho de pasta, liste os contedos da pasta. O cdigo abaixo lista as sub-pastas e, na seqncia, os arquivos. Os nomes das pastas so listados com letras maisculas e os dos arquivos com minsculas. Obviamente, voc pode mudar o cdigo para mostrar itens de acordo com o seu desejo. Vamos comear listando as sub-pastas.
else if(bItsADirectory) { //Lista os contedos da pasta - sub-pastas seguidas dos arquivos String *dirs[] = Directory::GetDirectories(di->ToString()); for(int i = 0; i < dirs->Count; i++) { DirectoryInfo *sdi = new DirectoryInfo(dirs->get_Item(i)->ToString()); //Lista as pastas com letras maisculas String *dirName = sdi->Name->ToUpper(); Console::Write(S"{0,30}", dirName); //Nenhum tamanho. Assim, coloque alguns espaos em branco String *ss = S"--"; Console::Write(S"{0,12}", ss); //ltima data OK if(bDate) Console::Write(S" {0}", Directory::GetLastAccessTime(sdi>ToString()).ToString()); //Nenhum atributo. //Fim de linha Console::WriteLine(); } }

A funo Directory::GetDirectories retorna um vetor de strings representando os nomes das sub-pastas na pasta atual. Como este vetor retornado .NET, voc pode usar a propriedade Count para determinar quantos elementos ele tem. Para cada elemento do vetor, voc criar um objeto DirectoryInfo usando o nome de caminho retornado quando ToString chamada no elemento. As letras do nome so convertidas para maisculas e, ento, impressas no console. Note o uso de uma largura de campo na declarao de Write: os especificadores de formato podem receber um campo de largura adicional aps o nmero do campo. Se este valor for positivo, o item justificado direita no campo; se for negativo, esquerda. Uma largura de campo de 30 caracteres deve ser suficiente para os nomes de pasta. As pastas no tm um tamanho. Assim, colocamos dois hfens como o tamanho do campo. Elas guardam o tempo do ltimo acesso, que pode ser obtido da classe Directory. PASSO 10: Processe as entradas de arquivo. O cdigo abaixo segue o mesmo padro usado para as sub-pastas, mas tambm incluem alguns dos cdigos para o caso que envolve um nico arquivo.
//Agora faz os arquivos String *files[] = Directory::GetFiles(di->ToString()); for(i = 0; i < files->Count; i++) { FileInfo *fci = new FileInfo(files->get_Item(i)->ToString()); //Lista os arquivos com letras minsculas String *fileName = fci->Name->ToLower(); Console::Write(S"{0,30}", fileName); if(bSize) Console::Write(S"{0,12}", __box(fci->Length)); if(bDate) Console::Write(S" {0}", File::GetLastAccessTime(fci>ToString()).ToString());

353

Jos Wilson Vieira


//Atributos if(bAtts) { FileAttributes fa = File::GetAttributes(fci->ToString()); Console::Write(S" "); if(fa & FileAttributes::Normal) Console::Write(S"<normal>"); else { if(fa & FileAttributes::Archive) Console::Write(S"a"); if(fa & FileAttributes::Hidden) Console::Write(S"h"); if(fa & FileAttributes::System) Console::Write(S"s"); if(fa & FileAttributes::ReadOnly) Console::Write(S"r"); } } //Fim de linha Console::WriteLine(); }

O mtodo static Directory::GetFiles retorna um vetor de strings representando os arquivos na pasta atual. Como antes, voc constri um objeto para representar cada arquivo e, ento, examinlo. E faz isto exatamente como no caso anterior para um nico arquivo. PASSO 11: Construa o aplicativo, abra uma janela console, muda da pasta padro para a pasta Debug do projeto e execute o programa com uma linha de comando aceitvel, como, por exemplo,
CppFiles v C:\Trabalho\CEFET\Graduacao\TRadiologia

A Figura 19.2 mostra a sada do CppFiles.

Figura 19.2: Prompt de comando mostrando o programa CppFiles. DICA: Se quiser executar o programa pelo depurador do Microsoft Visual Studio .NET, voc precisar fornecer argumentos de linha de comando ao aplicativo. Para fazer isto, clique com o boto direito do mouse sobre o nome do projeto no Solution Explorer, clique em Properties para abrir a caixa de dilogo Properties Pages para o projeto, selecione a operao Debugging em 354

Microsoft Visual C++ .NET Passo-a-Passo Configuration Properties e digite os argumentos no controle de edio Command Arguments. Agora, voc executa o programa no modo debug.

19.4.

I/O BINRIA

A I/O binria no .NET Framework usa as classes BinaryReader e BinaryWriter, que lem e escrevem tipos primitivos .NET no formato binrio. Como nas classes TextReader e TextWriter, as classes de I/O binrias usam um objeto Stream subjacente para fornecer um fluxo de bytes. Tanto BinaryReader quanto BinaryWriter tm uma propriedade BaseStream que permite acesso ao Stream subjacente.

19.4.1.

A CLASSE BinaryWriter

A Tabela 19.13 lista os mtodos fornecidos pela classe BinaryWriter. Tabela 19.13: Mtodos da classe BinaryWriter.
MTODO Close Dispose DESCRIO Fecha o escritor e o fluxo subjacente. Libera todos os recursos no gerenciados usados pelo escritor e, opcionalmente, libera recursos gerenciados tambm. Flush Faz com que todos os dados de buffer sejam escritos para o dispositivo subjacente. Seek Fixa a posio do investigador dentro do fluxo subjacente. Write Escreve um valor para o fluxo. Write7BitEncodeInt Escreve um inteiro de 32 bits em um formato compactado.

Se pesquisar na documentao do Visual Studio .NET, voc ver que a funo Write tem pelo menos 18 sobrecargas para lhe ajudar a escrever os diversos tipos bsicos fornecidos pelo .NET Framework. Como nem todos os tipos fornecidos pelo .NET Framework so compatveis com a CLS (especificao de linguagem comum), voc precisa ter cuidado quando usar alguns destes mtodos Write se pretende que os dados sejam lidos de cdigo escritos em outras linguagens .NET. NOTA: A CLS define tipos que todas as linguagens .NET devem suportar. Os tipos byte com sinal e inteiro sem sinal no so includos na CLS. Assim, eles podem no ser utilizveis em algumas linguagens. A mais importante destas a Microsoft Visual Basic .NET, que no suporta tipos no compatveis com a CLS.

19.4.2.

A CLASSE BinaryReader

A Tabela 19.14 descreve as funes fornecidas pela classe BinaryReader. Ao contrrio de BinaryWriter, BinaryReader fornece funes separadas para ler cada um dos tipos bsicos.

355

Jos Wilson Vieira Tabela 19.14: Mtodos da classe BinaryReader.


MTODO Close Dispose DESCRIO Fecha o escrito e o fluxo subjacente. Libera todos os recursos no gerenciados usados pelo escritor e, opcionalmente, libera recursos gerenciados tambm. FillBuffer Preenche o buffer interno com um nmero de bytes lido do fluxo subjacente. PeekChar Ler o prximo caractere mas no avana o ponteiro de procura. Read Ler um ou mais bytes ou caracteres do fluxo. Read7BitEncodedInt Ler um inteiro de 32 bits que foi escrito em um formato compactado. ReadBoolean Ler um booleano do fluxo. ReadByte, ReadBytes Ler um ou mais bytes do fluxo. ReadChar, ReadChars Ler um ou mais caracteres do fluxo. ReadDecimal Ler um valor decimal do fluxo. ReadDouble, ReadSingle Ler um valor ponto-flutuante com preciso dupla ou simples do fluxo. ReadInt16, ReadInt32, Ler um tipo inteiro do fluxo. ReadInt64 ReadSByte Ler um byte com sinal do fluxo. No compatvel com a CLS. ReadString Ler uma string do fluxo. ReadUInt16, ReadUInt32, Ler um tipo inteiro sem sinal do fluxo. No compatvel ReadUInt64 com a CLS.

19.4.2.1.

EXERCCIO: USANDO AS CLASSES BinaryReader E BinaryWriter

O exerccio a seguir mostra como usar as classes BinaryReader e BinaryWriter para escrever dados binrios para um arquivo e l-lo de volta. O exerccio utiliza uma classe, Customer, que representa um cliente de banco com um nome, um nmero de conta e um dbito atual. O programa escreve detalhes do cliente para um arquivo em binrio e os l de volta. PASSO 1: Crie um novo projeto Visual C++ Console Application (.NET) chamado CppBinRead. PASSO 2: Adicione a declarao using para System::IO no incio do arquivo-fonte CppBinRead.cpp:
using namespace System::IO;

PASSO 3: adicione uma nova definio de classe antes da funo _tmain:


__gc class Customer { String *name; long accNo; double balance; public: Customer():name(0), accNo(0), balance(0.0)//Construtores { } Customer(String *s, long l, double b):name(s), accNo(l), balance(b) { } //Escreve dados de objeto para um BinaryWriter

356

Microsoft Visual C++ .NET Passo-a-Passo


void Write(BinaryWriter *bw) { bw->Write(name); bw->Write(accNo); bw->Write(balance); } //L dados de objeto de um BinaryReader void Read(BinaryReader *br) { name = br->ReadString(); accNo = br->ReadInt32(); balance = br->ReadDouble(); } //Propriedades para obter variveis de instncia __property String* get_Name() { return name; } __property long get_Account() { return accNo; } __property double get_Balance() { return balance; } };

A classe tem trs membros de dados: uma String para o nome, um long para o nmero da conta e um double para o balano. H construtores para criar objetos padres e objetos completamente preenchidos; tambm h um conjunto de propriedades somente para leitura para permitir acesso aos membros de dados. As funes Read e Write usam objetos BinaryReader e BinaryWriter para ler e escrever o estado do objeto em formato binrio. PASSO 4: Edite a funo _tmain de modo que ela use parmetros de argumento de linha de comando, como abaixo:
int _tmain(int argc, char* argv[])

PASSO 5: Adicione o cdigo abaixo funo _tmain para checar o que o usurio passa em um nome de arquivo e salvar o caminho como uma string:
//Checa os argumentos if(argc < 2) { Console::Write(S"Use: CppBinRead Caminho"); return -1; } //Salva o caminho String *path = new String(argv[1]);

Este cdigo similar aos que manipulam argumentos, j utilizados em exerccios deste captulo. Note que, por simplicidade, no checamos a validade do caminho, mas fcil e aconselhvel adicionar tal checagem em um aplicativo real. PASSO 6: Crie alguns objetos Customer:
//Cria alguns objetos Customer

357

Jos Wilson Vieira


Customer *c1 = new Customer(S"Jos Wilson", 1234567, 100.0); Customer *c2 = new Customer(S"Maria", 2345678, 1000.0); Customer *c3 = new Customer(S"Joo", 34567879, 5000.0);

PASSO 7: Para escrever os objetos, voc precisa de um BinaryWriter e um FileStream para construir a sada para o arquivo.
try { //Cria um FileStream FileStream *fstrm = new FileStream(path, FileMode::Create, FileAccess::ReadWrite); //Cria um binaryWriter para usar o FileStream BinaryWriter *binw = new BinaryWriter(fstrm); } catch(System::Exception *pe) { Console::WriteLine(pe->ToString()); }

O FileStream ir escrever para um arquivo, criando-o se necessrio, e o arquivo ser aberto com acesso leitura/escrita porque voc ir ler a partir dele mais adiante no programa. Mais uma vez, boa prtica colocar o cdigo de criao da classe de I/O em um bloco try para se prevenir contra qualquer problema que possa ocorrer. PASSO 8: Escrever os dados do objeto para o arquivo simplesmente fazer uma chamada funo Write, passando-lhe um ponteiro para o BinaryWriter. Adicione o cdigo abaixo no final do bloco try:
//Escreve os clientes para o arquivo c1->Write(binw); c2->Write(binw); c3->Write(binw);

PASSO 9: Como o arquivo foi aberto com acesso leitura/escrita, voc agora pode ler a partir dele. Para fazer isto, crie um objeto BinaryReader e anexe-o ao mesmo FileStream:
//Cria um BinaryReader que l do mesmo FileStream BinaryReader *binr = new BinaryReader(fstrm);

PASSO 10: Antes que possa ler de um arquivo no qual escreveu, voc tem que mover a posio do ponteiro de busca.
//Desloca o ponteiro de busca para o comeo binr->BaseStream->Seek(0, SeekOrigin::Begin);

Note que este cdigo usa a propriedade BaseStream e seu ponteiro de busca associado para chegar no objeto Stream subjacente. Se voc ainda no trabalhou com ponteiros de busca, veja o texto a seguir. FLUXOS E PONTEIROS DE BUSCA Todo fluxo no .NET tem um ponteiro de busca associado a ele, que representa a posio no fluxo na qual a prxima operao de leitura ou escrita acontecer. Este ponteiro automaticamente reposicionado quando voc usa os mtodos da classe Stream para ler ou escrever o fluxo, mas tambm possvel voc mesmo desloc-lo quando necessrio (e se souber o que est fazendo). Um provvel instante no qual precisar deslocar o ponteiro quando voc abre um fluxo para acesso leitura/escrita. Uma vez escrito para o fluxo, o ponteiro de busca ser posicionado no final, pronto para a prxima escrita. Se quiser ler do fluxo, voc ter que reposicionar o ponteiro. 358

Microsoft Visual C++ .NET Passo-a-Passo Voc reposiciona o ponteiro usando o mtodo Seek do objeto Stream, passando-lhe um offset em bytes e uma posio onde o offset deve ser aplicado. O offset pode ser positivo ou negativo, o sinal refletindo se o offset deve deslocar para o comeo (negativo) ou para o fim (positivo) do fluxo. As posies possveis so membros da enumerao SeekOrigin e elas podem ser SeekOrigin::Current: Posio atual; SeekOrigin::Begin: O comeo do fluxo; SeekOrigin::End: O final do fluxo.

PASSO 11: Crie um novo Customer e leia seus detalhes do arquivo:


//Cria um novo cliente e l detalhes do arquivo Customer *c4 = new Customer(); c4->Read(binr); Console::WriteLine(S"O balano para {0} (a/c {1}) {2}", c4->Name, __box(c4->Account), __box(c4->Balance));

O novo objeto Customer tem todos os seus campos fixados para valores padres. A chamada funo Read lhe manda ler seus dados da atual posio no arquivo. O problema potencial bvio que a funo Read ler de onde quer que o BinaryReader esteja atualmente posicionado. Se no estiver no comeo dos dados de um objeto Customer, voc poder ser notificado com uma exceo. DICA: Se quiser salvar o estado dos objetos em um programa real, voc no dever faz-lo manualmente como neste exemplo. O namespace System::Runtime::Serialization contm classes que lhe ajudaro a salvar e restaurar o estado dos objetos de um modo eficiente. PASSO 12: Construa e execute o aplicativo, fornecendo-lhe um nome de arquivo satisfatrio.

359

Jos Wilson Vieira 19.5. RPIDAS REFERNCIAS SOBRE O CAPTULO 19

A Tabela 19.15 mostra um resumo deste captulo. Tabela 19. 15: Resumo do captulo 19.
PARA Escrever texto para um arquivo FAA ISTO Crie um StreamWriter que saia para um FileStream e, ento, use os membros Write e WriteLine de StreamWriter. Ex.: FileStream *fs = new FileStream(S"Teste.txt", FileMode::Append); StreamWriter *sw = new StreamWriter(fs); sw->WriteLine(S"Algum texto"); Coloque em fluxo e feche o StreamWriter quando voc no mais precisar dele. Ex.: sw->Flush(); sw->Close(); Crie um StreamReader que leia de um FileStream e, ento, use o membro ReadLine de StreamReader. Ex.: FileStream *fs = new FileStream(S"Texto.txt", FileMode::Open); StreamReader *sr = new StreamReader(fs); String *line = sr->ReadLine(); Crie um BinaryWriter que saia para um FileStream e, ento, use os membros sobrecarregados Write de BinaryWriter. Ex.: FileStream *fs = new FileStream(S"binario.dat", FileMode::Create); BinaryWriter *bw = new BinaryWriter(fs); bw->Write(S"Algum texto"); bw->Write(100.00); Crie um BinaryReader que leia de um FileStream e, ento, use os membros ReadXxx de BinaryReader. Ex.: FileStream *fs = new FileStream(S"binario.dat"); BinaryReader *br = new BinaryReader(fs); String *line = br->ReadString(); Double d = br->ReadDouble(); Use as funes static fornecidas pela classe File. Se for realizar vrias operaes no mesmo arquivo, considere a criao de um objeto FileInfo ao invs de um File. Use as funes static fornecidas pela classe Directory. Se for realizar vrias operaes no mesmo arquivo, considere a criao de um objeto DirectoryInfo ao invs de um Directory.

Ler texto de um arquivo

Escrever valores binrios para um arquivo

Ler valores binrios de um arquivo

Obter informaes sobre um arquivo Obter informaes sobre uma pasta

360

Das könnte Ihnen auch gefallen