APOSTILA

download APOSTILA

of 80

Transcript of APOSTILA

PROGRAMAO ORIENTADA A OBJETOS EM

BERILHES BORGES GARCIA FLVIO MIGUEL VAREJOCOLABORAO: FABRCIA DOS SANTOS NASCIMENTO

NDICE1.1 ABSTRAO DE DADOS...............................................................................................................4 1.2 HISTRICO......................................................................................................................................6 1.3 A LINGUAGEM C++......................................................................................................................7 2.1 INTRODUO..................................................................................................................................9 2.2.1 PROBLEMA EXEMPLO....................................................................................................................10 2.2 UM EXEMPLO DE ABSTRAO DE DADOS EM C++.........................................................10 2.2.1 ESPECIFICAO E IMPLEMENTAO.................................................................................................11 2.3 UM EXEMPLO DE ESPECIFICAO.....................................................................................12 2.3.1 ESPECIFICAO DA CLASSE BIGINT.................................................................................................12 2.3.2 OCULTAMENTO DE INFORMAO.....................................................................................................14 2.3.3 FUNES MEMBROS.....................................................................................................................15 2.3.4 OVERLOADING.............................................................................................................................15 2.3.5 FUNES COM ARGUMENTOS DEFAULT............................................................................................16 2.3.6 CHAMANDO FUNES MEMBROS....................................................................................................17 2.3.7 CONSTRUTORES ...........................................................................................................................17 2.3.8 CONSTRUTORES E CONVERSO DE TIPOS..........................................................................................18 2.3.9 CONSTRUTORES E INICIALIZAO....................................................................................................19 2.3.10 OVERLOADING DE OPERADORES....................................................................................................20 2.3.11 DESTRUTORES...........................................................................................................................21 3.1 INTRODUO................................................................................................................................22 3.2 IMPLEMENTAO DE UMA CLASSE BIGINT.....................................................................22 3.2.1 O CONSTRUTOR BIGINT(CONST CHAR*)............................................................................................23 3.2.2 O OPERADOR RESOLUO DE ESCOPO.............................................................................................23 3.2.3 CONSTANTES...............................................................................................................................24 3.2.4 FUNES MEMBROS CONSTANTES.................................................................................................25 3.2.5 O OPERADOR NEW.....................................................................................................................26 3.2.6 DECLARAO EM BLOCOS.............................................................................................................26 3.2.7 O CONSTRUTOR BIGINT (UNSIGNED)...............................................................................................26 3.2.8 O CONSTRUTOR CPIA.................................................................................................................27 3.2.9 REFERNCIAS.............................................................................................................................27 3.2.10 O OPERADOR ADIO DA CLASSE BIGINT.....................................................................................29 3.2.11 FUNES FRIEND.......................................................................................................................32 3.2.12 A PALAVRA RESERVADA THIS.....................................................................................................33 3.2.13 O OPERADOR ATRIBUIO DA CLASSE BIGINT...............................................................................33 3.2.14 A FUNO MEMBRO BIGINT::PRINT()...........................................................................................34 3.2.15 O DESTRUTOR DA CLASSE BIGINT................................................................................................34 3.2.16 FUNES INLINE........................................................................................................................35 4.1 INTRODUO................................................................................................................................37 4.1.1 CLASSES DERIVADAS....................................................................................................................38 4.1.2 REDECLARAO - FUNES MEMBROS VIRTUAIS..............................................................................39 4.2 UM EXEMPLO DE APLICAO...............................................................................................40 4.2.1 CLASSE PONTO...........................................................................................................................40 4.2.2 CLASSE LINHA E CLASSE CIRCULO.................................................................................................41 4.2.3 INSTNCIAS DE CLASSE COMO VARIVEIS MEMBROS DE UMA CLASSE...................................................42 4.2.4 A FUNO MEMBRO MOVER().......................................................................................................42 4.2.5 A FUNO MEMBRO DESENHAR() ...............................................................................................43 4.2.6 CLASSE QUADRO.........................................................................................................................45 4.2.7 COMPATIBILIDADE DE TIPOS..........................................................................................................47 4.2.8 CLASSE INCOMPLETAS - FUNES VIRTUAIS PURAS...........................................................................47

2

4.3 O EXEMPLO GEOMTRICO MELHORADO.........................................................................48 4.3.1 INICIALIZAO DE OBJETOS...........................................................................................................50 4.3.2 FINALIZAO DE OBJETOS.............................................................................................................52 5.1 INTRODUO................................................................................................................................54 5.2 CONSTRUTOR PR-DEFINIDO DE CPIA, OPERADOR PR-DEFINIDO DE ATRIBUIO E CONSTRUTOR PR-DEFINIDO DEFAULT....................................................54 5.3 OBJETOS TEMPORRIOS .......................................................................................................55 5.4 RETORNO DE OBJETOS EM FUNES.................................................................................56 5.5 ESPECIFICADORES DE ACESSO..............................................................................................57 5.6 ESTRUTURAS INTERNAS A CLASSES ...................................................................................59 5.7 HERANA MLTIPLA ...............................................................................................................60 5.7.1 AMBIGIDADES EM HERANA MLTIPLA..........................................................................................62 5.8 FUNES E CLASSES GENRICAS.........................................................................................65 5.9 NAMESPACES................................................................................................................................68 5.10 BOOLEANOS................................................................................................................................69 5.11 STRINGS........................................................................................................................................70 5.12 STREAMS......................................................................................................................................70 5.13 TRATAMENTO DE EXCEES..............................................................................................72 5.13.1 SINALIZANDO UMA EXCEO........................................................................................................73 5.13.1 CAPTURANDO E TRATANDO UMA EXCEO.....................................................................................74 5.14 COLEES E ITERADORES DA BIBLIOTECA PADRO C++........................................77 ESTA SEO OBJETIVA INTRODUZIR AS CLASSES DE COLEES E ITERADORES QUE FAZEM PARTE DA BIBLIOTECA PADRO C++. IMPORTANTE ESCLARECER QUE EXISTE UMA PEQUENA CONFUSO A RESPEITO DESTAS CLASSES, QUE SO FREQUENTEMENTE REFERIDAS COMO PARTE DA STL (STANDARD TEMPLATE LIBRARY). STL FOI O NOME DADO POR ALEX STEPANOV PARA APRESENTAR SUA BIBLIOTECA PARA O COMIT DE PADRONIZAO DE C++, EM 1994. O NOME FOI AMPLAMENTE ACEITO PELA COMUNIDADE DE PROGRAMADORES C++. O COMIT DE PADRONIZAO DE C++ DECIDIU INTEGR-LA BIBLIOTECA PADRO DE C++, PORM FAZENDO UM NMERO SIGNIFICATIVO DE MODIFICAES. O DESENVOLVIMENTO DA STL CONTINUOU NA SILICON GRAPHICS (SGI), DE ONDE SURGIU A SGI STL. ESTA BIBLIOTECA DIVERGE SUTILMENTE DA BIBLIOTECA PADRO EM MUITOS PONTOS. PORTANTO, EMBORA SEJA UMA CONFUSO POPULAR, A BIBLIOTECA PADRO C++ NO INCLUI A STL. AQUI NO DISCUTIREMOS EM DETALHES AS CLASSES DESTAS BIBLIOTECAS. VAMOS APRESENTAR UMA INTRODUO QUE SER VLIDA TANTO PARA UMA QUANTO PARA OUTRA.......................................................................................................................................77

3

Captulo I - Introduo

Este captulo contm uma introduo histria da linguagem C++, idias que influenciaram o projeto da linguagem C++, bem como apresenta uma anlise comparativa entre as linguagens C e C++.

1.1 Abstrao de DadosAbstrao de Dados e Programao Orientada a Objetos representam um estilo de programao que oferece oportunidades para a melhoria na qualidade de software. Programao orientada a objetos, com abstrao de dados como um fundamento necessrio, difere enormemente dos outros estilos e metodologias de programao, uma vez que requer uma abordagem diferente para a resoluo de problemas.

4

