Você está na página 1de 81

PROGRAMAO ORIENTADA A

OBJETOS EM




BERILHES BORGES GARCIA
FLVIO MIGUEL VAREJO

COLABORAO: FABRCIA DOS SANTOS NASCIMENTO

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

2
4.2.6 CLASSE QUADRO ............................................................................................................................ 44
4.2.7 COMPATIBILIDADE DE TIPOS ........................................................................................................... 46
4.2.8 CLASSE INCOMPLETAS - FUNES VIRTUAIS PURAS ...................................................................... 47
4.3 O EXEMPLO GEOMTRICO MELHORADO ......................................................................... 48
4.3.1 INICIALIZAO DE OBJETOS............................................................................................................ 50
4.3.2 FINALIZAO DE OBJETOS .............................................................................................................. 52
4.4 HERAA MLTIPLA................................................................................................................ 53
4.4.1 AMBIGUIDADES EM HERANA MLTIPLA ....................................................................................... 61




























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 Dados

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

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





4
1.2 Histrico

A 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

5
Formas genricas cujos tipos particulares ns no conhecemos quando escrevemos o
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.


















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,
1950
1960
1970
1980
1990
CLOS
Miranda
ML
Prolog
Lisp
Cobol
PL 1
Algol 68
Algol 60
Pascal
Simula 67
Smalltalk
Ada
C++
Eiffel
Fortran
C

6
que a passagem de um ponteiro como parmetro. C++ resolve este problema de uma
forma elegante e eficiente.

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.






















7


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 Introduo

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




8










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
#include <stdio.h>

main ()
{
int a = 193;
int b = 456;
int c;
c = a + b + 47;
printf("%d\n",c);
}

9
strings de caracteres contendo somente dgitos. Sendo assim, o compilador C++ deve
ser capaz de converter strings de caracteres em BigInts.













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
# include "BigInt.h"

main()
{
BigInt a = "25123654789456";
BigInt b = "456023398798362";
BigInt c;
c = a + b + 47;
c.print();
printf("\n");
}


10
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
mantidos em um arquivo parte. Em nosso exemplo, este arquivo chamado
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
programa executvel.
















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 Especificao

2.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):


BigInt.cpp
Client.cpp
BigInt.h
Compilador
C++
Compilador
C++
Client.exe
Client.o
BigInt.o
Linker


11
#include <stdio.h>
// 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.

12

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.










Figura 3 - Uma Instncia da Classe BigInt Representando o mero "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. Necessita-
se, 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.

digitos
ndigitos
6
1 5 4 3 2 6

13

Este princpio visa garantir que mudanas na implementao (representao)
de uma classe no afetem os clientes desta classe. Por conseguinte, torna-se mais fcil
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 membros
1
.

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.


















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.

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

BigInt c = a + b +47;

c.print();
char* digitos

int ndigitos
BigInt(const char*)
(construir)

BigInt(const BigInt&)
(inicializar)

operador+(const BigInt&)
(somar)

print()

Comment [nenhum1]:

14

2.3.4 Overloading

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

15

legal, mas as declaraes

unsigned Maximo (unsigned a, unsigned b=0, unsigned c);
unsigned Maximo (unsigned a=0, unsigned b, unsigned c);

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.


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

17

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.

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

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

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;

2
O argumento de BigInt& um exemplo de uma referncia em C++, que ser posteriormente descrita.

19

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&); // atribuio
BigInt operator+(const BigInt&) const; // operador adio

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

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

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

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

A implementao requer as informaes contidas na especificao, sendo
assim a primeira linha no arquivo BigInt.cpp :

#include "BigInt.h";

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:












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

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:

23

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 = "ome"; // ponteiro para uma constante
pc[3] = 'a'; // erro
pc = "teste" // OK

Para declarar um ponteiro constante, ao invs do objeto apontado, o operador
*const deve ser utilizado. Por exemplo:

char *const cp = "ome"; // ponteiro constante
cp[3] = 'a'; // OK
cp = "Teste"; // erro

Para fazer ambos objetos constantes eles devem ser declarados const. Por
exemplo:

const char *const cp = "ome"; // ponteiro constante para objeto constante
cp [3] = 'a'; // erro
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; // OK
const int *p2 = &a; // OK
int* p3 = &c; // erro, pois
*p3 = 7; // mudaria o valor de c

3.2.4 Funes Membros Constantes


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

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:


25
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]; // uma instruo
char* p = digitos; // 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:

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

26

for (register i=0; n<ndigitos, i++) digitos[i] = d[i];
}

Este construtor trabalha convertendo um argumento inteiro para dgitos
decimais no array temporrio d. Sabe-se, ento, quanto espao alocar para um BigInt,
e possvel, portanto, alocar a memria dinmica utilizando-se o operador new, e
copiar os dgitos decimais do array temporrio para esta rea.

3.2.8 O Construtor Cpia

A funo do construtor cpia copiar o valor de um BigInt passado como
argumento para uma nova instncia de classe BigInt:

BigInt::BigInt(const BigInt& n)
{
unsigned i = n.ndigitos;
digitos = new char[ndigitos = i];
char* p = digitos;
char * q = n.digitos;
while (i--) *p++ = *q++;
}

importante observar o acesso a n.ndigitos dentro do construtor de cpia. Se n
fosse um parmetro de outra classe diferente de BigInt, o acesso a estrutura privada de
n seria ilegal. Contudo, como o parmetro da prpria classe BigInt, C++ permite que
a sua estrutura interna seja acessada. Por conta disso, considera-se que C++ mais
orientado a classes do que a objetos.

Tambm cabe destacar que a funo construtora de cpia faz uso de uma
referncia, uma importante caracterstica de C++ que ser descrita a seguir.

3.2.9 Referncias

O parmetro formal da funo membro BigInt(const BigInt&) um exemplo
de uma referncia em C++. Referncias em C++ objetivam superar a ausncia de
mecanismos de passagem de parmetros por referncia em C.

Afim de entender o que isto significa, suponha que se deseja escrever uma
funo chamada inc() que adiciona um ao seu argumento. Se esta funo fosse escrita
em C, seria da seguinte forma:

void inc (int x)
{
x++;
}


27
e fosse utilizada pelo seguinte segmento de programa



int y = 1;
inc(y);
printf("%d\n",y);

o programa imprimiria um 1, e no 2. Isto porque em C o valor y copiado dentro do
parmetro formal x e a instruo x++ incrementa esta cpia, deixando o valor de y
inalterado. Este mecanismo de passagem de parmetros conhecido com passagem
por valor.

A funo inc() deve ser escrita em C da seguinte forma:

void inc (int* x)
{
*x++;
}

e o segmento de cdigo que utiliza essa funo deve ser o seguinte:

int y = 1;
inc(&y);
printf("%d\n",y);

Note que trs modificaes devem ser feitas:
o tipo do argumento da funo deve mudae de int para int*;
cada ocorrncia do argumento no corpo da funo deve mudar de x para *x;
cada chamada da funo deve mudar de inc(y) para inc(&y).

Utilizando-se a passagem por referncia de C++, a funo inc() poderia ser
escrita da seguinte forma:

void inc(int& x)
{
x++;
}

e o segmento de cdigo que utiliza esta funo seria:

int y = 1;
inc(y);
printf("%d\n",y);

A utilizao do mecanismo de passagem de parmetros por referncia
simplifica, em muitos casos, a funo e a utilizao desta funo.


28
Pode-se pensar em uma referncia como sendo um ponteiro que
automaticamente derreferenciado quando utilizado. Pode-se declarar variveis que so
referncias. Por exemplo:


int i;
int* p = &i; // um ponteiro para i
int& r = i; // uma referencia para i
r++; // incrementa o valor de i

Voc pode utilizar r em lugar de *p e &r em lugar de p. As nicas diferenas
so:

Uma referncia deve ser inicializada em sua declarao.
Uma referencia no pode ser apontada para um objeto diferente daquele para
o qual ela foi iniciada.

Sendo assim:

int j, i;
int* p = &i; // um ponteiro para i
int& r = i; // uma referencia para i
*p = 1; // ajusta i para 1
j = *p; // ajusta j para 1
r = 2; // ajusta i para 2
j = r; // ajusta j para 2
p = &j; // ajusta p para apontar para j
&r = &j; // ilegal

Uma referncia um nome alternativo (apelido) para um objeto que
proporciona uma sintaxe mais adequada e segura que a de ponteiros, contudo h
situaes que demandam o emprego de ponteiros.

3.2.10 O Operador Adio da Classe BigInt

A implementao da funo membro operator+() a seguinte:


29
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); // 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

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

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.



33
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];
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;


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

35

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.



























36


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



4.1 Introduo

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


37
Encapsulamento assegura que os programas cliente interagem com os objetos
somente por meio das funes membros.

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:

38


class Empregado: public Pessoa
{
};

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.


39
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

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 Aplicao

Nesta 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):

40

// Hierarquia de classes geometria

class Ponto{
int xc, yc; // coordenadas x, y
public:
Ponto ( ) {xc = yc = 0;}
Ponto (int novox, int novoy) {xc = novox; yc = novoy}
int x ( ) const {return xc;}
int x (int novox) {return xc = novox;}
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 << "( " << xc << ", " << yc << " )";
}
}

ostream& operator<< (ostream& strm, const Ponto& p)
{
return strm << "( " << p.x() << ", " << p.y() << " )";
}

A classe Ponto no utiliza nenhuma caracterstica de C++ que no tenha sido
discutida anteriormente. Para que se possa utilizar o operador << para objetos da
classe Ponto necessrio sobrecarreg-lo. Maiores detalhes de como sobrecarregar o
operador << sero discutidos posteriormente.

4.2.2 Classe Linha e Classe Circulo

O exemplo geomtrico requer algumas classes para representar vrias formas
geomtricas, como linha, retngulo, tringulo e crculo. Todas essas classes tm
algumas operaes em comum, como por exemplo, desenhar( ) e mover( ), e todas
tm uma varivel membro, org, que contm as coordenadas de origem da forma
geomtrica. Pode-se comear escrevendo-se declaraes representativas para as
classes Linha e Circulo, como por exemplo:






41
class Linha{
Ponto org; // origem
Ponto p; // ponto de fim
public:

Linha (const Ponto & a, const Ponto & b): org (a.x(),a.y()), p(b.x(),b.y()) {}
void mover (const Ponto & d); // move linha de um montante d
void desenhar() const ; // desenha uma linha de org ate p
};


class Circulo {
Ponto org; // origem
int raio; // raio do circulo
public:
Circulo(const Ponto & c, int r): org (c.x(),c.y()) { raio = r;}
void mover(const Ponto & d); // move um circulo com centro org
void desenhar( ) const; // desenha um circulo com centro org e raio r
};

4.2.3 Instncias de Classe como Variveis Membros de uma Classe

Pode-se ver, dos exemplos acima, que uma instncia de uma classe pode ser
uma varivel membro de outra classe: as variveis membro Linha::org, Linha::p,
Circulo::org so instncias da classe Ponto. Quando isto ocorre, o construtor da classe
pode necessitar utilizar uma notao especial, para inicializar as variveis membros
que aparecem como instncias de uma classe. Entre a lista de argumentos da funo e
o corpo da funo, escreve-se o nome das variveis membros que se deseja inicializar,
seguido por uma lista de argumentos que se deseja passar para os construtores das
variveis membros:

Linha (const Ponto& a, const Ponto& b ): org (a.x(),a.y()), p (b.x(),b.y()) {}
Circulo (const Ponto& c, int r): org (c.x(),c.y()) {raio = r;}

O construtor de cada uma das variveis membros processado antes que as
instrues no corpo do construtor da classe seja executado.

Pode-se tambm escrever:

Circulo (const Ponto & c, int r)
{
org = c;
raio = r;
}

mas provvel que isto no seja to eficiente. Relembre que, se uma classe
declara um ou mais construtores, ento C++ garante que um deles ser chamado para

42
inicializar toda instncia da classe. Relembre tambm que a atribuio org = c assume
que org j est inicializado.

4.2.4 A Funo Membro mover()

As funes membros Linha::mover() e Circulo::mover() aceitam um Ponto
como argumento, representando um deslocamento (x, y), e ento os adiciona as
variveis membros:

void Linha::mover(const Ponto& d)
{
org += d;
p += d;
}

void Circulo::mover(const Ponto& d)
{
org += d;
}

4.2.5 A Funo Membro desenhar()

As implementaes das funes membros das classes Linha e Circulos esto
completas a menos para as funes Linha::desenhar e Circulo::desenhar(). Afim de
manter estes exemplos simples, os detalhes de como se desenhar estas formas em um
dispositivo grfico particular ser omitido, sendo assim a implementao apenas
imprimir o tipo da forma e suas coordenadas:

void Linha::desenhar() const
{
cout << "Linha de " << org << "at" << p << "\n";
}

void Circulo::desenhar() const
{
cout << "Circulo com centro em " << org << " e raio de " << raio << "\n";
}

Seria til ter um tipo abstrato de dados chamado de Quadro que seria uma
coleo de Linhas, Triangulos, Retangulos e Circulos. Tambm seria til ser capaz de
desenhar() e mover() os quadros.

Seria extremamente elegante se a classe Quadro fosse geral, e no contivesse
nenhuma meno a formas especficas. Desta maneira, poderia se introduzir um nova
forma, por exemplo trapzio, sem que haja necessidade de se alterar a classe Quadro.
Pode-se fazer isto definindo-se um classe base Forma com classes derivadas Linha,
Triangulo, e assim sucessivamente, como mostrado na figura 5.

43









Figura 5 - Hierarquia de Exemplo

A primeira verso da classe Forma declara funes, tais como desenhar() e
mover(), como sendo funes virtuais. Estas funes podem ser aplicadas a qualquer
tipo de forma e, so implementadas para que escrevam uma mensagem de erro se
chamadas:

class Forma {
public:
virtual void mover (const Ponto&);
virtual void desenhar() const;
};

void Forma::mover (const Ponto& p)
{
cout << "Esqueceu de implementar mover()! \n";
exit (1);
}

void Forma::desenhar () const
{
cout << "Esqueceu de implementar desenhar()! \n";
exit (1);
}

Deve-se mudar as declaraes da classe Linha, Circulo e, assim
sucessivamente para faz-las classes derivadas (herdeiras) da classe Forma, isto
feito adicionando-se o nome da classe base Forma, s declaraes das classes
derivadas:

class Linha: public Forma {
Ponto org; // origem
Ponto p; // ponto de fim
public:
Linha (const Ponto& a, const Ponto& b): org (a.x(), a.y()), p(b.x(), b.y()) {}
virtual void mover (const Ponto& d); // mover linha um montante d
virtual void desenhar () const; // desenha uma linha de org ate p
};


Forma
Quadro Circulo Triangulo Linha

44

class Circulo: public Forma {
Ponto org; // origem
int raio; // raio do circulo
public:
Circulo (const Ponto& c, int r): org(c.x(), c.y()) { raio = r; }
virtual void mover (const Ponto& d); // move um circulo de um montante d
virtual void desenhar() const; // desenha um circulo com centro org
// e raio raio
};

Utilizando-se da palavra reservada public (especificador de acessabilidade)
antes do nome da classe base torna os membros pblicos da classe base membros
pblicos da classe derivada, de modo que os clientes da classe derivada podem ter
acesso a eles. A omisso da palavra reservada public torna os membros pblicos da
classe base membros privados da classe derivada, e, portanto, inacessveis aos clientes
da classe derivada.

A palavra reservada virtual tambm adicionada s declaraes das funes
desenhar() e mover() nas classes derivadas, mas no h motivo para mudar a
implementao destas funes
3
.

4.2.6 Classe Quadro

Pode-se agora escrever a classe Quadro para lidar somente com a classe
Forma e no suas classes derivadas Linha, Triangulo, Retangulo e Circulo, etc. Pode-
se representar um Quadro como um array contendo ponteiros para as suas formas
componentes. Pode-se, tambm, implementar Quadro::desenhar(), por exemplo,
simplesmente chamando-se desenhar() para cada forma no quadro:

const unsigned CAPACIDADE_QUADRO = 100;

class Quadro: public Forma {
Ponto org; // origem
Forma* s[CAPACIDADE_QUADRO]; // ponteiro de array para formas
int n; // numero de formas neste quadro
public:
Quadro() {n = 0;} // construtor
Quadro(Ponto& o): org (o.x(), o.y()) {n = 0;} // construtor
void adicionar (Forma&); // adicionar forma a quadro
virtual void mover (const Ponto& d); // mover quadro
virtual void desenhar() const; // desenhar quadro
};


3
A palavra reservada virtual requerida somente nas declaraes das funes na classe base. Pode-se
omitir a palavra virtual nas classes derivadas - contudo considera-se um bom estilo de programao,
porque torna possvel identificar quais funes so virtuais sem ter que olhar as declaraes na classe
base.

45
void Quadro::adicionar(Forma & t)
{
if (n == CAPACIDADE_QUADRO) {
cerr << "Capacidade do quadro excedida\n";
exit(1);
}
s[n++] = &t; // adicionar ponteiro para forma a quadro
}

void Quadro::mover(const Ponto& d)
{
org += d;
for (int i = 0; i < n; i++) s[i]->mover(d);
}

void Quadro::desenhar() const // desenhar um Quadro
{
for (int i = 0; i<n; i++) s[i]->desenhar();
}

Uma vez que desenhar() uma funo virtual, C++ tem o cuidado de
determinar a classe especfica de cada componente Forma quando o programa
executado e ento chama a implementao apropriada de desenhar() para cada classe.
Isto chamado de amarrao dinmica, em contraste com a chamada de funes no-
virtuais, as quais empregam amarrao esttica. Por exemplo, se em tempo de
execuo s[i] aponta para uma instncia da classe Linha, ento a instruo
s[i]->desenhar() chama Linha::desenhar(); se s[i] aponta para uma instncia de um
Circulo, chama-se Circulo::desenhar(), e assim sucessivamente. Considere o seguinte
programa exemplo:

main()
{
Linha l(Ponto(1,2), Ponto(3,4)); // cria uma linha
Circulo c(Ponto(5,6),1); // cria um circulo
l.desenhar(); // desenha uma linha
c.desenhar(); // desenha um circulo
Quadro q; //cria um quadro vazio
l.mover(Ponto(1,0)); // move a linha
q.adicionar(l); // adiciona a linha ao quadro
c.mover(Ponto(1,0)); // move o circulo
q.adicionar(c); // adiciona o circulo ao quadro
q.desenhar(); // desenha o quadro
q.mover(Ponto(10,10)); // translada o quadro para (10,10)
q.desenhar(); // desenha o quadro novamente
}

Quando se executa este programa, o resultado obtido :

Linha de (1,2) ate (3,4)

46
Circulo com centro em (5,6) e raio 1
Linha de (2,2) ate (4,4)
Circulo com centro em (6,6) e raio 1
Linha de (12,12) ate (14,14)
Circulo com centro em (16,16) e raio 1