Programadores h muito reconhecem o valor de se organizar tens de dados correlatos em construes de programas tais como Records de Pascal e structs de C, tratando-as posteriormente como unidades. Abstrao de dados estende essa organizao de forma a incorporar um conjunto de operaes que possam ser executadas sob uma instncia particular da estrutura. Usualmente, os elementos de dados e a implementao das operaes que podem ser executadas sobre eles so mantidos privativos ou protegidos de forma a prevenir alteraes indesejveis. Ao invs de acessar os elementos de dados diretamente, os programas clientes (cdigo do usurio) devem invocar as operaes exportveis de forma a produzir os resultados desejveis. Quando ns encapsulamos dados e operaes que atuam sob esses dados desta maneira, estas estruturas encapsuladas comportam-se analogamente a tipos prconstrudos ou fundamentais como nmeros inteiros ou de ponto flutuante. Pode-se, ento, us-las como caixas pretas que proporcionam uma transformao entre a entrada e a sada. Ns no necessitamos saber como o compilador trata os tipos fundamentais. Abstrao de dados, em essncia, nos permite criar novos tipos de dados, dai surgiu a idia de cham-los de Tipos Abstratos de Dados. Encapsulamento + Proteo = Tipo Abstrato de Dados

5

1.2 HistricoA Programao Orientada a Objetos no nova; seus conceitos remontam h duas dcadas atrs. A origem da programao orientada a objetos surge com as linguagens de programao Simula 67 e Smalltalk. Novo o fluxo recente de interesse nesta importante e promissora metodologia. Foi dito que a programao orientada a objetos difere substancialmente dos estilos de programao com os quais ns estamos mais familiarizados, requerendo uma abordagem diferente para se resolver problemas, mas o que programao orientada a objetos, e o que esta tem de diferente? Quando se est programando orientado a objetos, um programador especifica o que fazer com um objeto antes de se concentrar no aspecto procedimental convencional de como algo deve ser feito. Programao orientada a objetos lida com a manipulao de objetos. Um objeto, por sua vez, pode representar quase tudo - um numero, uma string, um registro de paciente em um hospital, um trem ou uma construo grfica como um retngulo ou alguma outra forma geomtrica. Em essncia, um objeto compreende os elementos de dados necessrios para descrever os objetos, junto com o conjunto de operaes permissveis sobre esses dados. Pode-se ver que um objeto nada mais do que uma instncia particular de um tipo abstrato de dados, o qual foi projetado de acordo com um conjunto particular de regras. Uma parte considervel do potencial da programao orientada a objeto resulta da utilizao do mecanismo de herana - a idia pela qual um programador comea com uma biblioteca de classes (tipos de objetos) j desenvolvida e a estende para uma nova aplicao adicionando novos elementos de dados ou operaes (atributos) de forma a construir novas classes. Em outras palavras, ao invs de desenvolver uma nova aplicao escrevendo o cdigo a partir do zero, um cliente herda dados e operaes de alguma classe base til, e ento adiciona nova funcionalidade descrevendo como a nova classe difere da classe base. Ao adicionar novos elementos de dados ou funes, o programador no necessita modificar a classe base. Isto nos conduz a um dos benefcios da programao orientada a objetos: a reutilizao de cdigo. O mecanismo de herana abre a possibilidade de ocorrncia de late ou dynamic binding (amarrao tardia ou amarrao dinmica), onde um programa cliente pode aplicar uma funo a esse objeto sem saber a classe especfica do objeto. Em tempo de execuo, o sistema run-time determinar a classe especfica do objeto e invocar a implementao correspondente da funo. Mas o que tudo isto significa, afinal de contas? Para comear a compreender estes novos conceitos, considere um programa que manipula estruturas de dados representando figuras geomtricas. Talvez pudssemos comear com uma classe Forma para representar as formas gerais. Atravs da herana, nos poderamos projetar novas classes de Forma a representar formas mais especificas, como Crculos e Quadrados. Imagine agora uma situao na qual ns necessitamos de um programa que ao invs de operar sob estruturas de dados especificas como crculos, possa lidar com Formas genricas cujos tipos particulares ns no conhecemos quando escrevemos o

6

programa. Por exemplo, ns podemos querer que o programa desenhe um conjunto de Formas, tais com Crculos, Tringulos e Retngulos. Quando o programa estiver executando, o sistema run-time determinar a classe particular de cada objeto do tipo Forma e, utilizando late binding, chamar a funo desenhar() apropriada para este.

1.3 A Linguagem C++A linguagem C++ foi projetada e implementada por Bjarne Stroustrup, do AT&T Bell Laboratories, para se tornar a linguagem sucessora de C. C++ estende C atravs do uso de vrias idias emprestadas das linguagens de programao Simula 67 e Algol 68. Mesmo assim, C++ mantm total compatibilidade com C, isto , qualquer programa feito em C tambm um programa em C++. A figura 1 ilustra a rvore genealgica das linguagens de programao mais conhecidas.

1950 Fortran 1960 Cobol Algol 60 Simula 67 1970 Smalltalk 1980 Ada Eiffel 1990 C++ CLOS Miranda Pascal C ML PL 1 Algol 68 Prolog Lisp

Figura 1 - rvore Genealgica das Principais LPs

C++ tambm adiciona muitas novas facilidades a C, fazendo com que esta seja conveniente para um amplo leque de aplicaes: de drivers de dispositivos s aplicaes comerciais. Muitos programadores concordam que C uma linguagem compacta e eficiente. Contudo, ela possui deficincias, algumas das quais foram corrigidas na linguagem C++. Por exemplo, C possui um nico mecanismo de passagem de parmetros, conhecido como passagem por valor. Quando se necessita passar um argumento para uma funo por referncia, deve-se utilizar um mecanismo obscuro e indutor de erros, que a passagem de um ponteiro como parmetro. C++ resolve este problema de uma forma elegante e eficiente.

7

C++ no apenas corrige muitas das deficincias de C, mas tambm introduz muitas caractersticas completamente novas que foram projetadas para suportar abstrao de dados e programao orientada a objetos. Exemplos de tais caractersticas so: Classes, a construo bsica que permite ao programador definir novos tipos de dados; Variveis membros, que descrevem a representao de uma classe, e funes membros, que definem as operaes permissveis sob uma classe; Overloading (Sobrecarga) de operadores, que permite a definio de significados adicionais para muitos operadores, de modo que estes possam ser usados com os tipos de dados criados pelo programador, e overloading de funes, similar ao overloading de operadores, que permite reduzir a necessidade de criao de nomes para funes, tornando o cdigo mais fcil de ler; Herana, que permite que uma classe derivada herde as variveis membros e funes membros de sua classe base; Funes virtuais, que permitem que uma funo membro herdada de uma classe base seja redefinida em uma classe derivada.

8

Captulo II - Abstrao de Dados

Este captulo descreve as facilidades que C++ fornece para a declarao de novos tipos de dados, bem como as maneiras pelas quais uma estrutura de dados pode ser protegida, inicializada, acessada e, finalmente, destruda, atravs de um conjunto especifico de funes.

2.1 IntroduoAs linguagens de programao tratam as variveis de programa e constantes como instncias de um tipo de dados. Um tipo de dado proporciona uma descrio de suas instncias fornecendo ao compilador informaes, tais como: quanto de memria alocar para uma instncia, como interpretar os dados, e quais operaes so permissveis sob aqueles dados. Por exemplo, quando ns escrevemos uma declarao tal como float x; em C ou C++, estamos declarando uma instncia chamada x do tipo de dado float. O tipo de dado float diz ao compilador para reservar, por exemplo, 32 bits de memria, para usar instrues de ponto flutuante para manipular esta memria, que operaes tais como "somar" e "multiplicar so aceitas e que operaes como "mdulo" e "shift no so. Ns no temos que escrever esta descrio do tipo float - o implementador do compilador fez isto para ns. Tipos de dados como float, int e char so conhecidos com tipos de dados primitivos. Algumas linguagens de programao tm caractersticas que nos permitem estender efetivamente a linguagem atravs da adio de novos tipos de dados. Pode-se implementar novos tipos de dados na linguagem C++ atravs da declarao de uma classe. Uma classe um novo modelo de dados definido pelo usurio que proporciona encapsulamento, proteo e reutilizao. Uma classe tipicamente contm: Uma especificao de herana; Uma definio para a representao (adicional) de dados de suas instncias (objetos); Definies para as operaes (adicionais) aplicveis aos objetos; A melhor maneira de aprender sobre abstraes de dados em C++ escrever um programa, e isto que ser feito nas prximas sees. Vamos comear em um territrio familiar, escrevendo um programa simples em C. #include main () { int a = 193; int b = 456; int c; c = a + b + 47; printf("%d\n",c); }