Amarrao dinmica de uma chamada de funo virtual ocorre somente
quando a funo virtual aplicada a um ponteiro ou referncia a um objeto, como em
Quadro::desenhar(), e no quando aplicada diretamente a um objeto:

void desenharIsto(Forma& referencia)
{
Linha linha(Ponto(1,2), Ponto(3,4));
Forma* ponteiro = &linha;
//...
ponteiro->desenhar(); // amarrado dinamicamente
referencia.desenhar(); // amarrado dinamicamente
linha.desenhar(); // amarrado estaticamente
}

Em tempo de execuo, as variveis ponteiro e referncia podem apontar para
uma instncia de qualquer classe derivada de Forma. Sendo assim, C++ utiliza
amarrao dinmica para determinar qual implementao de desenhar deve chamar.
No exemplo, a varivel linha uma instncia da classe Linha, portanto, C++
estaticamente amarra a chamada de linha.desenhar() a Linha::desenhar() e evita a
sobrecarga da amarrao dinmica.

4.2.7 Compatibilidade de Tipos

Porque o compilador C++ no acusa erro de compatibilidade de tipos nas
seguintes chamadas de funes: q.adicionar(l) e q.adicionar(c), uma vez que a
declarao para Quadro.adicionar() especifica que esta funo requer um argumento
do tipo Forma&, e o argumento passado para estas duas chamadas de funes so do
tipo Linha e Circulo, respectivamente. C++ permite isto porque Linha e Circulo so
classes derivadas da classe Forma. Em geral, C++ permite que uma referncia ou
ponteiro para uma instncia de uma classe derivada seja utilizada em lugar de uma
referncia ou ponteiro para a sua classe base sem que seja necessrio uma converso
de tipos explcita (cast). Pode-se, portanto, entender uma classe derivada como sendo
um subtipo de sua classe base. Subtipos so teis e no comprometem a segurana
porque classes derivadas herdam todas as variveis membros e funes de sua classe
base; sendo assim, uma referncia a qualquer varivel membro ou chamada de funo
membro de uma instncia da classe base est bem definida para uma instncia da
classe derivada. Pode-se pensar em Linha e Circulo como sendo espcies diferentes
(subtipos) de Formas.

O oposto, contudo, no verdade: C++ no permite que uma referncia ou um
ponteiro para uma instncia de uma classe base seja utilizada em lugar de referncia
ou ponteiro para uma instncia de uma classe derivada. Isto reflete o fato de que uma

47
Linha ou Circulo uma espcie de Forma, mas uma Forma no um tipo de Linha
ou Circulo.

4.2.8 Classe Incompletas - Funes Virtuais Puras

Classes tambm podem ser incompletas. Isto ocorre quando uma ou mais
operaes so declaradas em uma classe, mas no so implementadas. A
implementao destas operaes deixada para as classes dependentes. Isto
conveniente para disciplinar a construo de classes: para cada operao incompleta
em uma superclasse, todas as subclasses herdeiras desta superclasse devem
implementar ou virtualizar esta operao. Classes incompletas no modelam objetos,
mesmo se uma ou mais de suas operaes est completamente declarada.

Afim de exemplificar a convenincia das operaes virtuais puras, considere
novamente a classe Forma descrita anteriormente. Pode-se transformar esta classe em
uma classe incompleta da seguinte forma:

class Forma {
public:
virtual void mover(const Ponto&) =0;
virtual void desenhar() const =0;
};

O inicializador =0 nas declaraes de mover() e desenhar() indicam ao
compilador C++ que estas so funes virtuais puras: funes virtuais que a classe
base no implementa, e que, portanto, devem ser definidas pelas classes derivadas. H
duas consequncias de se declarar um funo virtual pura na classe base:
O compilador verifica cada classe derivada para assegurar que elas
implementam a funo virtual pura da classe base ou adiam sua implementao
(declarando-a novamente como funo virtual pura a ser implementada pelas suas
classes derivadas).
No se pode criar instncias de uma classe incompleta. Sendo assim, a
seguinte declarao ilegal:

Forma f;

As vantagens da utilizao de declaraes incompletas podem ser resumidas
da seguinte forma:


Melhoria da organizao hierrquica, atravs do encapsulamento de propriedades
na raiz da estrutura;
Uma maior disciplina na programao, j que fora o comportamento necessrio
nas classes descendentes;
Incentiva o uso de polimorfismo, permitindo um comportamento mais abstrato e
genrico para os objetos.

48
Deve-se ressaltar que nem todas as classes bases so classes abstratas. Por
exemplo, pode-se implementar uma classe desenhando arcos com uma classe derivada
da classe Circulo:

class Arco : public Circulo {
float inicio, fim; // angulo de inicio e de fim do arco
public:
Arco(const Ponto& c, int r, float a1, float a2) : Circulo(c,r) {
inicio = a1;
fim = a2;
}
virtual void desenhar();
};

Instncias da classe Circulo so ainda teis, apesar de se utilizar esta classe
como classe base da classe Arco.

O desenvolvimento da classe Arco tambm ilustra como uma hierarquia de
classes pode crescer atravs do processo de especializao das classes existentes.

4.3 O Exemplo Geomtrico Melhorado

O prximo passo no desenvolvimento de nosso exemplo de programa grfico
consiste em tirar proveito do fato de que todas as classes derivadas de Forma possuem
uma varivel membro comum, org. Sendo assim, pode-se mover esta varivel
membro das classes derivadas para a classe base Forma. A funo membro
Forma::mover() pode agora ajustar org, logo ela no mais uma funo virtual pura.
A classe base Forma agora a seguinte:

class Forma {
Ponto org; // origem
public:
Forma(const Ponto& p): org(p.x(), p.y()) {}
Ponto origem() const { return org; }
virtual void mover(const Ponto& d) { org += d;}
virtual void desenhar() const =0;
};

Uma vez que a classe Forma tem agora uma varivel membro, pode-se prover
um construtor para inicializ-la e a funo membro origem() para ler o seu valor.

Utilizando-se esta definio da classe Forma pode-se simplificar as classes
derivadas Linha, Circulo e Quadro, uma vez que elas herdam as funes membros
origem(), mover(), desenhar() e a varivel org da classe Forma:





49
class Linha: public Forma {
Ponto p; // ponto de fim
public:
Linha (const Ponto& a, const Ponto& b): Forma(a), p(b.x(), b.y()) {}
virtual void mover(const Ponto&); // mover linha de um montante d
virtual void desenhar() const; // desenha uma linha de org ate p
};

void Linha::mover(const Ponto& d)
{
Forma::mover(d);
p+= d;
}

void Linha::desenhar() const
{
cout << Linha de << origem() << ate << p << \n;
}

classe Circulo: public Forma {
int raio; // raio do circulo
public:
Circulo(const Ponto& c, int r): Forma (c) { raio = r }
virtual void desenhar() const; // desenha um circulo com
// centro org e raio raio
};

void Circulo::desenhar() const
{
cout << Circulo com centro em << origem() << e raio de << raio
<< \n;
}

const unsigned CAPACIDADE_QUADRO = 100;

class Quadro: public Forma {
Forma* s[CAPACIDADE_QUADRO]; // array de ponteiros para formas
int n; // numero de formas neste quadro
public:
Quadro(): Forma(Ponto(0,0)) { n = 0; } // construtor
Quadro(Ponto& org): Forma (org) { n = 0;} // construtor
void adicionar(Forma&); // adiciona Forma a Quadro
virtual void mover(const Ponto&); // mover quadro de um montante d
virtual void desenhar() const; // desenhar quadro;
};





50
void Quadro::adicionar(Forma& t)
{
if (n == CAPACIDADE_QUADRO) {
cerr << Capacidade do quadro excedida \n ;
exit(1);
}
s[n++] = &t; // adiciona ponteiro para Forma a Quadro
}

void Quadro::mover(const Ponto& d)
{
Forma::mover(d);
for (int i = 0; i < n; i++) s[i]->mover(d);
}

void Quadro::desenhar() const
{
for (int i=0; i<n; i++) s[i]->desenhar();
}

Deve-se observar que enquanto a classe Circulo pode simplesmente herdar
Forma::mover(), a classe Linha no pode uma vez que esta deve tambm ajustar o
outro ponto extremo p quando da operao mover.

4.3.1 Inicializao de Objetos

Os construtores da classe Linha, Circulo e Quadro ilustram como uma classe
derivada pode passar argumentos para um construtor de sua classe base. Para tal,
deve-se utilizar a seguinte sintaxe:

Nome_Classe_Derivada (parmetros da classe base, parmetros da classe
derivada
4
): Nome_Classe_Base(parmetros da classe base), Construtores_Membros
(parmetros para construtores dos membros)
{
//...
definio da funo
//...
}

Por exemplo:

Linha (const Ponto& a, const Ponto&b) : org(a.x(), a.y()), p(b.x(), b.y()) {}
Circulo (const Ponto& c,int r) : Forma (c) { raio = r }
Quadro (): Forma (Ponto(0,0)) {n = 0 ;}
Quadro (Ponto& org): Forma (org) {n = 0 ;}

As regras para se determinar a ordem pela qual C++ inicializa as instncias da
classe base e variveis membros em um objeto so um pouco complexas. A ordem de

4
A ordem dos parmetros na realidade no importa.

51
inicializao no caso onde as classes tm, no mximo, uma nica classe base a
seguinte:

1. a classe base, se existir alguma;
2. as variveis membros, na ordem em que so declaradas na declarao de
classe, no na ordem em que elas aparecem na lista de parmetros do construtor.

O que complica o fato de que C++ aplica estas regras recursivamente; por
exemplo, se a classe base tem uma classe base, C++ inicializa a classe base da classe
base e qualquer membro que esta possa conter antes de inicializar a classe base. O
exemplo seguinte mostra a ordem pela qual os construtores so chamados durante a
inicializao de um objeto complexo:

#include <iostream.h>

class X {
int i;
public:
X(const char* s) { cout << s << ;}
X() { cout << X::X() ;}
};

class A {
X a1;
X a2;
public:
A(const char* s) : a2(A::a2) {cout << s << ;}
};

class B: public A{
X b1;
X b2;
public:
B(const char* s): b2(B::b2), b1(B::b1), A(B::A) {cout << s << ;}
};

int initCi()
{
cout << C::i;
return 0;
}

int& initCr()
{
static int n = 1;
cout << C::r;
return n;
}


52
class C: public B {
cont int i;
int& r;
X c1;
X c2;
public:
C(const char* s): B(C::B), c1(C::c1), r(initCr()), i (initCi()),
c2(C::c2) { cout << s << \n; }
};

main()
{
C c(c);
}

A sada deste programa :

X::X() A::a2 B::A B::b1 B::b2 C::B C::i C::r C::c1 C::c2 c

Tente demonstrar como as regras produzem estes resultados.

Este exemplo tambm mostra como inicializar variveis membros constantes e
referncias (C::i e C::r neste caso) - eles devem ser inicializados pela lista de
inicializao do construtor.

4.3.2 Finalizao de Objetos

A regra que C++ utiliza para determinar a ordem pela qual destrutores so
chamados simples: destrutores so chamados na ordem inversa dos construtores. Isto
explica porque C++ ignora a ordem dos membros na lista de inicializao e utiliza a
ordem dos membros na declarao de classe: uma classe pode ter vrios construtores,
e cada um destes construtores poderia ordenar os membros em sua lista de
inicializao de uma maneira diferente. No momento de destruir um objeto, o
destrutor no saber qual construtor foi utilizado para criar o objeto. Utilizando-se a
ordem dos membros na declarao de classe evita-se este problema, permitindo-se que
os destrutores das variveis membros sejam chamados na ordem inversa, sem ter que
considerar qual construtor criou o objeto.

53


Captulo V - Conceitos Complementares de
Programao em C++


5.1 Introduo

Agora que voc j conhece os principais conceitos de programao na
linguagem C++, vamos apresentar alguns conceitos adicionais desta linguagem de
programao. Embora estes conceitos no sejam parte do ncleo bsico da linguagem,
os tpicos abordados neste captulo final definem caractersticas complementares de
C++ que facilitam o entendimento de programas e permitem a construo de
programas mais flexveis.

5.2 Construtor Pr-Definido de Cpia, Operador Pr-definido
de Atribuio e Construtor Pr-Definido Default

Ao se construir uma nova classe, importante identificar se nenhuma das
variveis membro tm, como seus valores, objetos dinmicos (isto , objetos criados
atravs do uso de alocao dinmica). Se for este o caso, no necessrio definir um
construtor de cpia e um operador de atribuio especficos para esta classe, visto que
C++ pr-define um construtor de cpia e um operador de atribuio que vlido para
todas as classes.

O construtor pr-definido de cpia realiza a cpia membro a membro das
variveis do objeto que ser copiado para as variveis do objeto que ser a cpia. Da
mesma maneira, o operador pr-definido de atribuio realiza a atribuio membro a
membro das variveis do objeto que ser atribudo para as variveis do objeto que
recebe a atribuio.

Se a classe no envolve objetos dinmicos, pode-se garantir que a cpia e a
atribuio de objetos ser feita de maneira apropriada. O mesmo no pode ser dito
quando a classe envolve objetos dinmicos. Neste caso, a utilizao do construtor e do
operador pr-definido pode gerar sinonmias e efeitos colaterais. Recomenda-se neste
caso, portanto, que sejam definidos um construtor de cpia e um operador de
atribuio especficos para a classe em questo.

Por exemplo, a classe Ponto, apresentada no captulo IV, no requer que sejam
definidos o construtor de cpia e o operador de atribuio, visto que suas variveis
membros xc e yc so do tipo inteiro. Por outro lado, a classe BigInt, requer que eles
sejam definidos porque a varivel membro digitos tem como valor um ponteiro para
um vetor de dgitos alocado dinmicamente.


54
Ao se construir uma classe possvel deixar de especificar construtores para
ela. Isto ocorre porque, quando no existem construtores para uma classe, o
compilador criar um construtor default para ela. Deste modo, o cdigo seguinte
funciona;

//: C06:AutoDefaultConstructor.cpp
// construtor default gerado automaticamente
class V {
int i; // private
}; // sem construtor
int main() {
V v, v2[10];
} ///:~

Contudo, se ao menos um construtor da classe for definido, o compilador no
criar o construtor default e as instncias de V acima gerariam erros em tempo de
compilao. importante tomar cuidado com o uso do construtor default pr-definido
porque ele no assegura nenhum tipo de ao especial (por exemplo, inicializar as
variveis membro numricas com zero). Portanto, se voc quer inicializar variveis
membro com zero, voc deve fazer isso por sua prpria conta escrevendo o construtor
default explicitamente.

Como vimos nesta seo, o compilador C++ pode fornecer construtores pr-
definidos e operadores de atribuio pr-definidos para as suas classes. Contudo,
muitas vezes, o comportamento destes construtores e operadores no so os que voc
deseja. Deste modo, esta caracterstica de C++ deve ser encarada como uma rede de
segurana, mas no deve ser amplamente utilizada. Em geral, trata-se de boa poltica
definir os contrutores explicitamente e no deixar que o compilador faa isto por voc.

5.3 Objetos Temporrios

Em algumas situaes, necessrio ou conveniente que o compilador de C++
crie objetos temporrios. A criao destes objetos dependente da implementao do
compilador. Sempre que um objeto temporrio criado, o construtor desta classe deve
ser chamado. Da mesma maneira, o destrutor da classe deve ser chamado quando o
objeto temporrio eliminado. Por exemplo,

class X {
//
public:
X(int);
X(const X&);
~X();
};
X f(X);
void g() {
X b = f(X(2));
}

55
Um objeto temporrio pode ser criado para abrigar o resultado da chamada ao
construtor X(2). Posteriormente, este objeto ser passado como argumento para f. Uma
vez que o objeto tenha sido passado como argumento, o destrutor j pode ser
chamado.

5.4 Retorno de Objetos em Funes

Objetos podem ser retornados por funes. Sempre que isto ocorre, o
construtor de cpia (definido pelo usurio ou o pr-definido) chamado para criar o
objeto que ser retornado. Pode-se usar no comando return objetos cujo tipo podem
ser convertidos (de forma pr-definida ou atravs do uso de construtores) para a classe
do objeto que deve ser retornado. Por exemplo, na funo f abaixo, o valor 0 ser
implicitamente convertido num BigInt, antes de ser retornado, atravs do uso de um
construtor da classe BigInt.

BigInt f() { return 0; }

Objetos tambm podem ser retornados atravs do uso de ponteiros e
referncias para o objeto. Nestes casos, importante tomar cuidado para no retornar
ponteiros e referncias para variveis locais, como acontece nos exemplos a seguir:

Ponto* f () {
Ponto a;
//
return &a; // erro
}

Ponto& f () {
Ponto a;
//
return a; // erro
}

O retorno de referncias objetiva permitir o uso de funes no lado esquerdo
de atribuies, no acesso seletivo de variveis compostas ou em atribuies mltiplas.
Como o operador de atribuio da classe BigInt retorna void, no permitido fazer
atribuies mltiplas de BigInts. Logo, o cdigo a seguir incorreto:

BigInt c = 243;
BigInt a, b;
a = b = c; // erro

Para que a atribuio acima seja permitida, necessrio alterar a declarao e
definio do operador atribuio de modo a retonar uma referncia a BigInt, como
mostrado abaixo:




56
BigInt& BigInt::operator=(const BigInt& n)
{
if (this == &n) return *this; // para manipular x = x corretamente
delete digitos;
unsigned i = n.digitos;
digitos = new char[n.digitos=i];
char* p = digitos;
char* q = n.digitos;
while (i--) *p++ = *q++;
return *this;
}

5.5 Especificadores de Acesso

Como j tivemos a oportunidade de ver, C++ permite que se controle o acesso
a variveis e funes membros de uma classe atravs dos especificadores public,
private e protected. Quando um membro private, seu nome pode ser apenas usado
pelas funes membro e friends da classe na qual ele declarado. Quando ele
public, seu nome pode ser usado por qualquer funo. Quando ele protected, seu
nome s pode ser usado or funes membro e friends da classe na qual ele
declarado, e por funes membro e friends de classes derivadas desta classe.

C++ tambm permite que se especifique se uma classe herda os membros da
classe base de maneira pblica ou privada. Quando se usa o especificador de acesso
public, os membros pblicos da classe base so membros pblicos da classe derivada
e os membros protegidos da classe base so membros protegidos da classe derivada.
Quando se usa o especificador de acesso private, os membros pblicos e protegidos da
classe base so membros privados da classe derivada. Se o especificador de acesso
omitido, assume-se por default que se est usando o especificador de acesso private.

Os seguintes exemplos ilustram como estes especificadores so utilizados.

class B {
int a;
protected:
int b;
public:
int c;
void f();
};

void B::f() {
a = 10; // ok
b = 10; // ok
c = 10; // ok
}



57
B x;
int k = x.a; // erro
k = x.b; // erro
k = x.c; // ok
x.f(); // ok

class C: private B {
public:
void g();
};

void C::g() {
a = 10; // erro
b = 10; // ok
c = 10; // ok
}