9

Exemplo 1

Este programa declara trs variveis inteiras a, b e c, inicializando a e b com os valores 193 e 456, respectivamente. varivel inteira c atribudo o resultado da adio a, b e o literal 47. Finalmente, a funo printf() da biblioteca padro de C chamada para imprimir o valor de c. 2.2.1 Problema Exemplo Agora suponha que se deseja executar um clculo similar, mas desta vez, a e b so nmeros muito grandes, como a dvida interna do Brasil expressa em reais. Tais nmeros so muito grandes para serem armazenados como ints, de modo que se ns tentssemos escrever a = 25123654789456; o compilador C emitiria uma mensagem de erro e o programa no seria compilado com sucesso. Inteiros grandes tm muitas aplicaes prticas como criptografia, lgebra simblica, e teoria dos nmeros, onde pode ser necessrio executar operaes aritmticas com nmeros de centenas ou mesmo milhares de dgitos.

2.2 Um Exemplo de Abstrao de Dados em C++Utilizando-se C++ possvel construir uma soluo para o problema anterior de uma forma conveniente e elegante. Note como o programa do exemplo 1 similar ao programa do Exemplo 2. Muitas linguagens atualmente em uso, tais como C, COBOL, Fortran ,Pascal, e Modula-2 tornam difcil a abstrao de dados. Isto porque a abstrao de dados requer caractersticas especiais no disponveis nestes linguagens. Afim de obter sentimento a respeito destas idias, ns iremos analisar o programa do Exemplo 2. As trs primeiras instrues no corpo da funo main() declaram trs variveis do tipo BigInt: a, b e c. O Compilador C++ necessita saber como cri-las, quanto de memria necessrio para alocar para elas e como inicializ-las. A primeira e a segunda instrues so similares. Elas inicializam as variveis BigInt a e b com constantes literais representando inteiros grandes escritos como strings de caracteres contendo somente dgitos. Sendo assim, o compilador C++ deve # include "BigInt.h" ser capaz de converter strings de caracteres em BigInts. main() { BigInt a = "25123654789456"; BigInt b = "456023398798362"; BigInt c; c = a + b + 47; c.print(); 10 printf("\n"); }

Exemplo 2

A quarta instruo mais complexa. Ela soma a, b e a constante inteira 47, e armazena o resultado em c. O compilador C++ necessita ser capaz de criar uma varivel BigInt temporria para reter a soma de a e b. E, ento, deve converter a constante inteira 47 em um BigInt e som-la com a varivel temporria. Finalmente, deve atribuir o valor desta varivel temporria a varivel c. A quinta instruo imprime c na sada padro, atravs da funo membro print. A ltima instruo chama a funo printf da biblioteca padro de C para imprimir o caracter de nova linha. Apesar do corpo de main() no possuir nenhuma instruo adicional, o trabalho ainda no est finalizado. O compilador deve gerar cdigo para que as variveis a, b e c, e qualquer BigInt temporrio sejam destrudos antes de deixar uma funo. Isto assegura que a memria ser liberada. De uma forma resumida, o compilador necessita saber como: Criar novas instncias das variveis BigInt; Converter strings de caracteres e inteiros para BigInts; Inicializar o valor de um BigInt com o de outro BigInt; Atribuir o valor de um BigInt a outro; Somar dois BigInts; Imprimir BigInts; Destruir BigInts quando eles no so mais necessrios.

2.2.1 Especificao e Implementao Onde o compilador C++ obtm este conhecimento? Do arquivo BigInt.h, que includo na primeira linha do programa exemplo. Este arquivo contm a especificao de nosso novo tipo de dados BigInt. A especificao contm as informaes que os programas que usam um tipo abstrato de dados, chamado de programas clientes (ou simplesmente, clientes) necessitam, afim de compilarem com xito. Muitos dos detalhes de como um novo tipo funciona, conhecidos como a implementao, so Compilador BigInt.cpp BigInt.o mantidos em um arquivo C++ Em nosso exemplo, este arquivo chamado parte. BigInt.cpp. A Figura 2 mostra como a especificao e a implementao de um tipo abstrato de dados so combinados com o cdigo fonte do cliente para produzir um Linker programa executvel.BigInt.h

Client.cpp

Compilador C++

Client.o Client.exe 11

Figura 2 - Combinando Especificao e Implementao em um Programa Cliente

O objetivo de se separar o cdigo de um tipo abstrato em uma especificao e uma implementao ocultar os detalhes de implementao do cliente. Ns podemos, ento, mudar a implementao e ter garantias que os programas clientes continuaro a trabalhar corretamente. Um tipo abstrato de dados bem projetado tambm oculta sua complexidade em sua implementao, tornando to fcil quanto possvel para os clientes utiliz-lo.

2.3 Um Exemplo de Especificao2.3.1 Especificao da Classe BigInt A instruo #include "BigInt.h inclui a especificao da classe BigInt dentro do programa exemplo, de modo que se possa usar BigInts no programa. Aqui est o contedo de BigInt.h (note que em C++, // comea um comentrio que se estende at o fim da linha):

12

#include // este arquivo contem a especificao da classe BigInt class BigInt { // ... char* digitos; // ponteiro para um array de digitos na memria unsigned ndigitos; // numero de digitos // ... public: BigInt (const char*); // funcao construtora BigInt (unsigned n = 0); // funcao construtora BigInt (const BigInt&); // funcao construtora utilizando copia void operator= (const BigInt&); // atribuio BigInt operator+ (const BigInt&) const; // operador adicao void print (FILE* f = stdout) const; // funo de impressao ~BigInt() { delete digitos; } // destrutor }; Programadores C entenderam muito pouco deste cdigo. Este um exemplo de uma das mais importantes caractersticas de C++, a declarao de uma classe. Esta uma extenso de uma construo que programadores em C j deveriam estar familiarizados: a declarao de uma struct. A declarao de uma estrutura agrupa um certo nmero de variveis, que podem ser de tipos diferentes, em uma unidade. Por exemplo, em C (ou C++) pode-se escrever: struct BigInt { char* digitos; unsigned ndigitos; }; Pode-se ento declarar uma instncia desta estrutura escrevendo-se: struct BigInt a; As variveis membros da struct, digitos e ndigitos, podem ser acessados utilizando-se o operador ponto(.); por exemplo, a.digitos, acessa a varivel membro digitos da struct a. Relembre que em C pode-se declarar um ponteiro para uma instncia de uma estrutura: struct BigInt* p; Neste caso, pode-se acessar as variveis individuais utilizando-se o operador ->, por exemplo, p->digitos.

13

Classes em C++ trabalham de forma similar, e os operadores . e -> podem ser usados da mesma maneira para acessar as variveis membro de uma classe. Neste exemplo, a classe BigInt tem duas variveis membros digitos e ndigitos. A varivel digitos aponta para um array de bytes, alocados na rea de memria dinmica, e contm os dgitos de um inteiro grande (um dgito por byte do array). Os dgitos so ordenados comeando do dgito menos significativo no primeiro byte do array, e so nmeros binrios, no caracteres ASCII. A varivel membro ndigitos contm o numero de dgitos de um inteiro. A figura 3 mostra o diagrama de uma instncia desta estrutura para o nmero 654321.

digitos 1 2 3 4 5 6 6 ndigitos Figura 3 - Uma Instncia da Classe BigInt Representando o Nmero "654321"

2.3.2 Ocultamento de Informao Um programa cliente pode declarar uma instncia da classe BigInt da seguinte forma: BigInt a; Temos agora um problema potencial: o programa cliente pode tentar, por exemplo, usar o fato de que a.ndigitos contm o nmero de dgitos no nmero a. Isto tornaria o programa cliente dependente de implementao da classe BigInt. Necessitase, portanto, de uma forma de preveno contra o acesso no autorizado a variveis membros (representao) de uma classe. C++ permite que o usurio controle o acesso aos atributos de uma classe. A palavra reservada public: dentro de uma declarao de classe indica que os membros que seguem esta palavra reservada podem ser acessados por todas as funes. J os membros que seguem a palavra reservada private:, como digitos e ndigitos em nosso exemplo, s podem ser acessados pelas funes membros declaradas dentro da mesma classe. H um terceiro tipo de controle determinado pela palavra chave protected:, o qual ser analisado posteriormente. Restringir o acesso aos membros de uma classe uma forma de aplicao do princpio de ocultamento de informao, este princpio pode ser estabelecido como: Todas as informaes a respeito de uma classe (mdulo) devem ser privativas desta classe, a menos que uma informao especfica seja declarada pblica. Este princpio visa garantir que mudanas na implementao (representao) de uma classe no afetem os clientes desta classe. Por conseguinte, torna-se mais fcil

14

fazer modificaes, porque o cdigo que manipula as variveis membros protegidas localizado, e isto auxilia no processo de depurao. 2.3.3 Funes Membros Como um programa cliente interage com as variveis membros privadas de uma classe? Enquanto a construo struct de C permite apenas que variveis sejam agrupadas juntas, a declarao class de C++ permite o agrupamento de variveis e funes. Tais funes so chamadas de funes membros, e as variveis privadas das instncias de uma classe somente podem ser acessadas atravs das funes membros1. Sendo assim, um programa cliente s pode ler e modificar os valores das variveis membros privativas de uma classe indiretamente, chamando as funes membros pblicas de uma classe, como mostrado na Figura 4.

BigInt(const char*) (construir) BigInt a = 218 print() char* digitos int ndigitos BigInt(const BigInt&) BigInt c = a + b +47; (inicializar) c.print();

operador+(const BigInt&) (somar) Figura 4 - Acesso a Variveis Privadas atravs de Funes Membro Pblicas

Por exemplo, a classe BigInt tem duas variveis membros privativas, digitos e ndigitos, e sete funes membros pblicas. A declarao destas funes parecer estranha aos programadores C pelas seguintes razes: trs funes tm o mesmo nome, BigInt; os nomes de funes operator=, operator+ e ~BigInt contm caracteres normalmente no permitidos em nomes de funes. 2.3.4 Overloading

Estritamente falando, funes friend tambm podem acessar os membros privativos de uma classe, como ser visto posteriormente.1

15

Um identificador ou operador sobrecarregado (overloaded) se simultaneamente denota duas ou mais funes distintas. Em geral, sobrecarga (overloading) aceitvel somente quando cada chamada de funo no ambgua, isto , quando cada funo pode ser identificada usando-se somente as informaes de tipos disponveis dos parmetros. Por exemplo, em C++ pode-se declarar duas funes com o mesmo nome abs: int abs(int); float abs(float) Pode-se ento escrever: x = abs (2); y = abs (3.14); A primeira instruo chamara abs(int), e a segunda abs(float) - o compilador C++ sabe qual funo abs deve usar porque 2 um int e 3.14 um float. Overloading reduz o numero de indentificadores de funes que um programador deve memorizar, e tambm elimina nomes artificiais para as funes. Deve-se notar que, ao contrrio de outras linguagens, como por exemplo Ada, C++ considera somente o nmero e o tipo dos argumentos de forma a determinar qual funo ser utilizada. Sendo assim, no se pode sobrecarregar float sqr(float) e double sqr(float), por exemplo. 2.3.5 Funes com Argumentos Default A especificao da classe BigInt contm as seguintes declaraes de funo: BigInt (unsigned n = 0) // funcao construtora void print (FILE* f = stdout); // funo de impressao Estas declaraes demandam explicaes adicionais. Alm da declarao do tipo dos argumentos, estas instrues declaram argumentos default. Se voc chamar uma destas funes sem argumento, C++ utiliza a expresso do lado direito do sinal = como default. Por exemplo, a chamada de funo print() idntica a chamada print(stdout). Ao se especificar defaults convenientes para as funes pode-se abreviar as chamadas de funes e torn-las mais legveis. Tambm se reduz o nmero de funes que se necessita escrever. Os argumentos defaults de uma funo devero ser os argumentos mais a direita em uma determinada funo. Assim, a declarao unsigned Maximo (unsigned a, unsigned b, unsigned c=0, unsigned d=0); legal, mas as declaraes unsigned Maximo (unsigned a, unsigned b=0, unsigned c); unsigned Maximo (unsigned a=0, unsigned b, unsigned c);

16

no so. 2.3.6 Chamando Funes Membros Considere agora a penltima linha do programa do exemplo 2: c.print(); Funes membros so chamadas de uma maneira anloga a forma como variveis membros so acessadas em C, isto , utilizando-se o operador . ou o operador ->. Uma vez que c uma instncia da classe BigInt, a notao c.print() chama a funo membro print da classe BigInt para imprimir o valor atual de c. Se ns declarssemos um ponteiro para um BigInt BigInt* p; p = &c; ento a notao p->print() chamaria a mesma funo. Esta notao assegura que uma funo membro pode operar somente sobre instncias da classe para a qual esta foi definida. Em C++, vrias classes diferentes podem ter funes membros com o mesmo nome. 2.3.7 Construtores Uma das coisas que o compilador C++ necessita saber sobre o tipo abstrato BigInt como criar novas instncias da classe BigInt. Pode-se informar ao compilador C++ como isto deve ser feito definindo-se uma ou mais funes membros especiais chamadas de construtores. Uma funo construtora possui o mesmo nome de sua classe. Quando o programa cliente contm uma declarao como, BigInt a = "123"; O compilador C++ reserva espao para as variveis membros digitos e ndigitos para uma instncia da classe BigInt e ento chama a funo construtora a.BigInt("123"). responsabilidade do implementador da classe BigInt escrever a funo BigInt() de modo que ela inicialize as instncias corretamente. Neste exemplo, BigInt("123") aloca trs bytes para memria dinmica, ajusta a.digitos para apontar para esta memria, modifica os trs bytes para [3,2,1], e atribui o valor trs para a.ndigitos. Isto criar uma instncia da classe BigInt que inicializada com o valor 123. Se uma classe tem uma ou mais funes construtoras (funes construtoras podem ser sobrecarregadas), C++ garante que uma funo ser chamada para inicializar cada instncia da classe. Um usurio de uma classe, tal como BigInt, no tem de se lembrar de chamar uma funo de inicializao separadamente para cada BigInt declarado. Uma classe pode ter um construtor que no requer nenhum argumento, ou pode ter uma funo construtora com defaults para todos os seus argumentos. Ns

17

chamamos esta funo de construtor default. Este o construtor que C++ chama para inicializar a varivel b mostrada abaixo: BigInt b; C++ tambm utiliza o construtor default para inicializar os elementos individuais de um vetor de instncias de classe, por exemplo: BigInt a[10]; C++ chamar o construtor default da classe BigInt 10 vezes, uma vez para a[0], a[1], ..a[9]. Pode-se tambm escrever a declarao: BigInt a = "123"; usando uma lista de argumentos, como a seguir: BigInt a ("123"); // mesmo que BigInt a = "123";

De fato, deve-se utilizar uma lista de argumentos para declarar uma instncia de uma classe que demande mais de um argumento. Por exemplo, se ns tivssemos um classe BigComplex que tivesse o seguinte construtor: BigComplex (BigInt re, BigInt im); ns poderamos declarar uma instncia da classe BigComplex chamada de x e inicializ-la para (12,34) atravs de: BigComplex x(12,34); 2.3.8 Construtores e Converso de Tipos Para compilar o exemplo 2, o compilador C++ necessita saber como converter uma string de caracteres como "25123654789456", ou um inteiro como 47, para um BigInt. Construtores tambm so usados com esse propsito. Quando o compilador C++ encontra uma declarao como: BigInt c = a + b + 47; ele reconhece que um int (47) deve ser convertido para um BigInt antes que a adio se realize. Sendo assim, ele necessita verificar se um construtor BigInt (unsigned) est declarado. Se este existir, uma instncia temporria da classe BigInt ser criada atravs de uma chamada a este construtor com o argumento 47. Se o construtor apropriado no est declarado, o compilador sinalizar um erro. A classe BigInt possui dois construtores BigInt(char*), BigInt (unsigned), de modo que se pode utilizar strings de caracteres ou inteiros, e o compilador C++ automaticamente chamar o construtor apropriado para realizar a converso de tipos.