C y;
k = y.a; // erro
k = y.b; // erro
k = y.c; // erro
y.f(); // erro
y.g(); // ok

class D: public B {
public:
void h();
};

void D::h() {
a = 10; // erro
b = 10; // ok
c = 10; // ok
}

D w;
k = w.a; // erro
k = w.b; // erro
k = w.c; // ok
w.f(); // ok
w.h(); // ok

class E: public C {
public:
void i();
};




58
void E::i() {
a = 10; // erro
b = 10; // erro
c = 10; // erro
}

E z;
k = z.a; // erro
k = z.b; // erro
k = z.c; // erro
z.f(); // erro
z.g(); // ok
z.i(); // ok

class F: public D {
public:
void j();
};

void F::j() {
a = 10; // erro
b = 10; // ok
c = 10; // ok
}


F v;
k = v.a; // erro
k = v.b; // erro
k = v.c; // ok
v.f(); // ok
v.h(); // ok
v.j(); // ok

5.6 Estruturas Internas a Classes

C++ possibilita que se criem estruturas e classes declaradas e visveis apenas
internamente a uma classe. A razo de se permitir este tipo de declarao que pode
ser necessrio criar estruturas auxiliares para implementar outras classes.
Normalmente, no faz sentido que estas estruturas sejam visveis para outros clientes,
uma vez que ela especficamente relacionada com a classe para o qual foi criada. Por
exemplo, na implementao de uma classe lista encadeada, necessrio criar uma
estrutura n da lista. Esta estrutura no deve ser pblica, visto que ela est
intimamente relacionado com o uso da classe lista.





59
class lista {
struct no {
no* prox;
int info;
}
no* prim;
public:
lista () { prim = 0 };
void insere_inicio (int);
void insere_fim (int);
int remove ();
int vazia ();
};

No exemplo acima a struct no s visvel internamente s funes membro da
classe lista. Nenhuma outra funo ou classe pode utilizar a estrutura n.

A combinao deste mecanismo com a herana de classe atravs do
especificador de acesso private abre novas possibilidades para a implementao de
classes e permite diferenciar herana de propriedades do conceito de subtipos. Se uma
classe um subtipo de outra, todas as propriedades pblicas da classe base devem ser
pblicas da classe derivada. Se apenas desejamos reutilizar cdigo de uma classe sem
que a nova classe seja considerada um subtipo, o uso do especificador de acesso
private permite que definamos quais das propriedades de uma classe devem ser
pblicas. Por exemplo, a classe pilha no um subtipo classe lista, pois nem todas
operaes de lista so operaes de pilha. Contudo, podemos reusar operaes de lista
para implementar uma pilha.


class pilha: private lista {
public:
pilha() {}
void empilha (int valor) {
lista::insere_inicio(int valor);
}
int pop() {
return lista::remove_inicio();
}
lista::vazia;
};

5.7 Herana Mltipla

Herana Mltipla permite que uma classe derivada seja herdeira de duas ou
mais classes bases imediatas, ou seja, filha de mais de uma classe. A sintaxe da
declarao de uma classe estendida de forma a permitir uma lista de classes bases e
seus respectivos modificadores de acessabilidade. Por exemplo, considere a hierarquia
da Figura 6:

60











Figura 6 - Exemplo de Herana Mltipla

Uma possvel implementao desta hierarquia em C++ a seguinte:

class Apartamento {
protected:
float area;
unsigned qtde_quartos;
public:
Apartamento(float, unsigned);
void mostrar();
friend void procura(unsigned, unsigned);
};

class Garagem {
protected:
unsigned qtde_carros;
public:
Garagem(unsigned);
void mostrar();
friend void procura(unsigned, unsigned);
};

class AptoComGaragem: Apartamento, Garagem { // Herana Mltipla
public:
AptoComGaragem(float, unsigned, unsigned);
void mostrar();
};

/************** Funcoes da Classe Apartamento **************/

Apartamento::Apartamento(float metragem, unsigned dormitorios) {
area = metragem;
qtde_quartos = dormitorios;
}



Garagem Apartamento
AptoComGaragem

61
void Apartamento::mostrar() {
cout << \n\n\t umero de Dormitorios : << qtde_quartos
<< \n\t Area do Apartamento :
<< setprecision(2) << area << metros quadrados ;
}

/************** Funcoes da Classe Garagem **************/

Garagem::Garagem(unsigned automoveis) {
qtde_carros = automoveis;
}

void Garagem::mostrar() {
cout << Capacidade para << qtde_carros;
if (qtde_carros > 1)
cout << Automoveis
else
cout << Automovel ;
}

/************** Funo da Classe AptoComGaragem **************/

AptoComGaragem::AptoComGaragem(float m2, unsigned td, unsigned ta)
: Apartamento (m2,td), Garagem (ta) {}

void AptoComGaragem::mostrar() {
Apartamento::mostrar();
cout << \n\t Garagem com ;
Garagem::mostrar();
}

Neste exemplo, a classe AptoComGaragem herda os membros das classes
Apartamento e Garagem. Ao se criar uma instncia da classe AptoComGaragem
atravs da declarao

AptoComGaragem:: AptoComGaragem apt1202(130,4,2);

o construtor da classe derivada chamado. Antes de executar seu cdigo, esta funo
chama os construtores das classes bases Apartamento e Garagem, nesta ordem, em
sua definio:

AptoComGaragem:: AptoComGaragem(float m2, unsigned td, unsigned ta)
: Apartamento(m2, td), Garagem (ta) {}
5.7.1 Ambigidades em Herana Mltipla

Herana Mltipla conceitualmente direta, mas ela introduz algumas
complexidades prticas nas linguagens de programao. Existem dois problemas
relacionados a ambigidade em Herana Mltipla: conflito entre nomes de classes

62
bases diferentes e herana repetida. Conflitos ocorrem quando duas ou mais classes
bases possuem membros homnimos. Em C++, tais conflitos devem ser resolvidos
com uma qualificao explcita. Por exemplo:

class Surf {
float notas[10];
public:
virtual void ordena();
}

class Vela {
float tempos[10];
public:
virtual void ordena();
}

class WindSurf : public Surf, public Vela {
char competicao;
};

Caso um objeto da classe WindSurf fosse criado e tentasse utilizar a funo
ordena() de uma classe base, haveria uma ambigidade detectada pelo compilador.

int main() {
//...
WindSurf corrida;
corrida.ordena(); // erro de ambiguidade
//...
}

Para evitar esse erro, deve-se especificar qual classe base est sendo
referenciada pelo objeto da classe derivada. No exemplo, o tipo de competio que
identificar a classe base adequada. Sendo assim, pode-se eliminar a ambigidade
redefinindo-se a operao de ordenao na classe derivada WindSurf e utilizando-se o
operador de resoluo de escopo :: para acessar as operaes nas classes bases.

class WindSurf : public Surf, public Vela {
char competicao;
public:
virtual void ordena();
}

void WindSurf::ordena() {
if (competicao == E )
Surf::ordena();
else
Vela::ordena();
}

63
Herana repetida ocorre quando uma classe herdeira de duas ou mais classes
que, por sua vez, so herdeiras de uma classe base comum. Neste caso os atributos da
classe base comum sero herdados duplamente pela classe em questo. Este fato pode
ser observado na figura a seguir:

mi
base
d1
base
d2
base
d1
base
d2
base


Alm de duplicar a memria utilizada para armazenar objetos da classe mi, a herana
repetida pode provocar ambigidade. Veja no exemplo seguinte:

//: C06:MultipleInheritance1.cpp
#include "../purge.h"
#include <iostream>
#include <vector>
using namespace std;

class MBase {
public:
virtual char* vf() const = 0;
virtual ~MBase() {}
};

class D1 : public MBase {
public:
char* vf() const { return "D1"; }
};

class D2 : public MBase {
public:
char* vf() const { return "D2"; }
};

class MI : public D1, public D2 {}; // erro devido a ambiguidade em vf

int main() {

64
vector<MBase*> b;
b.push_back(new D1);
b.push_back(new D2);
b.push_back(new mi); // nao sabe qual Mbase herdado usar
for(int i = 0; i < b.size(); i++)
cout << b[i]->vf() << endl;
purge(b);
} ///:~

Para contornar o primeiro erro, o programador deve disambigar a funo vf()
atravs de sua redefinio na classe mi. C++ fornece um mecanismo especial, atravs
da palavra virtual para resolver o segundo problema. Se uma classe herdada
precedida por um virtual somente um subobjeto daquela classe compor o objeto da
classe herdeira, independentemente se a classe herdada mltiplas vezes ou no.

//: C06:MultipleInheritance2.cpp
// Virtual base classes
#include "../purge.h"
#include <iostream>
#include <vector>
using namespace std;

class MBase {
public:
virtual char* vf() const = 0;
virtual ~MBase() {}
};
class D1 : virtual public MBase { // so herda MBase uma unica vez
public:
char* vf() const { return "D1"; }
};
class D2 : virtual public MBase { // so herda MBase uma unica vez
public:
char* vf() const { return "D2"; }
};
// DEVE disambiguar vf() explicitamente
class MI : public D1, public D2 {
public:
char* vf() const { return D1::vf();}
};
int main() {
vector<MBase*> b;
b.push_back(new D1);
b.push_back(new D2);
b.push_back(new MI); // OK
for(int i = 0; i < b.size(); i++)
cout << b[i]->vf() << endl;
purge(b);
} ///:~

65
5.8 Funes e Classes Genricas