18

Pode-se tambm chamar o construtor explicitamente para forar a converso de tipo. Por exemplo, C++ processaria as declaraes: unsigned long i = 4000000000; BigInt c = i + 1000000000; adicionando-se i a constante 1000000000, e ento convertendo o resultado para um BigInt de forma a inicializar c. Infelizmente, a soma de i e 1000000000 provavelmente causar um erro de overflow. Pode-se, contudo, evitar este problema convertendo-se explicitamente i ou 1000000000 para um BigInt: BigInt c = BigInt (i) + 1000000000; ou BigInt c = BigInt (1000000000) + i; Pode-se tambm escrever isto utilizando o cast de C e C++: BigInt c = (BigInt) i + 1000000000; ou BigInt c = i + (BigInt) 1000000000; A notao de chamada do construtor mais indicativa do que est ocorrendo do que a notao cast, e tambm mais geral desde que permite que se utilize construtores que requerem mltiplos argumentos. 2.3.9 Construtores e Inicializao Compilar o exemplo 2 tambm requer que o compilador C++ saiba como inicializar um BigInt com o valor de outro BigInt, por exemplo: BigInt c = a + b + 47; A instncia BigInt c deve ser inicializada com o valor da instncia temporria da classe BigInt que contm o valor da expresso a + b + 47. C++ fornece um construtor pr-definido que copia objetos na inicializao. Deve-se, contudo, tomar cuidado ao utilizar este construtor pr-definido quando as instncias da classe alocam dinamicamente algum dos seus elementos. Isto decorre do fato dele fazer cpia bitwise de um objeto sobre o outro, no copiando os elementos alocados dinamicamente. Tal fato pode gerar, portanto, sinonmias e efeitos colaterais. Pode-se controlar como C++ inicializa instncias da classe BigInt definido-se uma funo construtora de copia BigInt (const BigInt&)2. No exemplo, este construtor aloca memria para uma nova instncia da classe e faz uma cpia do contedo da instncia passada como argumento. Quando se programa em C++, importante entender a distino entre inicializao e atribuio. Inicializao ocorre em trs contextos:O argumento de BigInt& um exemplo de uma referncia em C++, que ser posteriormente descrita.2

19

declaraes com inicializadores (como descrito acima); argumentos formais de funes; valores de retorno de funes; Atribuies ocorrem em expresses (no em declaraes) que utilizam o operador =. Por exemplo, BigInt c = a + b + 47; uma chamada ao construtor BigInt (BigInt&) inicializa a varivel c. Contudo em: BigInt c; c = a + b + 47; uma chamada ao construtor BigInt (unsigned n=0) inicializa a varivel c para zero, e a segunda instruo atribui o valor da varivel temporria BigInt a c. 2.3.10 Overloading de Operadores C++ tambm deve ser capaz de adicionar dois BigInts e atribuir o valor a um novo BigInt afim de compilar o Exemplo 2. Pode-se definir funes membros chamadas de add e assign para fazer isto, contudo, esta soluo tornaria a escrita de expresses aritmticas deselegantes. C++ permite a definio de significados adicionais para muitos de seus operadores, incluindo + e =, sendo assim estes podem significar "add" e "assign quando aplicados a BigInts. Isto conhecido como overloading de operadores, e similar ao conceito de overloading de funes. Realmente, muitos programadores j esto familiarizados com esta idia porque os operadores de muitas linguagens de programao, incluindo C, j so sobrecarregados. Por exemplo, em C: int a,b,c; float x,y,z; c = a + b; z = x + z; Os operadores + e = realizam coisa diferentes nas duas ltimas instrues: a primeira faz adio e atribuio de inteiros e a segunda faz adio e atribuio de pontos flutuante. Overloading de operadores simplesmente uma extenso disto. C++ reconhece um nome de funo tendo a forma operator@ como um overloading do smbolo @. Pode-se sobrecarregar os operadores + e = declarando-se as funes membros operator+ e operator=, como foi feito na classe BigInt: void operator= (const BigInt&); BigInt operator+(const BigInt&) const; // atribuio // operador adio

Pode-se chamar estas funes utilizando-se a notao usual para chamada de funes membros utilizando-se apenas o operador:

20

BigInt a,b,c; c = operator+(b); c=a+b As duas ltimas linhas so equivalentes. Deve-se ressaltar que a sobrecarga dos operadores no muda os significados pr-definidos, apenas d um significado adicional quando utilizado com instncias da classe. A expresso 2 + 2 continua dando 4. A precedncia dos operadores tambm no alterada. Pode-se sobrecarregar qualquer operador de C++, exceto o operador de seleo (.) e o operador de expresso condicional (?:). A utilizao da sobrecarga de operadores recomendada apenas quando o operador sugere fortemente a funo que este executa. Um exemplo de como sobrecarga NO deve ser usada sobrecarregar o operador + para executar uma subtrao. 2.3.11 Destrutores O compilador C++ necessita saber, ainda, como destruir as instncias da classe BigInt. Pode-se informar ao compilador como fazer isto definido-se um outro tipo especial de funo chamada destrutor. Uma funo destrutora tem o mesmo nome de sua classe prefixado pelo caracter ~. Para a classe BigInt, esta a funo membro ~BigInt(). Deve-se escrever a funo destrutora de uma forma que esta finalize apropriadamente instncias da classe. No exemplo apresentado, isto significa liberar a memria dinmica alocada para a instncia da classe pelo construtor. Se uma classe tem uma funo destrutora, C++ garante que ela ser chamada para finalizar toda instncia da classe quando esta no for mais necessria. Isto significa dizer que o usurio no necessita chamar explicitamente o destrutor da classe, o sistema se encarrega de fazer isto, eliminando assim uma fonte possvel de erros de programao. Diferente dos construtores, os destrutores no tm nenhum argumento, e assim no podem ser sobrecarregados. Por conseguinte, uma classe s possui um nico destrutor.

21

Captulo III - Exemplo de Implementao

3.1 IntroduoO captulo anterior descreve um programa cliente em C++ que utiliza a classe BigInt e a especificao desta classe. O presente captulo ir descrever a implementao da classe BigInt, onde todo o trabalho real feito. Ao se descrever a implementao da classe, vrias caractersticas novas de C++ sero encontradas e explicadas: o operador de resoluo de escopo, tipos constantes, funes membros constantes, referncias, os operadores new e delete, funes friend e funes inline. Como foi dito anteriormente a implementao de uma classe contm os detalhes de como ela trabalha.

3.2 Implementao de uma Classe BigIntA implementao requer as informaes contidas na especificao, sendo assim a primeira linha no arquivo BigInt.cpp : #include "BigInt.h";

22

Uma vez que a implementao e os programas clientes so compilados com a mesma especificao, o compilador C++ assegura uma interface consistente entre elas. 3.2.1 O construtor BigInt(const char*) A classe BigInt tem trs construtores, um para declarar instncias de um BigInt a partir de string de caracteres, um para criar instncias de um inteiro no negativo (um unsigned) e um para inicializar um BigInt com outro. Aqui est a implementao do primeiro construtor:

BigInt:: BigInt (const char* digitString) { unsigned n = strlen (digitString); if (n!=0) { digitos = new char [ndigitos = n ]; char * p = digitos; const char* q = &digitString[n]; while (n--) *p++ = *--q - '0'; // converte o digito ASCII para binario } else { // string vazia digitos = new char[ndigitos = 1]; digitos[0] = 0; } } Se a string vazia esta tratada como um caso especial e cria-se um BigInt inicializado para zero. 3.2.2 O Operador Resoluo de Escopo A notao BigInt::BigInt identifica BigInt como uma funo membro da classe BigInt, isto se deve ao fato de que vrias classes podem ter funes membros com o mesmo nome. O operador :: conhecido como operador de resoluo de escopo, e pode ser aplicado a funes membros e variveis membros. Logo, BigInt::print() refere-se a funo membro print() que membro da classe BigInt, e BigInt::digitos refere-se a varivel membro digitos que membro da classe BigInt.

23

3.2.3 Constantes Um programador C estar familiarizado com o uso do tipo char* para argumentos que so strings de caracteres, mas o que uma const char*? Em C++, pode-se usar a palavra reservada const para indicar que uma varivel uma constante, e portanto no pode ser modificada por um comando de atribuio (=). Quando utilizado em uma lista de argumentos como acima, isto previne que o argumento possa ser modificado pela funo. Sendo assim, se tentarmos adicionar a seguinte instruo: digitString[0] = 'x'; ao construtor, o compilador C++ emitir uma mensagem de erro. Isto evita erros de programao muito comuns. Recomenda-se a utilizao de const para argumentos ponteiros (referncias) que no devem ser modificados pelas funes. Deve-se tambm empregar const ao invs de se utilizar a diretiva de pr processamento #define quando se deseja definir constantes simblicas: const double PI = 3.1415926; A utilizao de const permite ao compilador verificar o tipo do valor constante, alm de permitir colocar a definio do identificador constante em uma classe, bloco ou funo. Deve-se notar que const restringe a maneira pela qual um objeto pode ser usado. Quando se utiliza um ponteiro, dois objetos esto envolvidos; o prprio ponteiro e o objeto apontado. Quando se deseja que o objeto apontado, mas no o ponteiro, fique inalterado, deve-se prefixar a declarao do ponteiro com a palavra reservada const. Por exemplo: const char* pc = "Nome"; pc[3] = 'a'; pc = "teste" // ponteiro para uma constante // erro // OK

Para declarar um ponteiro constante, ao invs do objeto apontado, o operador *const deve ser utilizado. Por exemplo: char *const cp = "Nome"; cp[3] = 'a'; cp = "Teste"; // ponteiro constante // OK // erro

Para fazer ambos objetos constantes eles devem ser declarados const. Por exemplo: const char *const cp = "Nome"; // ponteiro constante para objeto constante cp [3] = 'a'; // erro

24

cp = "Teste";

// erro

O endereo de uma constante no pode ser atribudo a um ponteiro irrestrito (um ponteiro que no aponta para um objeto constante) porque isto permitiria que o valor do objeto fosse mudado. Por exemplo: int a = 1; const int c = 2; const int* p1 = &c; const int *p2 = &a; int* p3 = &c; *p3 = 7;

// OK // OK // erro, pois // mudaria o valor de c

3.2.4 Funes Membros Constantes Na declarao da classe BigInt, mostrada na seo 2.3, pode-se notar que a palavra reservada const aparece depois da lista de argumentos na declarao das funes membros operator+() e print(). BigInt operator+(const BigInt&) const; // operador adicao void print (FILE* f = stdout) const; // funcao de impressao O que isto significa? Na seo 2.3.6 foi explicado que funes membros so aplicveis a instncias (objetos) de sua classe utilizando os operadores . e ->. Por exemplo: BigInt c = "29979250000"; // velocidade da luz (cm/s) c.print(); BigInt* cp = &c; cp-> print(); A palavra reservada const depois da lista de argumentos de uma funo membro informa ao compilador que a funo membro uma funo membro constante: uma funo membro que no modifica o objeto sobre a qual aplicada, de modo que esta funo pode ser aplicada a objetos constantes: const BigInt c = "29979250000"; // velocidade da luz (cm/s) c.print(); const BigInt* cp = &c; cp-> print(); O compilador C++ diria que estas chamadas a print() so ilegais caso se omitisse a palavra reservada const depois da lista de argumentos na declarao e definio de print(). Alm disso, o compilador C++ tambm verifica se uma funo membro constante no modifica nenhuma das variveis membros das instncias da classe. Sendo assim, por exemplo, se a definio da funo membro BigInt::print() contivesse a instruo: ndigitos--;

25

o compilador indicaria um erro, desde que isto muda o valor de uma das variveis membros da classe BigInt.

3.2.5 O Operador New Utiliza-se o operador new para alocuo de memria dinmica, necessria, por exemplo para manter os dgitos de um BigInt. Em C, para fazer isto, utilizaria-se a funo padro da biblioteca C, chamada malloc(). O operador new tem duas vantagens: Ele retorna um ponteiro para o tipo de dado apropriado. Enquanto que, para se alocar espao para as variveis membros de uma struct BigInt em C, teramos que escrever: struct BigInt* p; p = (struct BigInt*) malloc(sizeof (struct BigInt)); em C++, ns podemos escrever simplesmente: BigInt* p; p = new BigInt; A segunda vantagem que ao se utilizar o operador new para alocar uma instncia de uma classe, o construtor desta classe automaticamente chamado de modo a inicializar o objeto. 3.2.6 Declarao em Blocos Programadores em C podem ter observado que a declarao de p parece estar mal colocada em if (n!=0) { digitos = new char[ndigitos = n]; char* p = digitos; // uma instruo // uma declarao !!!!!!

uma vez que esta aparece depois da primeira instruo do bloco. Em C++, uma declarao pode aparecer em qualquer lugar dentro de um bloco desde que a varivel seja declarada antes de ser utilizada. A declarao efetiva at o fim do bloco em que ela ocorre. Pode-se frequentemente melhorar a legibilidade de um programa colocando-se as declaraes de variveis prximas aos lugares onde so empregadas. 3.2.7 O Construtor BigInt (unsigned) A implementao do construtor BigInt(unsigned), o qual cria um BigInt de um inteiro positivo, a seguinte:

26

BigInt:: BigInt(unsigned n) { char d[3*sizeof(unsigned)+1]; // buffer para digitos decimais char *dp = d; // ponteiro para o proximo digito decimal ndigitos = 0; do { // converte inteiros para digitos decimais *dp++ = n%10; n /= 10; ndigitos++; } while (n > 0); digitos = new char[ndigitos]; // aloca espaco para digitos decimais for (register i=0; nn.ndigitos ? ndigitos : n.ndigitos) + 1; char* sumPtr = new char [ maxDigitos ]; BigInt sum (sumPtr, maxDigitos); // deve-se definir este construtor unsigned i = maxDigitos - 1; unsigned carry = 0; while (i--) { *sumPtr = /* proximo digito de this */ + /* proximo digito de n */ + carry; if (*sumPtr >= 10) { carry = 1; *sumPtr = 10; } else carry = 0; sumPtr ++; } if (carry) // verifica se numero de digitos cresceu *sumPtr = 1; return sum; else { // elimina casa adicional i = maxDigitos - 1; char* p = new char [i]; char * q = sum.digitos; while (i--) *p++ = *q++; BigInt s (p, maxDigitos - 1); return s; } } A adio de dois BigInts feita utilizando o mtodo aprendido nas escolas de 1 grau; adiciona-se os dgitos de cada operando, da direita para esquerda, comeando do dgito mais a direita, e tambm somando-se um possvel transbordo (carry) da coluna anterior. H dois problemas quando se escreve a funo membro operator+(): Primeiro, h necessidade de se declarar uma instncia da classe BigInt chamada sum que conter o resultado da adio, o qual ser deixado em um array apontado por sumPtr. Deve-se utilizar um construtor para criar uma instncia de BigInt a partir de sumPtr, mas nenhum dos construtores disponveis at agora capaz de executar esta operao; sendo assim se faz necessrio escrever um outro construtor para tratar esta nova situao. Este novo construtor recebe como argumentos um array contendo os dgitos e os nmeros de dgitos do array e cria um objeto da classe BigInt a partir desses argumentos. Deve-se ressaltar que no conveniente que os programas clientes tenham acesso a essa funo dependente de implementao, sendo assim ns a declaramos na parte privada da classe BigInt, de modo que ela s possa ser acessada pelas funes membros da classe BigInt. Logo, a declarao:

30

BigInt(char* , unsigned); acrescentada antes da palavra public: na declarao da classe BigInt no arquivo BigInt.h, e a implementao deste construtor adicionada ao arquivo BigInt.cpp: BigInt:: BigInt(char* d, unsigned n) { digitos = d; ndigitos = n; } O segundo problema que a operao de percorrer os dgitos dos operandos na instruo *sumPtr = /* proximo digito de this */ + /* proximo digito de n */ + carry; pode se tornar complicada porque um dos operandos pode conter um nmero menor de dgitos que o outro. Neste caso deve-se complet-lo com zeros mais a esquerda. Este mesmo problema aparecer na implementao das operaes de subtrao, multiplicao e diviso, logo relevante encontrar uma soluo adequada para este problema. Uma boa soluo utilizar um tipo abstrato de dados ! Sendo assim, uma nova classe, chamada SeqDigitos, definida de forma a manter uma trilha de qual dgito em um certo BigInt est sendo processado. A classe SeqDigitos tem um construtor que recebe como um argumento uma referncia para um BigInt e inicializa uma SeqDigitos para esse BigInt. A classe SeqDigitos tambm tem uma funo membro que retorna o prximo dgito do BigInt cada vez que ela chamada, comeando no dgito mais a direita (menos significativo). A declarao da classe SeqDigitos e a implementao de suas funes membros so as seguintes: class SeqDigitos { char* dp; // ponteiro para o digito corrente unsigned nd; // numero de digitos restantes public: SeqDigitos (const BigInt& n ) // construtor { dp = n.digitos; nd = n.ndigitos; } unsigned operator++( ) // retorna o digito atual e avana para o // prximo digito { if (nd == 0) return 0; else { nd--; return *dp++; } } };

31

Pode-se agora declarar uma instncia da classe SeqDigitos para cada um dos operandos e utilizar o operador ++ quando se necessita ler o prximo dgito. Como esses dois problemas resolvidos, a implementao da funo membro operator+() a seguinte: BigInt BigInt::operator+(const BigInt& n ) const { unsigned maxdigitos = (ndigitos>n.ndigitos ? ndigitos : n.ndigitos) + 1; char* sumPtr = new char[maxdigitos]; BigInt sum (sumPtr, maxdigitos); // aloca memria para sum SeqDigitos a (*this); // mais sobre a varivel this posteriormente SeqDigitos b (n); unsigned i = maxdigitos - 1; unsigned carry = 0; while (i--) { *sumPtr = (a++) + (b++) + carry; if (*sumPtr >=10) { carry = 1; *sumPtr = 10; } else carry = 0; sumPtr++; } if (carry) // verifica se numero de digitos cresceu *sumPtr = 1; return sum; else { // elimina casa adicional i = maxDigitos - 1; char* p = new char [i]; char * q = sum.digitos; while (i--) *p++ = *q++; BigInt s (p, maxDigitos - 1); return s; } } Deve-se observar que ao sobrecarregar o operador ++ e -- para uma classe, C++ no faz nenhuma distino entre a forma pr-fixada e ps-fixada desses operadores. 3.2.11 Funes Friend Voc deve estar se perguntando como o construtor SeqDigitos(const BigInt&) capaz de acessar os membros privativos digitos e ndigitos da classe BigInt. De fato, isso no possvel. Sendo assim h a necessidade de se conceder acesso a essas variveis privativas funo construtoras da classe SeqDigitos, mas somente a essa funo e nenhuma outra. C++ proporciona uma maneira de fazer isto - tornando o construtor friend da classe BigInt, isto pode ser feito adicionando-se a seguinte declarao a especificao da classe BigInt:

32

friend SeqDigitos::SeqDigitos(const BigInt&); Pode-se tambm fazer todas as funes membros de uma classe friend de outra declarando um classe inteira como um friend. Por exemplo, pode-se tornar todas as funes membros da classe SeqDigitos friends da classe BigInt declarando-se na especificao da classe BigInt friend class SeqDigitos; 3.2.12 A Palavra Reservada This Retornando a implementao da funo BigInt::operator+(), observa-se a utilizao da varivel ponteiro this na seguinte declarao: SeqDigitos a (*this); C++ automaticamente declara uma varivel chamada de this em toda a funo membro de uma classe e a inicializa para apontar para a instncia da classe a qual a funo membro foi aplicada. Sendo assim, na funo membro operator+() da classe BigInt, this implicitamente declarada como: BigInt* this; e quando operator+() chamada, escrevendo-se uma expresso como b + 47, onde b um BigInt, this automaticamente inicializada para apontar para b. Logo, o efeito da declarao SeqDigitos a(*this) na funo operator+() criar uma instncia de SeqDigitos para o operando da esquerda do operator+(), neste caso b. Analogamente, quando uma funo membro tal como BigInt::print() chamada por uma expresso como c.print(), onde c um BigInt, this inicializado para apontar para c. 3.2.13 O Operador Atribuio da Classe BigInt O objetivo do operador atribuio (=) copiar o valor de um BigInt ( o argumento da direita) para outro (o argumento da esquerda). Embora o operador de atribuio seja similar ao construtor cpia BigInt(const BigInt&), h uma importante diferena: o construtor copia o valor para uma instncia no inicializada de BigInt, enquanto o operador atribuio copia o valor dentro de uma instncia inicializada de BigInt, isto , uma que j contm um valor. void BigInt::operator=(const BigInt& n) { if (this == &n) return; // para manipular x = x corretamente delete digitos; unsigned i = n.digitos; digitos = new char[n.digitos=i];

33

char* p = digitos; char* q = n.digitos; while (i--) *p++ = *q++; } Deve-se ressaltar que o corpo da funo operator=() idntico ao do construtor de cpia BigInt (const BigInt&), exceto as duas primeiras instrues. A primeira instruo verifica se o endereo do operando da esquerda o mesmo do operando da direita. Se for, nada necessita ser feito. Caso contrrio, a memria dinmica para os dgitos do operando da esquerda liberada, chamando-se o operador delete. 3.2.14 A Funo Membro BigInt::print() A implementao da funo membro constante print() simples e direta: void BigInt::print(FILE* f) const { for (int i = ndigitos-1, i>=0, i--) fprintf(f, "%d", digitos[i]); } 3.2.15 O Destrutor da Classe BigInt A nica coisa que a funo destrutora ~BigInt() da classe BigInt deve fazer liberar a memria dinmica alocada pelos construtores: BigInt::~BigInt() { delete digitos; } Isto feito utilizando-se o operador delete de C++, o qual, neste caso, libera a memria dinmica que apontada por digitos. Ao se utilizar o operador delete para desalocar uma instncia (objeto) de uma classe que tem uma funo destrutora definida, o destrutor automaticamente chamado para finalizar a instncia da classe antes que a memria seja liberada. Por exemplo: BigInt* a = new BigInt("9834567843"); //... delete a; automaticamente executar-se- a->~BigInt(), que desalocar a memria apontada por digitos antes de desalocar a memria para as variveis membros das instncias da classe BigInt. A funo destrutora deve ser chamada para cada elemento de um array quando este array destrudo. Isto feito implicitamente para arrays que no so alocados utilizando-se new. No entanto, isso no pode ser feito implicitamente para arrays alocados na memria dinmica porque o compilador no consegue determinar

34

se um ponteiro aponta para um objeto nico ou para o primeiro elemento do array de objetos. Por exemplo: void f() { BigInt* a = new BigInt; BigInt* b = new BigInt[10]; delete a; // OK delete b; // Problemas!!! } Neste caso deve-se especificar que b aponta para um array: void f() { BigInt* a = new BigInt; BigInt* b = new BigInt[10]; delete a; // OK delete[10] b; // OK!!! } Desta forma, sabe-se quantas instncias de classe o array contm, podendo assim chamar o destrutor para cada instncia do array. 3.2.16 Funes Inline A utilizao de funes membros pequenas comum quando se programa orientado a objetos. O emprego de tais funes, pode gerar uma considervel ineficincia devido ao custo de uma chamada de funo. Sendo assim, C++ permite que se declare uma funo como inline. Nesse caso, cada chamada de funo substituda por uma cpia da funo inteira, semelhante a expanso de macro. Isso elimina o custo de uma chamada de funo. H duas maneiras de se definir uma funo como sendo inline: funes inline definidas dentro da especificao da classe - Quando uma funo membro definida dentro da prpria classe, o compilador faz uma chamada em linha dessa funo toda vez que o compilador a chamar. Por exemplo, o destrutor da classe BigInt. funes inline definidas fora da especificao da classe - Para que uma funo membro seja inline sem ser definida dentro da classe, deve-se colocar a palavra reservada inline antes da definio da funo. Deve-se recomendar, contudo, um certo cuidado ao se utilizar funes inline. As funes candidatas a serem colocadas em linha so as que apresentam pouco cdigo e este cdigo tambm bastante simples.

35

36

Captulo IV - Conceitos de Programao Orientada a Objetos em C++

4.1 IntroduoAs caractersticas mais interessantes da linguagem C++ so aquelas que suportam o estilo de Programao Orientada a Objetos (POO). Programao orientada a objetos uma tcnica de organizao de tipos abstratos de dados que explora as caractersticas em comum destes tipos de modo a reduzir o esforo de programao atravs da reutilizao de cdigo. Esse estilo de programao se caracteriza por: Encapsulamento Herana Amarrao Dinmica Nos captulos anteriores discutiu-se encapsulamento, que restringe os programas clientes a interagirem com as instncias da classe somente por meio de um conjunto bem definido de operaes: a interface (especificao) da classe. Isso evita que os programas clientes conheam detalhes de como uma classe representa os dados que ela contm e dos algoritmos que ela utiliza, ou seja, da implementao de uma classe. Isso faz com que as mudanas (corretas) que ocorram na implementao de uma classe no tenham nenhum efeito permissivo nos programas clientes desta classe, alm de facilitar o processo de depurao e manuteno porque o cdigo que lida com os dados de uma classe est localizado na prpria classe. Herana simplifica a tarefa de criao de uma nova classe que seja similar a uma classe existente, uma vez que permite ao programador expressar apenas as diferenas existentes entre a nova classe e a classe existente, ao invs de exigir que o programador crie uma classe a partir do comeo. Amarrao Dinmica, ou amarrao tardia, ajuda a fazer um programa cliente mais geral, ocultando as diferenas entre um grupo de classes que esto relacionadas entre si. Amarrao Dinmica permite que cada classe de um grupo de classes relacionadas tenha uma implementao diferente para uma funo particular. Programas clientes podem aplicar a operao a uma instncia de classe sem ter que considerar a classe especfica da instncia. Em tempo de execuo, determina-se a classe especfica da instncia e chama-se a implementao da funo para esta classe. Sendo assim, encapsulamento, herana e amarrao dinmica trabalham juntas: Encapsulamento assegura que os programas cliente interagem com os objetos somente por meio das funes membros.

37

Herana proporciona um meio de se expressar as diferenas entre as classes, mas permitindo o reuso do cdigo e das variveis membros para implementar as caractersticas similares entre elas. Amarrao Dinmica das funes usadas pelos programas clientes oculta as diferenas entre as classes expressas via herana. Foi visto anteriormente que C++ suporta encapsulamento atravs da definio dos atributos pblicos e privados de uma classe. C++ suporta herana por meio de classes derivadas e suporta amarrao dinmica por meio de funes virtuais. O presente captulo introduzir as caractersticas de C++ que suportam o estilo de programao orientada a objetos, em especial herana e amarrao dinmica. 4.1.1 Classes Derivadas O mecanismo de herana (B herda de A) associa classe que est sendo declarada (B): Uma representao inicial para os seus objetos (aquelas dos objetos de A); Um conjunto inicial de mtodos aplicveis aos objetos desta classe (aqueles da classe A). A nova declarao de classe pode conter representao e operaes adicionais, especializando estado e o comportamento de novos objetos a serem criados. Deve-se notar, contudo, que os mtodos iniciais so ainda aplicveis aos objetos desta nova classe. Esta visibilidade tem relao com o conceito de subtipo. Suponha uma classe Pessoa em C++, como declarada abaixo: class Pessoa { char* nome; int idade; public Pessoa ( char*auxnome, int a) {nome=auxnome; idade=a;} void MudarIdade (int a) {idade=a;} virtual void Print ( ); }; Suponha tambm que se necessite representar empregados em um programa. Uma vez que todo o empregado basicamente uma pessoa pode-se tomar vantagem da declarao existente para pessoa, declarando-se uma nova classe Empregado derivada da classe Pessoa: class Empregado: public Pessoa { };

38

Com esta declarao pode-se criar instncias de Empregado (objetos): Empregado e1, e2; Contudo, nada foi modificado at agora, uma vez que empregados so exatamente iguais a pessoas, tanto na estrutura de dados quanto no comportamento. Isso se deve ao fato de que nenhuma declarao adicional foi acrescentada localmente a Empregado. Obviamente faz-se necessrio refinar a classe Pessoa de maneira a representar um empregado. Isto pode ser feito adicionando-se novas estruturas de dados e operaes dentro da classe Empregado, como mostrado abaixo: class Empregado : public Pessoa { float salario; public: Empregado(char *auxnome, int auidade, float auxsal) : Pessoa (auxnome, auxidade) { salario=auxsal;} void MudarSalario (float r) { salario = r; } void Print (); }; Com essa nova declarao da classe Empregado, o mecanismo de herana estabelece que: todo empregado tem a seguinte estrutura de dados: nome, idade e salario. todo empregado pode receber as seguintes operaes: MudarIdade, MudarSalario, Print. Pode-se notar que uma operao para mudar a idade de um empregado no necessita ser redeclarada na classe Empregado; o mtodo MudarIdade na classe Pessoa serve perfeitamente para mudar a idade de um empregado. Sendo assim, pode-se escrever: e1.MudarIdade (56); mudando a idade de e1 para 56 anos. Uma classe derivada herda todos os atributos (variveis membros e funes) da classe base. Pode-se, contudo, diferenciar a classe derivada de sua classe base de trs formas: adicionando variveis membros; adicionando funes membros; redeclarando funes membros herdadas da classe base. Uma classe base pode ter mais de uma classe derivada, uma classe derivada pode, por sua vez, servir como classe base para outras classes derivadas. Uma classe derivada normalmente mais especializada que sua classe base. 4.1.2 Redeclarao - Funes Membros Virtuais

39

Uma outra caracterstica relacionada com o mecanismo de herana a habilidade de redeclarar operaes na subclasse. Atravs da redeclarao ns objetivamos a criao, na subclasse, de uma nova operao com a mesma interface (nome, parmetros e resultado) que uma operao na superclasse. Se uma operao da superclasse redeclarada em uma subclasse, ento a nova operao visvel para os usurios da subclasse. Contudo, a operao da superclasse tambm aplicvel dentro da subclasse desde que o nome da subclasse preceda o nome da operao (em caso contrrio, uma chamada recursiva ocorrer) . A operao redeclarada tem a habilidade de manipular a representao de dados inteira da subclasse, chamando a operao homnima da superclasse para lidar com a representao comum, e incluindo declaraes para lidar com a representao adicional. Por exemplo: void Empregado::Print ( ) { Pessoa :: Print ( ); cout salario \n; } A palavra chave virtual em C++ indica que a funo Print da classe Pessoa pode ter diferentes verses para diferentes classe derivadas. Quando um programa cliente aplica uma funo virtual a um objeto, C++ automaticamente determina a que classe o objeto pertence, em tempo de execuo, e transfere o controle para a implementao correta da funo. Isso ocorre mesmo quando o programa cliente trata o objeto como uma instncia da classe base. Deve-se ressaltar que uma funo virtual deve ser definida na primeira classe onde ela declarada. Uma classe base com mltiplas classes derivadas, alm de permitir a eliminao de cdigo redundante, tambm cria uma oportunidade de se fazer programa clientes mais gerais.

4.2 Um Exemplo de AplicaoNesta seo ser desenvolvido um programa grfico simples que desenha algumas figuras geomtricas em duas dimenses. Este programa utilizar os conceitos de encapsulamento, herana e amarrao dinmica. 4.2.1 Classe Ponto Esse exemplo utiliza um tipo abstrato de dados, chamado de classe Ponto, para executar operaes sob pares de coordenadas (x, y): // Hierarquia de classes geometria class Ponto{ int xc, yc; public: Ponto ( ) Ponto (int novox, int novoy) int x ( ) const int x (int novox) // coordenadas x, y {xc = yc = 0;} {xc = novox; yc = novoy} {return xc;} {return xc = novox;}

40

int y ( ) const {return xy;} int y (int novoy) {return yc = novoy;} Ponto operator+ (const Ponto&p) const { return Ponto (xc + p.xc, yc + p.yc); } void operator+= (const Ponto & p) { xc += p.x ( ); yc += p.y ( ); } void printOn() const { cout