Existem situaes em programao onde importante parametrizar uma
determinada funo ou classe pelo tipo do objeto que ela manipula. Por exemplo, um
mesmo algoritmo de ordenao aplicado independentemente do fato do tipo do
elemento a ordenar ser inteiro, real, string ou mesmo um tipo composto. Da mesma
maneira, a estrutura e operaes de uma classe lista independem do tipo dos
elementos que compem a lista.

Em muitas linguagens de programao, o programador forado a copiar (ou
reescrever) o cdigo destas funes e classes para cada um dos tipos desejados. Este
procedimento costuma ser redundante, visto que o cdigo eminentemente o mesmo,
e provocar erros, visto que durante a atualizao do cdigo copiado necessrio fazer
pequenas modificaes para ajustar a estrutura de dados e as operaes ao novo tipo.

C++ fornece um mecanismo chamado template para tratar esses casos. Este
mecanismo permite parametrizar uma funo ou classe pelo tipo do elemento que ela
manipula. Por exemplo, a funo a seguir recebe dois parmetros de um tipo no
especificado e retorna o que tem maior valor.

#include <iostream.h>
template <class T>
T max (T p, T s) {
return p > s ? p : s;
}

Para utilizar esta funo, basta cham-la passando como parmetros elementos
de um mesmo tipo, visto que p e s so do tipo genrico class T. importante notar
que, para a funo funcionar apropriadamente, tambm necessrio que o tipo do
elemento que foi passado como parmetro possua a comparao < como uma de suas
operaes, visto que a funo vai necessitar comparar elementos deste tipo. O
exemplo a seguir mostra como esta funo pode ser utilizada.

main() {
int c, a = 1, b = 2;
char f, d = 'a', e = 'b';
c = max (a, b);
f = max (d, e);
cout << " results: " << c << " - " << f << "\n";
}

O mesmo mecanismo pode ser usado para criar uma classe genrica. O
exemplo a seguir mostra a implementao de uma classe genrica que define um tipo
pilha.

template <class T, int tam>
class pilha {
T vet[tam];

66
int max;
int top;
public:
pilha(void);
int vazia (void) { return top == -1; }
T topo (void) { return vet[top]; }
void empilha (T);
void desempilha (void);
};
template <class T, int tam>
void pilha<T, tam>::pilha(void) {
max = tam - 1;
top = -1;
}
template <class T, int tam>
void pilha<T, tam>::empilha (T e){
if (top == max) {
cout << "pilha ja esta cheia\n";
} else {
vet[++top] = e;
}
}
template <class T, int tam>
void pilha<T, tam>::desempilha (void){
if (top == -1) {
cout << "pilha ja esta vazia\n";
} else {
top--;
}
}

Esta implementao do tipo pilha utiliza um vetor para armazenar os valores
da pilha. Como pode ser observado, a classe no especifica qual o tipo do elemento da
pilha. Isso s definido quando da criao de um objeto desta classe. Este exemplo
mostra ainda que a classe tambm parametrizada por um inteiro que determina o
tamanho mximo do vetor usado na pilha. Este valor s definido no momento em
que algum objeto da classe pilha criado. O exemplo a seguir mostra o uso dessa
classe na criao de uma pilha de int com tamanho mximo igual a trs e uma pilha de
char com tamanho mximo igual a 2:

#include "iostream.h"
#include "cgen.h"
void main () {
pilha <int, 3> x;
pilha <char, 2> y;
x.empilha (1);
x.empilha (2);
x.empilha (3);
x.empilha (4);

67
x.desempilha( );
cout << x.topo( ) << "\n" ;
y.desempilha( );
if (y.vazia( )) y.empilha('a');
y.empilha('b');
cout << y.topo( ) << "\n";
}

Note que, embora a pilha acima tenha sido usado com os tipos primitivos int e
char, nada impede que ela seja tambm usada com um objeto de uma classe definida
pelo programador. Outra observao interessante que, ao contrrio do que temos
sempre visto, costuma-se colocar a especificao e a implementao de classes
genricas no arquivo cabealho (.h). Isso ocorre devido a forma de implementao dos
templates que reutiliza cdigo fonte e no cdigo objeto. De fato, em alguns
compiladores a separao de especificao e implementao de classes genricas
provoca erro de ligao.

5.9 Namespaces

Um dos problemas com a programao em C, que quando programas
atingem um certo tamanho fica cada vez mais difcil pensar em novos nomes para as
entidades de computao globais. Pior ainda quando o programa dividido em
pedaos, cada qual construdo e mantido por um programador diferente. Como C s
possui uma nica regio onde todos os identificadores globais so declarados, isto
frequentemente provoca colises de nomes.

C++ padro inclui o mecanismo de namespaces para subdividir a regio de
identificadores globais em pedaos mais gerenciveis. Cada conjunto de definies de
C++ numa biblioteca ou programa embutida numa namespace, e se alguma outra
definio tem um identificador idntico, mas numa outra namespace, ento no existe
coliso. A criao de uma namespace muito similar a criao de uma classe:

//: C10:Header1.h
#ifndef HEADER1_H
#define HEADER1_H
namespace MyLib {
extern int x;
void f();
// ...
}
#endif // HEADER1_H ///:~

Contudo, ao contrrio de uma definio de classe, uma nova definio de uma
namespace no implica numa redefinio da classe e sim numa continuao da
definio anterior:

//: C10:Header2.h
#ifndef HEADER2_H

68
#define HEADER2_H
#include "Header1.h"
// Adiciona mais entidades a MyLib
namespace MyLib { // ao eh redefinicao!
extern int y;
void g();
// ...
}
#endif // HEADER2_H ///:~

//: C10:Continuation.cpp
#include "Header2.h"
int main() {} ///:~

Uma namespace pode ser associada a um outro nome, de modo que o
programador no tenha que usar nomes esquisitos ou grandes idealizados pelo criador
da namespace.

//: C10:BobsSuperDuperLibrary.cpp
namespace BobsSuperDuperLibrary {
class Widget { /* ... */ };
class Poppit { /* ... */ };
// ...
}
// Cria outro nome para a namespace pois e muito para teclar!!
namespace Bob = BobsSuperDuperLibrary;
int main() {} ///:~

O mecanismo de namespaces conveniente e til. Contudo, necessrio dar
cincia da sua existncia aos programas antes que eles as utilizem. Incluir apenas um
arquivo cabealho provavelmente ocasionar mensagens de erros estranhas do
compilador indicando que ele no capaz de encontrar as declaraes de tens que
voc acabou de incluir com o arquivo cabealho. C++ fornece a palavra reservada
using para indicar que se quer usar as declaraes e definies de uma certa
namespace.

using namespace std;

Toda a biblioteca padro C++ est embutida na namespace std. Isto significa
que sempre que a frase acima for includa num programa, voc pode usar qualquer
entidade pertencente a biblioteca padro includa.

Existe uma correspondncia entre a forma padronizada de incluso de arquivos
(sem o uso de .h), namespaces e a forma antiga (com o uso de .h):

#include <iostream.h>

o mesmo que


69
#include <iostream>
using namespace std;
5.10 Booleanos

Enquanto C no possui um tipo booleano, C++ padro resgata este tipo de
dado, chamado de bool. Antes que este tipo se tornasse parte de C++, muitos
programadores tendiam a usar diferentes tcnicas para produzir o efeito de booleanos.
Isto produzia problemas de portabilidade e introduzia erros sutis.

O tipo bool pode assumir dois valores: true e false. O valor true converte para
um e o valor false converte para zero. Adicionalmente, alguns mecanismos de C++
foram adaptados ao tipo booleano. Agora, enquanto os operadores lgicos e
relacionais retornam booleanos, as expresses de comandos condicionais convertem
implicitamente seus argumentos para valores booleanos.

5.11 Strings

O uso de vetores de char para tratar strings pode requerer grande esforo de
programao para lidar com situaes onde necessrio lidar com strings que possam
aumentar de tamanho na medida da necessidade. A biblioteca padro de C++ inclui
uma classe string que foi projetada para cuidar (e esconder) todas as operaes de
gerenciamento de memria, cpia e concatenao de vetores de caracteres requeridas
de um programador C. Essas manipulaes so fonte constante de erros e desperdcio
de tempo.

//: C02:HelloStrings.cpp
// O basico da classe string de C++ padrao
#include <string>
#include <iostream>
using namespace std;
int main() {
string s1, s2; // strings vazias
string s3 = "Hello, World."; // Inicializada
string s4("I am"); // Inicializada
s2 = "Today"; // Atribuicao
s1 = s3 + " " + s4; // Concatenacao
if (s1 > s2) // Comparacao
s1 += " 8 "; // Inclusao
cout << s1 + s2 + "!" << endl;
} ///:~

O exemplo anterior mostra como strings podem ser criadas, inicializadas,
atribudas, concatenadas e comparadas de uma forma direta, sem que o programador
necessite saber como estas operaes so implementadas.

70
5.12 Streams

No seria interessante voc poder tratar todos os tipos de dispositivos de
armazenamento de dados (entrada e sada padro, arquivos e blocos de memria) de
uma maneira equivalente de modo a s manipul-los com uma nica interface de
operaes? Esta a idia por detrs de iostreams. Elas so muito mais fceis, seguras
e frequentemente eficientes do que as funes avulsas da biblioteca padro stdio de C.

A biblioteca padro iostream geralmente a primeira que novos
programadores C++ aprendem a usar. Isto ocorre porque este pacote define
automaticamente objetos chamados cin e cout, que aceitam dados que devem ser lidos
ou escritos na entrada e sada padro. Para enviar dados para a sada padro usa-se o
operador sobrecarregado <<. Para receber dados da entrada padro usa-se o operador
sobrecarregado >>.

int i;
cin >> i;
float f;
cin >> f;
char c;
cin >> c;
char buf[100];
cin >> buf;




cout << "i = ";
cout << i;
cout << "\n";
cout << "f = ";
cout << f;
cout << "\n";
cout << "c = ";
cout << c;
cout << "\n";
cout << "buf = ";
cout << buf;
cout << "\n";

que o mesmo que

cin >> i >> f >> c >> buf;
cout << "i = " << i << "\nf = "<< f << "\nc = "<<
c << "\nbuf = " << buf << "\n";

importante ter em mente que o uso de cin apresenta as mesmas
caractersticas da entrada com scanf em C, isto , o uso do delimitador espao para
separar entradas e a possibilidade de ler strings alm do tamanho reservado para elas.
Para ler strings que contenham brancos com segurana C++ fornece o mtodo getline,
que permite ler uma linha inteira

cin.getline(buf, sz); // terceiro parmetro o delimitador - default = \n

C++ oferece, na biblioteca padro iostream, uma hierarquia de classes para
manipulao do sistema de entrada e sada atravs de streams. A figura seguinte
apresenta parte desta hierarquia. Uma stream em C++ um objeto que formata e
armazena bytes e que corresponde a uma classe desta hierarquia. A classe raiz desta
hierarquia ios, a qual contm dados e operaes para todas as classes derivadas. As
classes istream e ostream fornecem respectivamente operaes bsicas de entrada e

71
sada e so usadas como classes bases para as demais classes da hierarquia. As classes
ifstream e ofstream so usadas para manipular arquivos. As classes istringstream e
ostringstream para manipular strings. A classe fstream permite criar e manter arquivos
que requeiram acesso de leitura e escrita.

















Para abrir arquivos para leitura e escrita em C++, necessrio incluir a biblioteca
padro <fstream>. Para abrir um arquivo para leitura, necessrio criar um objeto da
classe ifstream. Para abrir um arquivo para escrita, necessrio criar um objeto da
classe ofstream. Uma vez que voc tenha aberto o arquivo, pode-se ler ou escrever
nele como se estivesse manipulando qualquer objeto da classe iostream.

//: C02:Scopy.cpp
// Copia um arquivo sobre outro, uma linha de cada vez
#include <string>
#include <fstream>
using namespace std;
int main() {
ifstream in("Scopy.cpp"); // Abrir para leitura
ofstream out("Scopy2.cpp"); // Abrir para escrita
string s;
while(getline(in, s)) // Discarta o caracter de nova linha
out << s << "\n"; // ... adiciona de novo o caracter de nova linha
} ///:~

Outro exemplo interessante copia um arquivo completo em uma string:

//: C02:FillString.cpp
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream in("FillString.cpp");
ios
istream
ostream
iostream ifstrea
m
ofstream istringstream ostringstream
fstream

72
string s, line;
while(getline(in, line))
s += line + "\n";
cout << s;
} ///:~

Por causa da natureza dinmica das strings, no necessrio se preocupar com
a alocao de memria para a string.

5.13 Tratamento de Excees

Uma exceo um evento que ocorre durante a execuo de um programa que
interrompe o fluxo normal das instrues. Muitos tipos de erros podem causar
excees, tais como falha de hardware e tentativa de acesso a posies fora dos limites
de um vetor. Mas excees no esto relacionadas exclusivamente com erros.
Condies excepcionais, tal como a chegada ao fim de um arquivo tambm so
excees.

O tratamento de excees bastante direto em situaes onde uma
determinada condio verificada e j se sabe exatamente o que se deve fazer porque
toda a informao necessria est disponvel naquele contexto. Voc simplesmente as
trata no ponto onde elas ocorrem. Esta postura tende a carregar o cdigo,
comprometendo a legibilidade do programa.

Em situaes onde no existe informao disponvel no contexto onde a
condio foi verificada, necessrio passar a informao local sobre a exceo para
um contexto mais externo onde esta informao exista. Uma maneira de fazer isto
retornando cdigos que representam as excees. Contudo, uma das razes pelas quais
prtica comum ignorar condies de exceo em programao relacionado com o
fato de ser extremamente tedioso e dispersante escrever cdigo que faa checagem
extensiva de erros. A proliferao de cdigo para tratamento de erro, alm de ser
extremamente cansativa, ainda dificulta em muito a legibilidade do programa.

Uma das principais caractersticas de C++ o mecanismo de tratamento de
excees, que objetiva tornar esta tarefa menos tediosa e deixar o cdigo do programa
mais simples e claro. Com o mecanismo de tratamento de excees de C++:

1. O cdigo de tratamento de excees no fica misturado com o cdigo que cumpre
as funcionalidades do programa. Voc escreve o cdigo que voc quer que execute
e mais tarde, numa seo separada, voc escreve o cdigo para lidar com os
problemas. Se voc faz mltiplas chamadas a uma funo voc trata os erros
daquela funo um nica vez, em um nico local.
2. Excees no podem ser ignoradas. Se uma funo necessita enviar uma
mensagem de erro para quem chamou aquela funo, ele envia um objeto
representando aquele erro para fora da funo. Se quem chamou a funo no trata
o erro, o objeto enviado para o prximo nvel de escopo at que alguma funo
trate o erro.


73
5.13.1 Sinalizando uma Exceo

Em C++, se voc encontra uma situao excepcional no seu cdigo, voc pode
enviar informao sobre o contexto corrente para um contexto mais externo atravs da
criao de um objeto contendo a informao e do seu envio para fora de seu contexto
corrente. Isto se chama sinalizar uma exceo. O exemplo a seguir mostra um
exemplo de como isso feito:

throw meuErro ("algo de errado aconteceu");

O objeto meuErro criado especialmente para a sinalizao de excees.
possvel utilizar qualquer tipo (inclusive os pr-definidos) para sinalizar excees. O
comando indicado pela palavra reservada throw primeiramente constri um objeto que
normalmente no existiria na execuo normal do programa. Este objeto
posteriormente retornado pela funo onde o comando foi escrito. Ao contrrio do
retorno de funes, onde o fluxo de execuo continua a partir do ponto onde a funo
foi chamada, uma sinalizao de exceo pode desviar o fluxo para um ponto que
pode estar bem distante de onde a funo foi chamada. Alm disso, apenas objetos
que foram criados at o ponto onde a exceo sinalizada sero destrudos (ao
contrrio de retorno de funes, que assume que todos os objetos no escopo devem ser
destrudos). Obviamente, o objeto criado pela exceo tambm ser destrudo no
ponto apropriado. Por fim, uma funo pode sinalizar vrias excees e criar objetos
de tipos diferentes para cada uma delas tanto quanto for necessrio. Em regra geral,
voc vai sinalizar um objeto de tipo diferente para cada tipo diferente de exceo.

Se voc est dentro de uma funo e uma exceo sinalizada (ou uma funo
que foi chamada sinaliza uma exceo), esta funo ser encerrada durante o processo
de sinalizao. Se voc no quer que isso ocorra, voc pode definir um bloco especial
dentro da funo onde voc tenta realizar uma funcionalidade (e gerar excees
potenciais). Para criar este bloco, deve-se usar a palavra reservada try:

try {
// Code that may generate exceptions
}

Se tivssemos de checar os erros sem usar tratadores de exceo, cada
chamada de funo deveria ser envolvida com cdigo de teste, mesmo se a mesma
funo fosse invocada vrias vezes. Com o try, todas as chamadas de funo so
colocadas dentro de um bloco sem que seja necessria alguma checagem de erro. Isto
quer dizer que seu cdigo mais fcil de escrever e ler porque o objetivo do seu
cdigo no se confunde com a checagem de erros.

5.13.1 Capturando e Tratando uma Exceo

Obviamente, a exceo sinalizada deve ser capturada e tratada em algum lugar. Este
lugar chamado de tratador de exceo. Existe um tratador para cada tipo de exceo

74
que se pode capturar. Tratadores de exceo seguem imediatamente o bloco try e so
denotados pela palavra reservada catch:

try {
// codigo que pode sinalizar excecoes
} catch (tipo1 id1) {
// trata excecoes de tipo1
//
throw; // repropaga a excecao
} catch (tipo2 id2) {
// trata excecoes de tipo2
} catch ( )
// trata excecoes de qualquer tipo - deve ser o ultimo
}

Cada clusula catch como uma pequena funo que recebe um nico
argumento (no pode ser mais que um) de um determinado tipo. O identificador do
parmetro pode ser omitido se o objeto no for necessrio para tratar a exceo (isto ,
o tipo da exceo j fornece informao suficiente para tratar a exceo).

Se uma exceo sinalizada, o primeiro tratador cujo tipo de parmetro case
com o tipo da exceo ser executado e a exceo ser tratada (a busca pelo tratador
interrompida uma vez que a execuo de um tratador finalizada). Portanto, somente
a a primeira clusula que casa que executada. Note que, dentro de um bloco try,
vrias chamadas de funo podem gerar a mesma exceo, porm somente um
tratador necessrio.

Algumas vezes necessrio criar um tratador de excees que capture
qualquer tipo de exceo. Isto realizado usando uma elipse como parmetro do
catch. Como este tratador capturar qualquer exceo, quando for usado, ele deve ser
colocado ao final da lista de tratadores para evitar que algum tratador nunca seja
chamado.

Outras vezes, pode ser necessrio resinalizar a exceo que acabou de ser
capturada. Isto feito atravs do uso de throw sem nenhum objeto associado. Quando
isto ocorre, qualquer outro tratador do bloco try tambm ignorado e a busca pelo
tratador da exceo passa para o contexto mais externo seguinte. importante saber
que o objeto da exceo preservado intacto, de modo que o tratador do nvel mais
externo tambm capaz de extrair informao deste objeto.

Se nenhum dos tratadores de exceo de um determinado bloco try casa com a
exceo, a busca pelo tratador passa para o contexto mais externo seguinte, isto , a
funo ou bloco try que envolve o bloco try que falhou na captura da exceo.
importante ter ateno nestes casos porque a localizao do novo bloco try para busca
no sempre bvia. Este processo continua at que, em algum nvel, um tratador case
com a exceo. Neste ponto, a exceo tratada e no feita nenhuma busca
adicional. Se nenhum tratador encontrado em qualquer nvel, o programa abortado.
Isto tambm ocorre se uma nova exceo sinalizada antes de que uma exceo

75
previamente sinalizada atinja o seu tratador ( a causa mais comum para isto ocorre
quando o construtor do objeto da exceo causa ele mesmo uma exceo).
O exemplo a seguir mostra como o mecanismo de tratamento de excees
pode ser utilizado:

class Array {
//
public:
const int max = 3200;
class Range {
public:
int index;
Range (int i): index (i) { }
}
class Size { }
Array (int sz);
int& operator[] (int i);
//
}

Array::Array (int sz) {
//
if (sz < 0 || max > sz) throw Size( );
//
}

int& Array::operator[] (int i) {
//
if (i < 0 || i > sz) throw Range( i );
//
}

void f ( ) {
//
try {
usa_Arrays ( ); // qualquer funcao que use vetores
}
catch (Array::Size) {
// ajusta
f ( );
}
catch (Array::Range r) {
cerr << "indice fora dos limites do vetor: " << r.index << '\n';
exit (99);
}
// chega aqui se nao houver excecoes ou ao fim da excecao Size
//
}


76
void f1 ( ) {
try {
f2( );
}
catch (Array::Size) {
//
}
}

void f2 ( ) {
try {
usa_Arrays ( ); // qualquer funcao que use vetores
}
catch (Array::Range) {
//
}
}

Um outro exemplo usando herana e amarrao tardia:

class MathErr {
//
virtual void debugPrint ( );
}
class Overflow: public MathErr { // };
class Underflow: public MathErr { // };
class ZeroDivide: public MathErr { // };

void g ( ) {
try {
//
}
catch (Overflow) {
// trata ocorrencias de Overflow e subclasses
}
catch (MathErr m) {
// qualquer MathErr que nao e overflow
m.debugPrint ( );
}
//
}

5.14 Colees e Iteradores da Biblioteca Padro C++

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

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

Existem situaes onde no se sabe quantos objetos sero necessrios para
resolver um determinado problema ou quanto tempo estes objetos iro durar. Nestes
casos, voc no saber como armazen-los. A soluo para estes problemas criar um
novo tipo de objeto que se expandir sempre que for necessrio acomodar qualquer
coisa dentro dele. Este tipo de objeto chamado uma coleo. Portanto, colees so
classes que descrevem objetos capazes de armazenar outros objetos. Assim, quando
uma destas situaes aparece basta criar um objeto do tipo coleo e deix-lo cuidar
dos detalhes.

Boas LPs orientadas a objetos fornecem um conjunto de colees como parte
da sua biblioteca padro. Em algumas bibliotecas, uma coleo genrica considerada
boa para todas as necessidades. Em outras (como C++) a biblioteca fornece diferentes
tipos de colees para diferentes tipos de uso: um vetor para acesso consistente a
todos os elementos; uma lista encadeada para a insero consistente de elementos em
qualquer ponto da lista. Estas bibliotecas podem incluir colees que modelam
conjuntos, filas, pilhas, rvores, tabelas hash, etc.

Todas colees tm alguma maneira efetiva e diferenciada de colocar,
armazenar, recuperar e operar sobre os elementos dentro dela. Normalmente, o modo
como se colocam coisas numa coleo atravs de uma funo de insero ("push"ou
"add" ou um nome similar). J o modo de recuperar elementos de uma coleo mais
variado. Por exemplo, se a coleo uma entidade tal como um vetor, o programador
deve poder usar indexao.

Para se ter uma idia do que so colees vamos examinar trs das mais
bsicas colees da biblioteca padro C++. Um vector uma sequncia linear que
permite o acesso aleatrio rpido aos seus elementos. Contudo, custoso inserir um
elemento no meio da sequncia e tambm custoso alocar memria adicional. Um
deque tambm uma sequncia linear que permite acesso aleatrio quase to rpido
quanto vector, mas significativamente mais rpida quando necessita alocar nova
memria e permite incluir facilmente novos elementos no incio e fim da coleo
(vector s permite incluso ao final). Uma list uma sequncia linear onde muito
custoso acessar elementos aleatoriamente e pouco custoso inserir um elemento no
meio da sequncia. Como todas estas colees apresentam uma funcionalidade bsica
similar, na maioria das situaes bastar escolher uma delas e somente experimentar
com as outras quando se estiver buscando eficincia.

Para acessar elementos de um vector ou deque possvel usar indexao,
porm o mesmo no pode ser feito com uma list. Em algumas situaes, um nico

78
modo de acesso pode ser muito restritivo. Alm disso, seria interessante possuir uma
interface comum de acesso entre as diversas colees.
Um iterador um objeto de uma classe que abstrai o processo de percorrer os
elementos de uma sequncia e apresent-los ao usurio do iterador. Ele permite que
voc selecione cada elemento da sequncia sem ter de saber os detalhes de estrutura
interna de armazenamento da coleo, seja ela um vector, uma stack, uma list, ou
qualquer outra. Atravs do iterador, a coleo simplesmente abstrada a uma
sequncia. Tal caracterstica poderosa porque nos permite aprender uma nica
interface que funciona com todas as colees e parcialmente porque permite as
colees serem usadas intercambiadamente. Em outras palavras, ela fornece
flexibilidade para mudar facilmente a coleo sem pertubar o cdigo do programa
usurio.

//: C04:Stlshape.cpp
#include <vector>
#include <iostream>
using namespace std;

class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {};
};

class Circle : public Shape {
public:
void draw() { cout << "Circle::draw\n"; }
~Circle() { cout << "~Circle\n"; }
};

class Triangle : public Shape {
public:
void draw() { cout << "Triangle::draw\n"; }
~Triangle() { cout << "~Triangle\n"; }
};

class Square : public Shape {
public:
void draw() { cout << "Square::draw\n"; }
~Square() { cout << "~Square\n"; }
};

typedef std::vector<Shape*> Container;
typedef Container::iterator Iter; // iterador definido dentro de vector

int main() {
Container shapes; // cria vector de shapes
shapes.push_back(new Circle); // coloca elemento ao final da coleo
shapes.push_back(new Square);

79
shapes.push_back(new Triangle);
for(Iter i = shapes.begin(); i != shapes.end(); i++)
(*i)->draw(); // poderia usar i->draw() --- * e -> sao sobrecarregados
// ...
for(Iter j = shapes.begin(); j != shapes.end(); j++)
delete *j; // colecoes nao chamam os destrutores dos objetos
} ///:~

bastante interessante notar que possvel mudar o tipo da coleo trocando
apenas duas linhas do programa acima. Ao invs de incluir <vector>, poderia-se
incluir <list> e substituir o primeiro typedef por:

typedef std::list<Shape*> Container;

Isto possvel no por causa de uma interface estabelecida por herana (pode
parecer surpreendente, mas no existe herana entre as colees), mas por causa de
uma interface comum estabelecida atravs de convenes adotadas pela biblioteca
padro, de modo que essa mudana possa ser feita.

Existem duas razes para que se possa escolher colees. Primeiramente,
colees fornecem diferentes tipos de interfaces e comportamento externo. Uma stack
tem uma diferente interface e comportamento que uma queue, que diferente de um
set ou list. Uma destas colees pode se ajustar melhor ao seu problema do que outra.
Em segundo lugar, diferentes colees possuem diferentes eficincias para certas
operaes. Por exemplo, insero de elementos no meio de uma sequncia tem
diferenas radicais no custo se usamos um vector ou uma list. Como os iteradores
fornecem uma interface comum para as colees, voc pode implementar um
prottipo usando uma determinada coleo e depois modificar para uma coleo mais
eficiente quando da implementao final do programa, com impacto mnimo no seu
cdigo.


Bibliografia

[1] Stroustrup, B.; 1988; What is Object-Oriented Programming?; IEEE Software
vol 5(3); p.10

[2] Meyer, B.; 1988; Object-Oriented Software Construction; New York, NY:
Prentice Hall.

[3] Stroustrup, B.; 1991; The C++ Programming Language; Second Edition;
Addison Wesley.

[4] Booch, G.; 1994; Object-Oriented Analysis and Design with Applications;
Second Edition ; The Benjamin/Cummings Publishing Company.

[5] Pohl, I.; 1989; C++ for Programmers; The Benjamin/Cummings Publishing
Company.

80

[6] Montenegro, F. & Pacheco, R.; 1994; Orientao a Objetos em C++; Cincia
Moderna.

[7] Gorlen, K. & Orlow, S. & Plexico, P.; 1990; Data Abstraction and Object-
Oriented Programming in C++; Addison Wesley.

[8] Rumbaugh, J. & Premerlani, W. & Eddy, F. & Lorensen, W.; 1991; Object-
Oriented Modeling and Design; Englewood Cliffs, New Jersey: Prentice Hall.

[9] Eckel, B.; 1999; "Thinking in C++", Volume I, 2
nd
Edition.

[10] Eckel, B; 2000; "Thinking in C++", Volume II, 1
st
Edition.

Você também pode gostar