You are on page 1of 86

C++ e Orientao a Objetos

PET Computao
Fbio Beltro, Felipe Chies, Lucas Zawacki, Marcos Cavinato e Matheus Proena 2 Edio, 18 de Agosto de 2009

1 Edio por Arthur Ribacki, Gabriel Portal, Leonardo Chatain e Roslia Galiazzi

Sumrio
1 Introduo ao C++ 1.1 Histria do C++ . . . . . . 1.2 Primeiros Programas . . . . 1.3 Namespaces . . . . . . . . . 1.4 Variveis Locais e Globais . 1.5 Tipos de Dados . . . . . . . 1.6 Modicadores . . . . . . . . 1.7 Estruturas de Controle . . . 1.8 Funes . . . . . . . . . . . 1.8.1 Funes inline . . . . 1.8.2 Parmetros Default 1 1 2 4 4 5 5 6 6 8 8 13 13 14 14 16 17 18 21 21 22 23 25 25 25 26 28 29 29 30 30

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . .

2 Introduo a Orientao a Objetos 2.1 Uma classe Carro . . . . . . . . . . . 2.2 Classes . . . . . . . . . . . . . . . . . 2.2.1 Encapsulamento . . . . . . . 2.2.2 Membros Pblicos e Privados 2.2.3 Construtores e Destrutores . 2.2.4 Diviso de Arquivos . . . . . 2.3 Composio . . . . . . . . . . . . . . 2.4 Objetos Constantes . . . . . . . . . . 2.5 Funes Membro Constantes . . . . 2.6 Ponteiro this . . . . . . . . . . . . .

3 Sobrecarga 3.1 Quando usar? . . . . . . . . . . . . . . . . . 3.2 Modicador friend . . . . . . . . . . . . . . 3.3 Fundamentos de Sobrecarga de Operadores 3.4 Funes Friend . . . . . . . . . . . . . . . . 3.4.1 Sintaxe . . . . . . . . . . . . . . . . 3.5 Sobrecarga de Operadores Unrios . . . . . 3.6 Sobrecarga de Operadores Binrios . . . . . 3.7 Sobrecarga dos Operadores de Streams . . .

SUMRIO 3.7.1 Exemplo dos operadores de streams ( e ) . . . . . . Converso entre Tipos . . . . . . . . . . . . . . . . . . . . . . Sobrecarregando ++ e - - . . . . . . . . . . . . . . . . . . . . 31 32 32 33 33 35 35 35 37 39 41 41 42 42 45 45 47 47 47 49 49 51 51 51 52 52 52 53 53 53 55 57 59 59 60 60 62

3.8 3.9

4 Herana 4.1 Classe base e Classes Derivadas: . . . . . . . . . . . . . . . . 4.2 Membros Protected . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1 Sintaxe . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.2 Exemplos de Herana . . . . . . . . . . . . . . . . . . 4.3 Coero de ponteiros entre classes bases e classes derivadas . 4.4 Sobrescrevendo membros da classe base em uma classe derivada 4.4.1 Output . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5 Tipos de Herana . . . . . . . . . . . . . . . . . . . . . . . . . 4.6 Herana Mltipla . . . . . . . . . . . . . . . . . . . . . . . . . 4.6.1 Output . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Funes Virtuais e Polimorsmo 5.1 Funes Virtuais . . . . . . . . . . . . . . . 5.1.1 Teste de Aprendizado . . . . . . . . 5.2 Ponteiros Polimorfos como Parmetros . . . 5.2.1 Teste de Aprendizado . . . . . . . . 5.3 Classes Base Abstratas e Classes Concretas 5.3.1 Teste de Aprendizado . . . . . . . . 6 Templates e Excees 6.1 Templates para Funes 6.1.1 Sintaxe . . . . . 6.1.2 Exemplo . . . . . 6.2 Template de Classe . . . 6.2.1 Sintaxe . . . . . 6.2.2 Exemplo . . . . . 6.3 Introduo a Excees . 6.4 Try, Throw e Catch . . 6.4.1 Exemplo . . . . . 6.4.2 Exerccio . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

7 Processamento de Arquivos 7.0.3 Arquivos em C++ . 7.0.4 Arquivo Seqencial . 7.0.5 Modos de Abertura 7.1 Arquivo Aleatrio . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

SUMRIO 8 Standard Template Library 63 8.1 Conhecendo a STL . . . . . . . . . . . . . . . . . . . . . . . . 63 8.2 Continers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 8.3 Funes Membro Comuns . . . . . . . . . . . . . . . . . . . . 64 8.4 Funes Membro Especcas . . . . . . . . . . . . . . . . . . . 65 8.4.1 Arquivos de Cabealho . . . . . . . . . . . . . . . . . . 65 8.5 Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 8.5.1 Teste de Aprendizado . . . . . . . . . . . . . . . . . . 66 8.6 Como lidar com capacidade ilimitada, ou como o Vector no mgico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 8.6.1 Observao . . . . . . . . . . . . . . . . . . . . . . . . 69 8.7 List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 8.8 Outros Containers . . . . . . . . . . . . . . . . . . . . . . . . 70 8.9 Iteradores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 8.10 Algoritmos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 8.10.1 Usando o Algoritmo Sort para qualquer Ordenao . . 75 8.11 Usando a STL com Classes Prprias . . . . . . . . . . . . . . 75 8.11.1 Teste de Aprendizado . . . . . . . . . . . . . . . . . . 78 8.12 ltimas Consideraes . . . . . . . . . . . . . . . . . . . . . . 78

Captulo 1

Introduo ao C++
Antes de comearmos o estudo da linguagem, necessrio que se entenda por que to importante aprender C++. Existe, atualmente, uma demanda muito grande por software, que deve ser construdo rpida, correta e economicamente. A orientao a objetos surge com o objetivo de ajudar a suprir essa demanda. Fortemente baseada no princpio de reutilizao de componentes e estreitamente ligada engenharia de software, permite uma reduo do tempo de implementao bastante signicativa. Alm disso, atravs de uma organizao modular e independente, melhora a manutenabilidade e aumenta a conabilidade dos programas.

1.1

Histria do C++

A linguagem de programao C foi desenvolvida pela AT&T com o propsito de escrever um sistema operativo para a srie de computadores PDP-11 que acabaria por ser o sistema operativo UNIX. O C foi desenvolvido com o principal objetivo de ser eciente. Bjarne Stroustrup, tambm da AT&T, desenvolveu o C++ para acrescentar construes orientadas a objetos na linguagem C. C++ pode ser visto como uma linguagem procedimental com alguns construtores adicionais. Comeando pelo C, alguns construtores para programao orientada a objetos e para melhorar a sintaxe procedimental foram acrescentados. Como j dito anteriormente, um programa bem escrito em C++ ir reetir elementos tanto do estilo de programao orientada a objetos como programao procedimental clssica. E isto porque o C++ uma linguagem extensvel, ou seja, podemos denir novos tipos de tal maneira que eles ajam do mesmo modo que tipos pr-denidos que j fazem parte da linguagem padro.

Introduo ao C++

1.2

Primeiros Programas

Iniciaremos o estudo com um pequeno exemplo, que demonstrar algumas diferenas sintticas de C++ em relao linguagem C. O programa apenas escreve Hello World! no console, como todo bom primeiro programa em uma linguagem de programao. A seguir, cada parte do programa ser vista em detalhe.

Cdigo 1.1: Hello World


// Pr i me ir o programa C++ #include <i o s t r e a m > i n t main ( ) { s t d : : c o u t << " H e l l o World " << s t d : : e n d l ; // E s c r e v e no t e r m i n a l return 0 ; }

Pode se observar que a primeira linha precedida por //. Essa a sintaxe do comentrio de uma linha do C++. O compilador deve ignorar tudo aquilo que estiver direita de duas barras invertidas, mesmo que no no comeo, como se pode ver algumas linhas depois. Vale observar que os comentrios do C (iniciados por /* e terminados por */ ) t ambm so utilizados em C++, principalmente para fazer comentrios de vrias linhas. A diretiva #include conhecida para os programadores de C, bastando observar que a biblioteca no a mesma e que, para bibliotecas padro, no includo o .h do arquivo header. As funes std::cout e std::endl, substituem, respectivamente, a funo printf e o caractere \n do C. S para relembrar, isso signica que std::cout escrever na tela, enquanto std::endl adicionar uma nova linha. Uma das grandes vantagens desse mtodo a segurana de tipos, ou seja, o fato de que a funo sabe qual o tipo do dado que est sendo passado a ela, e responde de acordo. Por exemplo, o cdigo 1.2 imprime a frase Meu nome Lucas, tenho 18 anos.. A entrada em C++ to simples quanto a sada, trocando apenas o nome da varivel para std::cin e a direo dos operadores, para . O tipo da varivel e o signicado dos operadores e sero discutidos em captulos posteriores, por enquanto basta aprender seu funcionamento. O programa 1.3, que l duas variveis da tela e imprime sua soma, trivial e tem por objetivo xar a sintaxe, de modo que no ser detalhado.

1.2 Primeiros Programas

Cdigo 1.2: Exemplo


// Im pressao com c o u t #include <i o s t r e a m > i n t main ( ) { char nome [ ] = " Lucas " ; int idade = 1 8 ; s t d : : c o u t << "Meu nome eh " << nome << " , tenho " << i d a d e << " anos . " << s t d : : e n d l ; }

Cdigo 1.3: Entrada


//Mesmo que o exemplo a n t e r i o r , mas usando namespaces #include <i o s t r e a m > using namespace s t d ; // Para nao a b r i r t o d o namespace poderiamos u s a r / using std : : cin ; using std : : cout ; using std : : endl ; / i n t main ( ) { char nome [ 3 0 ] ; int idade ; c i n >> nome ; c i n >> i d a d e ; c o u t << "Meu nome eh " << nome << " , tenho " << i d a d e << " anos . " << e n d l ;

Introduo ao C++

1.3

Namespaces

Nos exemplos anteriores, podemos perceber a prexao de trs funes (cin, cout e endl) por std::. Isto no uma coincidncia, ou uma excentricidade do autor da linguagem. O fato, que as trs funes pertencem a um mesmo namespace (ambiente de nomes), no caso o ambiente de nomes padro do C++, std. Caso desejemos, podemos omitir o std na frente de cada funo, contanto que o compilador seja previamente avisado. Isso feito declarando-se using namespace std; antes da funo main do programa.

1.4

Variveis Locais e Globais

Em C++, todo bloco de comandos deve estar entre chaves ({ }). Segundo as regras de hierarquia de escopo, qualquer varivel declarada dentro de um bloco visvel apenas dentro do mesmo (inclua-se qualquer sub-bloco), sendo destruda assim que o bloco nalizado. Essas so chamadas variveis locais, em contraponto s variveis declaradas fora de qualquer bloco, chamadas variveis globais. Foi introduzido na linguagem um novo operador: o operador de escopo, representado por ::. Este operador utilizado para se acessar uma varivel de escopo superior cujo nome seja o mesmo de uma varivel local (neste caso, a varivel de escopo superior estaria inacessvel). Exemplicando: caso se tenha uma varivel com o nome x num escopo global e declare-se uma varivel x num escopo local, teoricamente a varivel x global caria inacessvel no respectivo bloco. Contudo, referenciando-se ::x possvel acessar o x global. Cdigo 1.4: Escopos
#include <i o s t r e a m > using namespace s t d ; // demonstrando os d i f e r e n t e s e s c o p o s int i = 1 2 ; i n t main ( ) { int i ; // uso do o p e r a d o r : : f o r ( i =0 ; i < : : i ; i ++) { c o u t << i << e n d l ; } }

1.5 Tipos de Dados

1.5

Tipos de Dados

Em C++ as variveis podem ser de cinco tipos primrios distintos: int: valores inteiros, variando entre 16 e 32 bits dependendo do compilador; char: caracteres, variando entre -128 e 127, de acordo com a tabela ASCII; oat: valores em ponto utuante (reais); double: valores em ponto utuante, com preciso dupla; bool: valor false (0) ou true (1). A declarao dos tipos feita de modo bastante similar a C, diferindo apenas para ponteiros, onde usado o comando new, em vez de malloc, e delete, em vez de free. Podemos ver a sintaxe desses comandos no cdigo 1.5.

Cdigo 1.5: Comando new e delete


#include <i o s t r e a m > using namespace s t d ; i n t main ( ) { // a l o c a o v e t o r int vetor ; // descomente para t e r uma s u r p r e s a ! // v e t o r [ 0 ] = 3 ; v e t o r = new i n t [ 1 0 ] ; v e t o r [ 0 ] = 3 ; // ok c o u t << v e t o r [ 0 ] << e n d l ; // d e l e t a o v e t o r delete ( v e t o r ) ; }

1.6

Modicadores

Modicadores so utilizados para especicar determinados tipos de tratamento s variveis. Os dois tipos mais comuns so const e static. Const declara uma varivel (ou funo, como veremos posteriormente) como sendo constante,

Introduo ao C++ de modo que o compilador previne erros do programador (caso seja feita uma tentativa errnea de alterao do valor). Alm disso, o compilador faz algumas otimizaes quando sabe que o valor de uma varivel no ser alterado. A palavra-chave static usada principalmente dentro de funes (tambm usada em classes, mas isso ser discutido no momento adequado). Ela serve pra dizer que o valor da varivel no deve ser descartado no nal na execuo, como seria normal a uma varivel local, mas guardado para uso posterior. O cdigo 1.6 demonstra isso. Cdigo 1.6: Modicadores
// Demonstrando o m o d i f i c a d o r s t a t i c #include <i o s t r e a m > void i n c r e m e n t a ( ) { s t a t i c in t c = 0 ; c++; s t d : : c o u t << c << s t d : : e n d l ; } i n t main ( ) { incrementa ( ) ; incrementa ( ) ; } return 0 ;

1.7

Estruturas de Controle

As estruturas em C++ seguem o padro da linguagem C, devendo-se atentar apenas para a declarao de variveis locais dentro do bloco, que no sero enxergadas por outras partes do programa. A sintaxe apresentada nos cdigos 1.7 a 1.10 j deve ser conhecida e fornecida apenas para ns de reviso. Algumas observaes, porm, so vlidas, no que diferem do C. Por exemplo, a declarao de uma varivel dentro da inicializao do for bastante usual entre programadores de C++.

1.8

Funes

Funes tm um papel bastante importante em C++, pois possibilitam a reutilizao de cdigo e segmentao dos programas, o que os torna muito mais

1.8 Funes

Cdigo 1.7: do .. while


#include <i o s t r e a m > using namespace s t d ; i n t main ( ) { int a = 0 , b = 0 ; c i n >> a >> b ; c o u t << " Testando o w h i l e " << e n d l ; // do . . . w h i l e while ( a < b ) { c o u t << a << e n d l ; a += 1 ; } c o u t << " Agora o do / w h i l e " << e n d l ; do { c o u t << a << e n d l ; a = 1 ; } while ( a > b ) ; }

Cdigo 1.8: for


#include <i o s t r e a m > using namespace s t d ; i n t main ( ) { int a = 0 ; c i n >> a ; c o u t << " Testando o f o r " << e n d l ; f o r ( i n t i =0; i < a ; i ++) c o u t << i << e n d l ; }

Introduo ao C++

Cdigo 1.9: if .. else


#include <i o s t r e a m > using namespace s t d ; i n t main ( ) { int a = 0 , b = 0 ; c i n >> a >> b ; c o u t << " Testando i f " << e n d l ; i f (a > b) { c o u t << a ; } else { c o u t << b ; } c o u t << e n d l ; }

fceis de serem compreendidos e depurados. vlido lembrar que existem dois modos de passar um parmetro para uma funo: por valor e por referncia. Por valor, passada apenas uma cpia da varivel para a funo de modo que a varivel original no ser alterada. Por referncia, passado um ponteiro para a varivel, de modo que se o valor dela for alterado dentro da funo, essa mudana ser reetida fora tambm. A sintaxe usada e o efeito dos dois tipos de passagem de parmetro so demonstrados no cdigo 1.11.

1.8.1

Funes inline

Chamadas de funes envolvem um overhead durante a execuo. Para reduzir este overhead, a linguagem d suporte a funes inline. Dessa forma, utilizando o qualicador inline antes do tipo de retorno de uma funo aconselhamos o compilador a gerar cpias do cdigo da funo para evitar chamar a funo. Contudo, este artifcio deve ser utilizado apenas com funes pequenas e freqentemente usadas. Alm disso, o uso de funes inline pode diminuir o tempo de execuo, mas deve aumentar o tamanho do programa.

1.8.2

Parmetros Default

Normalmente, preciso passar todos os valores de parmetros numa chamada de funo. Contudo, possvel determinar valores default para parmetros de uma funo; assim, pode-se chamar a funo com menos argumentos. Faz-se

1.8 Funes

Cdigo 1.10: switch .. case


#include <i o s t r e a m > using namespace s t d ; i n t main ( ) { int a = 0 ; c i n >> a ; c o u t << " Testando o s w i t c h " << e n d l ; switch ( a ) { case 1 : c o u t << "um" ; break ; case 2 : c o u t << " d o i s " ; break ; case 3 : c o u t << " t r s " ; break ; default : c o u t << "Nenhum dos t r e s " ; } c o u t << e n d l ; }

10

Introduo ao C++

Cdigo 1.11: Funes


#include <i o s t r e a m > void i n c r e m e n t a N a o A l t e r a ( i n t x ) { x += 1 ; s t d : : c o u t << " Dentro da f u n c a o : " << x << s t d : : e n d l ; } void i n c r e m e n t a A l t e r a ( i n t x ) { x += 1 ; s t d : : c o u t << " Dentro da f u n c a o : " << x << s t d : : e n d l ; } i n t main ( ) { int v a l o r = 0 ; s t d : : c o u t << v a l o r << s t d : : e n d l ; incrementaNaoAltera ( v a l o r ) ; s t d : : c o u t << v a l o r << s t d : : e n d l ; i n c r e m e n t a A l t e r a (& v a l o r ) ; } s t d : : c o u t << v a l o r << s t d : : e n d l ;

Cdigo 1.12: Funes Inline


#include <i o s t r e a m > #define CUBO( x ) xxx //MACRO using namespace s t d ; i n l i n e i n t cubo ( i n t x ) { return xxx ; } //INLINE i n t main ( ) { c o u t << cubo ( 5 ) << e n d l ; // 125 c o u t << CUBO( 5 ) << e n d l ; // 125 c o u t << cubo (2+3) << e n d l ; // 125 c o u t << CUBO(2+3) << e n d l ; // 17 return 0 ;

1.8 Funes isso fazendo uma atribuio para cada parmetro default dentro da declarao da funo. Contudo, para o compilador no se confundir, sempre que um parmetro omitido todos parmetros esquerda devem ter um valor default tambm e devero ser omitidos. Cdigo 1.13: Parmetros Default
#include <i o s t r e a m > using namespace s t d ; i n t cubo ( i n t x = 3 ) { return xxx ; } i n t main ( ) { c o u t << cubo ( 5 ) << e n d l ; c o u t << cubo ( ) << e n d l ; }

11

Captulo 2

Introduo a Orientao a Objetos


Nessa seo, sero introduzidos os alicerces da programao orientada a objetos, um dos principais recursos exclusivos de C++ em relao a C. O total entendimento dos conceitos a seguir imprescindvel para a continuao do curso. Para entender a Orientao a Objetos, preciso inicialmente que se entenda a que nos referimos quando falamos de objeto. O conceito de objeto na programao bastante similar ao usual. Um objeto possui caractersticas e realiza determinadas aes.

2.1

Uma classe Carro

Um carro um exemplo de objeto. Quando falamos de um objeto carro, devemos lembrar que um objeto nico. Um objeto carro um carro especco. Existem, porm, objetos com caractersticas semelhantes, seguindo o exemplo anterior, dois carros. Ainda que no sejam iguais, possuem atributos em comum e realizam, a grosso modo, as mesmas funes. Descrever as caractersticas e aes de cada objeto carro separadamente extremamente redundante. Existe, ento, o conceito de classe, uma generalizao de objetos de modo a possibilitar seu agrupamento. Criamos a classe Carro, e denimos os dois carros como objetos dessa classe. A gura abaixo facilita o entendimento.

14

Introduo a Orientao a Objetos

Simplicando, podemos denir uma classe como um tipo de dados, porm que possui funes em sua denio, alm de atributos. Vendo dessa forma, entenderamos um objeto como sendo uma varivel desse tipo. Uma observao importante a de que dois objetos iguais no so o mesmo objeto, como enfatiza a gura. Esse erro conceitual, bastante freqente no incio do aprendizado, pode ser facilmente eliminado quando fazemos a analogia classe/objeto e tipo/varivel. Duas variveis inteiras de valor 5 obviamente no so a mesma varivel, ainda que sejam do mesmo tipo e de mesmo valor.

2.2
2.2.1

Classes
Encapsulamento

O encapsulamento se constitui do englobamento de funes e dados dentro de classes. Esse o conceito mais bsico e abrangente da Orientao a Objeto, e apenas uma formalizao daquilo que entendemos como programao atravs desse paradigma. Abaixo, podemos ver uma comparao entre a programao baseada em procedimentos e a orientada a objetos. Como podemos ver, a classe Carro engloba todos os dados e funes que se referem ao tipo de dados Carro.

2.2 Classes

15

Os ganhos da Orientao a Objeto em relao a organizao, cam claros quando se imagina uma funo main contendo dois objetos da classe carro em comparao a variveis como corCarro1 e corCarro2, tipoCarro1 e tipoCarro2, aceleraoCarro1 e aceleraoCarro2. Uma vez entendida a idia, necessrio que se veja a sintaxe da implementao de classes no C++. Para isso, utilizaremos nosso exemplo da classe Carro, apresentado no cdigo 2.1. Cdigo 2.1: Classe Carro
c l a s s Carro { public : Carro ( ) ; ~ Carro ( ) ; void a c e l e r a r ( ) ; void f r e a r ( ) ; private : int cor ; int a c e l e r a c a o ;

};

Como podemos ver, existem pequenos detalhes na implementao atravs da linguagem, mas basicamente se trata do que foi conceitualmente discutido.

16

Introduo a Orientao a Objetos Uma classe englobando atributos e funes a ela referentes. As pequenas peculiaridades da prtica sero vistas a seguir.

2.2.2

Membros Pblicos e Privados

Pode-se observar no exemplo que existem duas palavras chaves precedendo determinado grupo de dados ou funes, public e private (existe tambm um tipo de dados protected, que ser tratado posteriormente). Dados pblicos (public) so aqueles que podem ser acessados de qualquer lugar do programa. Dados privados (private), pelo contrrio, so reconhecidos apenas por funes membro da classe. Para acessar dados privados, usual que se implementem funes set e get. Funes get retornam o valor do objeto, e funes set setam um novo valor para ele. A motivao para o uso de dados privados pode parecer obscura em um primeiro momento (no seria mais fcil permitir o acesso?), mas bastante til permitir a manipulao de dados apenas atravs de funes da classe. Pode ser necessria uma validao de dados no momento da atribuio, como mostra o cdigo 2.2. Cdigo 2.2: Validao de entrada
#include " c a r r o . h " void Carro : : s e t C o r ( i n t novaCor ) { i f ( ( novaCor >= 0 )&&(novaCor <= 2 5 5 ) ) { c o r = novaCor ; } else { s t d : : c o u t << " Cor I n v a l i d a ! " << s t d : : e n d l ; } }

No exemplo acima, so aceitas apenas 256 cores diferentes. Ainda que isso seja informado ao usurio, no se pode garantir que ele realmente siga essa recomendao. Torna-se ento clara a utilidade dos membros privados (e das funes set, como so chamadas geralmente as funes de atribuio). Caso cor fosse um membro pblico, a vericao teria de ser feita pelo programador fora da classe, ou se permitiria que fossem atribudos dados invlidos varivel. A possibilidade de realizar a validao dos dados internamente, torna a classe auto-contida, tornando-a mais robusta e convel, e contribuindo para a organizao e reutilizao do cdigo.

2.2 Classes

17

2.2.3

Construtores e Destrutores

Alm da diviso dos dados entre pblicos e privados, outro detalhe estranho chama diretamente nossa ateno quando olhamos pela primeira vez a implementao de uma classe. Existem duas funes com o nome da classe, e sem qualquer tipo. No nosso exemplo, Carro() e Carro(). A primeira funo, Carro(), o construtor da classe. Construtores so os responsveis pela alocao de memria e inicializao de dados, sendo sempre chamados automaticamente na declarao um novo objeto. O uso de construtores elimina o problema de ter que manualmente inicializar os dados sempre que declarados, o que pode ser esquecido e levar a erros. Cdigo 2.3: Construtor
#include " c a r r o . h " Carro : : Carro ( ) { cor = 0; }

Alm do construtor padro, ou seja, sem qualquer parmetro, podem existir construtores que recebam, por exemplo, um parmetro cor. Deve ser permitido, porm, que o usurio declare objetos da classe Carro sem fornecer parmetros, de modo que deve ser declarado um construtor padro, ou, pelo menos, fornecidos valores padro para os parmetros. Uma explicao mais aprofundada sobre o uso de duas funes com mesmo nome (mesmo construtores), ser vista ao decorrer do curso, quando forem tratadas as funes sobrecarregadas. Cdigo 2.4: Construtor com parmetros
#include " c a r r o . h " Carro : : Carro ( i n t novaCor ) { c o r = novaCor ; }

Para atribuir um valor padro ao parmetro novaCor, basta atribuir a ele o valor desejado j dentro da declarao. A outra funo, Carro(), o destrutor da classe, a qual chamada automaticamente sempre que um objeto deixa de existir (provavelmente porque o programa saiu de seu escopo). Destrutores podem tambm ser chamados pelo programador quando no for mais necessrio, visto que liberam a memria alocada para o objeto. Na maioria dos exemplos dessa apostila, um destrutor

18

Introduo a Orientao a Objetos

Cdigo 2.5: Construtor com parmetros default


#include " c a r r o . h " Carro : : Carro ( i n t novaCor = 2 5 5 ) { c o r = novaCor ; }

vazio ser suciente. Cdigo 2.6: Destrutor


#include " c a r r o . h " Carro : : ~ Carro ( ) { }

vlido observar que os destrutores no seguem a mesma regra que os construtores, no sendo possvel que recebam parmetros.

2.2.4

Diviso de Arquivos

Antes de comear a implementar classes em C++, preciso que se observe um detalhe importante das boas prticas de programao. Quando se cria uma classe, no raro pretender que ela seja utilizada por outras pessoas posteriormente. No necessrio compartilhar com cada usurio como foi feita a implementao da classe. Para esconder o cdigo, separamos a denio e a implementao, utilizando, respectivamente, arquivos header (.h) e source (.cpp). Nos arquivos header, coloca-se idealmente apenas aquilo que dene como uma classe deve ser utilizada, basicamente seus atributos e os cabealhos de suas funes, sem qualquer referncia a como as funes so implementadas. Diz-se idealmente, pois nem sempre recomendado, por questes de desempenho, que se faa isso. A disputa organizao/desempenho, porm, no possui resposta correta, devendo cada um fazer seu prprio julgamento sobre o que mais importante. As bases para tal discusso sero dadas mais tarde. Um exemplo de denio de classe pode ser visto no cdigo 2.2.4. Observe que no incio do arquivo existem um #ifndef e o nome da classe. Para explicar a necessidade dessa linha, ser usado um exemplo: Suponhamos que existam duas classes de um mesmo programa, por exemplo, Imagem e Boto, e as duas classes possuam um objeto da classe Vetor2D para indicar sua posio na tela. As duas classes devem ento, incluir o arquivo da classe

2.2 Classes
#i f n d e f __VETOR2D_H #define __VETOR2D_H c l a s s Vetor2D { public : Vetor2D ( ) ; ~Vetor2D ( ) ; i n t getX ( ) ; i n t getY ( ) ; void setX ( i n t newX) ; void setY ( i n t newY) ; private : int x , y ;

19

#endif

Vetor2D. O problema que uma classe no pode ser includa mais de uma vez, j que, se isso acontece, uma varivel redeclarada, e o compilador acusa um erro de Nome-davarivel redened. Para que isso no ocorra, criamos uma ag que testa se o arquivo j foi includo.

Cdigo 2.7: Diretiva ifndef


#i f n d e f __VETOR2D_H // s e a v a r i a v e l nao e s t a d e f i n i d a #define __VETOR2D_H // d e f i n e a v a r i a v e l c l a s s Vetor2d / declara a classe / #endif // t e r m i n a o i f

Dessa forma, o cdigo de declarao s executado uma vez (a prxima vez, o smbolo VETOR2D H j estar declarado). Este um detalhe bastante importante, sendo impossvel levar adiante projetos grandes sem a utilizao dessas ags. Nos arquivos source, deve ser colocado aquilo que o usurio da classe no deve ver, ou seja, a implementao das funes membro. No esquea de incluir o arquivo header, para que o arquivo possa enxergar as denies. fundamental adicionar Nome-da-classe:: antes dos nomes de funo, para que o compilador saiba que se trata de uma funo da respectiva classe. A implementao da classe acima segue em 2.8:

20

Introduo a Orientao a Objetos

Cdigo 2.8: Vetor 2D


#include " vetor2D . h " Vetor2D : : Vetor2D ( ) { } Vetor2D : : ~ Vetor2D ( ) { } i n t Vetor2D : : getX ( ) { return x ; } void Vetor2D : : setX ( i n t newX) { x = newX ; } i n t Vetor2D : : getY ( ) { return y ; } void Vetor2D : : setY ( i n t newY) { x = newY ; }

2.3 Composio

21

2.3

Composio

Por composio entende-se objetos de classes fazendo parte de outras classes. Este um princpio bsico da engenharia de software, onde mdulos menores fazem parte de mdulos maiores do projeto. E faz bastante sentido tambm, por exemplo, a classe Carro ter um componente que o motor (um objeto da classe Motor) e um vetor de quatro rodas (quatro objetos da classe Roda). Para tomar esse tipo de deciso, costuma-se fazer a pergunta tem um?. Ento: Um carro tem um motor?, Um carro tem quatro rodas?. Para denir a composio, basta denir o objeto dentro dos membros da classe da mesma forma como se faz com variveis primitivas.

2.4

Objetos Constantes

s vezes, interessante informarmos ao compilador que um objeto constante. Isso porque o compilador consegue realizar algumas otimizaes signicativas quando usamos constantes. Alm disso, caso seja feita uma tentativa de modicar uma constante, o compilador avisa e gera um erro sinttico, muito mais facilmente corrigvel que um erro de lgica. Para tornar um objeto constante, basta adicionar antes dele a palavra-chave const como mostrado no cdigo 2.9. Existem vrios usos para essa possibilidade, sendo o mais simples deles a passagem de parmetros constantes em funes, de modo que a funo no possa alterlos em seu interior. Cdigo 2.9: Argumentos Constantes
#include " c a r r o . h " void Carro : : s e t C o r ( const i n t novaCor ) { c o r = novaCor ; }

Dessa forma, o compilador acusa possveis inconsistncias do cdigo. Como qualquer programador sabe, encontrar um erro cuja linha acusada pelo compilador innitamente mais simples do que debugar um cdigo com erros de lgicas ou execuo. Outro uso dos objetos constantes quando desejamos passar o valor de um objeto grande para uma funo. Nesse caso, passar apenas uma referncia uma opo inegavelmente mais adequada, para que o programa no precise fazer uma cpia do objeto. Devemos lembrar, entretanto, que quando alteramos um objeto que foi passado por referncia a uma funo, essa alterao ser aplicada ao objeto tambm em seu escopo original. Desse modo, bastante aconselhvel que se avise ao compilador que no permita a alterao do objeto. Alm disso, a utilizao da palavra const contribui para a clareza do cdigo.

22

Introduo a Orientao a Objetos

2.5

Funes Membro Constantes

Funes membro constantes so funes que no alteram nenhum atributo do objeto em seu interior. Uma funo get, por exemplo, deve apenas retornar um atributo, de modo que no lhe deve ser possibilitado alterar o objeto. Impedir em tempo de compilao que isso seja feito considerado uma boa prtica de programao. Outro motivo para declarar uma funo como sendo constante que apenas uma funo constante pode ser chamada por um objeto constante. Por exemplo, suponhamos que foi passado um objeto Carro constante para uma funo. A essa funo apenas ser permitido usar a funo getCor() se essa funo for constante, ainda que ela no altere o objeto. Para declarar uma funo constante, basta adicionar a palavra-chave const aps a lista de parmetros que essa funo recebe. Como exemplicado na listagem 2.10. Cdigo 2.10: Mtodos Const
#include " c a r r o . h " i n t Carro : : g e t A c e l e r a c a o ( ) const { return a c e l e r a c a o ; } void u l t r a p a s s a r C a r r o ( const Carro &c a r r o _ a _ s e r _ u l t r a p a s s a d o ) { carro_a_ser_ultrapassado . getAceleracao ( ) ; }

Quando comeamos a trabalhar com o modicador const natural que haja um pouco de confuso, pois, por exemplo, as seguintes declaraes so igualmente vlidas [2.11]: Cdigo 2.11: Tipos de declarao Const
const Fred p ; Fred const p ; const Fred const p ;

Primeiro considere que Fred nesses exemplos um nome de qualquer tipo de dados (classe ou tipo primitivo). Porm, antes que voc que voc se assuste com esses exemplos, note que a melhor maneira de ler esse tipo de declarao indo da direita para a esquerda. Ento no exemplo anterior teremos: const Fred* p: p um ponteiro para um Fred constante, logo no podemos usar p para mudar o valor do Fred apontado; Fred* const p: p um ponteiro constante para um Fred, logo podemos mudar o valor do Fred, mas no podemos mudar para qual Fred p aponta;

2.6 Ponteiro this const Fred* const p: p um ponteiro constante para um Fred constante, o que signica que no podemos mudar o Fred e nem para que Fred este ponteiro aponta.

23

2.6

Ponteiro this

s vezes, desejamos fazer referncia ao prprio objeto, dentro da classe. Para essas situaes, existe um ponteiro this que aponta para o objeto da classe. Mais tarde, por exemplo, veremos como implementar para uma classe um operador +=. Nesse caso, interessante retornar o objeto, acrescido de algum atributo. Utilizemos a funo MaisIgual para simular a semntica de um operador += entre vetores. A sintaxe seria como mostrado no cdigo 2.12. Cdigo 2.12: Operador +
Vetor2D m a i s I g u a l ( Vetor2D vetor_a_ser_somado ) { x += vetor_a_ser_somado . getX ( ) ; y += vetor_a_ser_somado . getY ( ) ; return t h i s ; }

Captulo 3

Sobrecarga
Sobrecarga, pode ser encarada como uma prtica utilizada na programao, que visa associar a um mesmo nome (ou smbolo) mais de um comportamento. Para entender o conceito vamos comear com um tipo simples de sobrecarga, a sobrecarga de funes. Entende-se por isso, denir vrias funes com o mesmo nome. Em C++ esta prtica possvel, desde que o conjunto de parmetros de todas seja diferente. O compilador consegue denir qual funo chamar de acordo com a lista de parmetros da funo chamada. Neste captulo sero abordadas tcnicas e possibilidades de sobrecarga na linguagem C++.

3.1

Quando usar?

A prtica da sobrecerga comum e aconselhada para funes que fazem coisas parecidas, ou seja, possuem uma mesma semntica, mas agem sobre um conjunto diferente de dados. Vale notar que abusar deste tipo de notao pode tornar nosso cdigo de difcil leitura. Alm disso preciso tomar muito cuidado com funes sobrecarregadas e funes com parmetros default, pois bem fcil de se deparar com uma ambigidade misturando estas facilidades da linguagem. O compilador no tem condio, nessas situaes, de decidir que funo chamar, como exemplicado no cdigo 3.1.

3.2

Modicador friend

Antes de iniciarmos o assunto do captulo propriamente dito, ser necessrio abordar um pequeno assunto da linguagem C++: funes e classes friend. Uma funo friend denida fora do escopo de uma classe, no sendo uma funo membro . Ainda assim, esta funo tem permisso para acessar membros private e protected da classe (e os membros public, naturalmente). Usar funes friend melhora o desempenho, entretanto, alguns estudiosos consideram que funes friend violam a ocultao de dados da orientao a

26

Sobrecarga

Cdigo 3.1: Sobrecarga


#include <i o s t r e a m > using namespace s t d ; i n t quad ( i n t x ) { return xx ; } f l o a t quad ( f l o a t x ) { return xx ; } i n t main ( ) { c o u t << quad ( 2 ) << e n d l ; c o u t << quad ( 2 . 5 ) << e n d l ; return 0 ; }

objetos. Para declarar uma funo como friend de outra classe deve-se preceder o prottipo da funo na denio da classe com a palavra-chave friend. Uma classe friend funciona da mesma forma que uma funo friend, isto , tem acesso tanto a membros private como protected da classe da qual friend. Para declarar uma classe como friend de outra, deve-se colocar na declarao da segunda friend. Exemplos da sintaxe de friend podem ser vistos nos cdigos 3.2, 3.3.

3.3

Fundamentos de Sobrecarga de Operadores

Sobrecarga de Operadores um recurso bastante poderoso da linguagem C++, que, por sua vez, no existe em outras linguagens como Java. Na verdade, sua inexistncia em outras linguagens se deve ao fato de que tudo que pode ser feito com este recurso pode ser realizado tambm de outras maneiras, porm, no de maneira to clara e elegante. Basicamente, a sobrecarga de operadores permite ao programador dena o comportamento dos operadores bsicos da linguagem, como o operador de adio (+) ou de atribuio (=), para que trabalhem de maneira correta com os tipos de dados criados pelo usurio. Isto , ser possvel usar tais operadores com suas prprias classes, ao invs de apenas com os tipos de dados primitivos. Imagine, por exemplo, uma classe Vetor (um segmento de reta com origem em um ponto do espao e destino em outro ponto do espao). Esta classe foi criada para auxili-lo nas suas tarefas de clculo. Contudo, como foi uma classe

3.3 Fundamentos de Sobrecarga de Operadores

27

Cdigo 3.2: Funo Friend


class X { public : X( ) {} ~X( ) {} friend void f (X a l g o ) ; private : }; int a , b , c ;

void f (X a l g o ) { algo . a + algo . b + algo . c ; } i n t main ( ) { X alguem ; f ( alguem ) ; // okay }

Cdigo 3.3: Classe Friend


c l a s s ClasseN { friend c l a s s ClasseM ; c l a s s ClasseM { . . . }; ... };

28

Sobrecarga criada pelo usurio, o compilador no saberia como proceder diante da seguinte expresso: vetor1 = vetor2 + vetor3; Visto que os operadores de adio e atribuio esto denidos apenas para os tipos primitivos de dados. Para resolver este pequeno problema, precisamos utilizar o recurso de sobrecarga de operadores disponibilizado pelo C++. A prpria linguagem utiliza este recurso internamente. O operador de adio, por exemplo, trabalha diferentemente com ints, oats e doubles, entretanto, utiliza-se o mesmo operador para realizar tais operaes aritmticas. Os seguintes operadores da linguagem C++ podem ser sobrecarregados:

Os seguintes operadores da linguagem C++ NO podem ser sobrecarregados:

Cabe lembrar que o uso excessivo de sobrecarga de operadores pode tornar o cdigo bastante difcil de ler. Logo, este recurso deve ser utilizado de maneira muito bem pensada e, de preferncia, com algum signicado similar ao signicado original do operador. Classes matemticas utilizam muito bem este recurso, como, por exemplo, uma classe de nmeros complexos.

3.4

Funes Friend

A sobrecarga de operadores pode ser realizada de duas formas distintas. A primeira delas declarar a funo de sobrecarga de operador como uma funo membro de uma classe. Esta maneira a que deve ser sempre priorizada, contudo ela tem uma certa limitao: o operando a esquerda do operador deve ser obrigatoriamente da classe que contm a sobrecarga. Esta limitao no costuma ser um grande problema, porm existem situaes em que ser necessrio contornar este problema. Como forma alternativa, a segunda forma de sobrecarregar um operador declarando a funo de sobrecarga como uma funo friend da classe relacionada. Quando sobrecarregamos os operadores de insero em streams e extrao de streams ( e ), por exemplo, o operando da classe car direita, de modo que ser necessrio utilizar esse artifcio.

3.5 Sobrecarga de Operadores Unrios

29

3.4.1

Sintaxe

A sintaxe para realizar a sobrecarga de um operador bastante parecida de uma funo comum, contudo o nome da funo deve iniciar com a palavra reservada operator seguido do operador a ser sobrecarregado. Como mostrado no cdigo 3.4. Cdigo 3.4: Sobrecarga de Operadores
bool operator ! ( ) const ; const Vetor2D operator+ ( i n t e s c a l a r ) ; const Vetor2D operator ( i n t e s c a l a r ) ;

3.5

Sobrecarga de Operadores Unrios

A sobrecarga de operadores unrios pode ser realizada de duas formas diferentes, como foi visto anteriormente. Se escolhermos que a funo de sobrecarga deve ser uma funo membro da classe, ento ser uma funo sem parmetros, visto que o nico operando ser o prprio objeto da classe que realiza a chamada do operador, como podemos ver no exemplo 3.5. Cdigo 3.5: Operadores Unrios
// c l a s s e de numeros c o m p l e x o s c l a s s Complexo { public : bool operator ! ( ) ; / v e r i f i c a s e os termos r e a i s e i m a g i n a r i o s do numero complexo sao 0 / };

Entretanto, ainda podemos escolher que a funo de sobrecarga seja uma funo friend da classe em questo. Nesse caso, a funo dever ter um parmetro, que ser o operando modicado pelo operador. Fica claro, ento, o porqu da funo necessitar ser friend da classe: esta precisar acessar membros private da classe, e caso isto fosse realizado usando funes de encapsulamento (setter e getter) acarretaria em certa perda de desempenho, ou mesmo seria impossvel se nem todos os valores necessrios para computar a funo tivessem funes setter e getter correspondentes.

30

Sobrecarga

3.6

Sobrecarga de Operadores Binrios

A sobrecarga de operadores binrios feita de maneira bastante semelhante sobrecarga de operadores unrios. Cabe lembrar que o C++ no sobrecarrega operadores ternrios, o nico operador ternrio ( :? ) no est na lista de operadores que podem ser sobrecarregados. Considerando o primeiro caso, em que a funo de sobrecarga seja uma funo membro da classe em questo, necessitamos de apenas um parmetro, visto que o outro parmetro j o prprio objeto da classe em questo. Fica claro, assim, a limitao citada anteriormente: o operando da esquerda obrigatoriamente do mesmo tipo da prpria classe em questo e o operando da direita pode ser denido como qualquer tipo no parmetro da funo. Cdigo 3.6: Operadores Binrios
c l a s s Complexo { public : Complexo operator+= ( Complexo &c ) ; Complexo operator+ ( Complexo &c ) ; Complexo operator+ ( f l o a t f ) ; // . . . }; Complexo& Complexo : : operator+= ( Complexo &c ) { r e a l += c . r e a l ; i m a g i n a r i o += c . i m a g i n a r i o ; return t h i s ; } Complexo Complexo : : operator+ ( Complexo &c ) { return Complexo ( r e a l + c . r e a l , i m a g i n a r i o + c . i m a g i n a r i o ) ; } Complexo Complexo : : operator+ ( f l o a t f ) { return Complexo ( r e a l + f , i m a g i n a r i o ) ; }

J quando escolhemos que o operador binrio ser sobrecarregado atravs de uma funo friend da classe, podemos determinar o tipo de dado tanto do operador da esquerda quanto do operador da direita atravs da declarao dos dois parmetros da funo.

3.7

Sobrecarga dos Operadores de Streams

Para sobrecarregar os operadores de manipulao de streams, necessrio utilizar a sobrecarga atravs de funes friend, e no como funes membro da classe. Isso se deve ao fato de que, numa expresso deste tipo, o membro

3.7 Sobrecarga dos Operadores de Streams

31

Cdigo 3.7: Operadores Binrios Friend


c l a s s Complexo { friend Complexo operator+= ( Complexo &, Complexo & ) ; // . . . };

da classe car sempre no lado direito do operador. E deve-se lembrar que s se podem utilizar funes membro de uma classe para sobrecarga quando o operando a esquerda do operador for um membro desta classe.

3.7.1

Exemplo dos operadores de streams ( e )

cin >> x; cout << y; Fora isso, a sobrecarga dos operadores de streams semelhante s funes de sobrecarga vistas anteriormente. O cdigo 3.8 exemplicando como funciona este recurso. Esta seria a implementao das funes para o input e output de nmeros complexos. Cabe lembrar: Cdigo 3.8: Operadores de Streams
ostream &operator<< ( ostream &output , Complexo &c ) { output << " ( " << c . g e t R e a l ( ) << " , " << c . g e t I m a g i n a r i a ( ) << " )"; return output ; } i s t r e a m &operator>> ( i s t r e a m &input , Complexo &c ) { input . ignore () ; // s a l t a i n p u t >> c . r e a l ; input . ignore (2) ; i n p u t >> c . i m a g i n a r i a ; input . ignore () ; return i n p u t ; }

Agora, declare estas funes como friend da sua classe Complexo. Insira o cdigo das funes no arquivo .cpp e teste o funcionamento delas em um programa teste.

32

Sobrecarga

3.8

Converso entre Tipos

Freqentemente, necessrio converter dados de um tipo para o outro em programas e isto realizado utilizando operadores de cast. Contudo, o compilador s sabe realizar essas operaes entre os tipos primitivos de dados. Ento, caso deseje realizar um cast de um classe criada pelo usurio ser preciso implementar a funo que faz isso. O que pode ser visto no cdigo 3.9: Cdigo 3.9: Operador de Converso
// t i p o de dados c r i a d o p e l o u s u a r i o string s ; // c a s t de um t i p o c r i a d o p e l o u s u r i o para um t i p o p r i m i t i v o char x = ( char ) s ; // para o c a s o de s t r i n g s , recomendamos a s e g u i n t e n o t a c a o char y = s . c _ s t r ( ) ;

Atravs deste artcio, possvel realizar uma converso de uma classe para outra classe ou de uma classe para um tipo primitivo. O prottipo de funo para um operador de cast o seguinte:

3.9

Sobrecarregando ++ e - -

Os operadores de incremento e decremento tambm podem ser sobrecarregados, contudo estes tm certa peculiaridade devido ao fato de que podem ser pr/ps incremento/decremento. Na verdade, o nico cuidado que deve ser tomado o de diferenciar as funes para que o compilador saiba quando deve chamar cada funo. A verso de prexo, isto o pr-incremento e o pr-decremento, so implementadas da mesma forma que as funes de sobrecarga que vimos at agora, sem nenhuma mudana. A diferenciao vem na outra verso, a de ps-incremento e psdecremento, pois agora preciso diferenci-la da anterior. Para isso, foi adotada uma conveno em que este tipo de funo deve ter um parmetro fantasma inteiro, com o valor de 0. Cdigo 3.10: Sobrecarga do ++
Complexo operator++() ; // f u n c a o de prei n c r e m e n t o Complexo operator++(i n t x ) ; // f u n c a o de posi n c r e m e n t o

Ateno: Estas diferenas garantem apenas a chamada da funo correta. O programador devar garantir o fato da funo ser pr-incrementada ou psincrementada.

Captulo 4

Herana
Nesta aula iremos estudar um dos recursos mais importantes da programao orientada a objetos: a herana. Ela nos permite reutilizao de software de qualidade reconhecida, reduo da quantidade de cdigo repetido e nos auxilia na manuteno do software. No decorrer da aula mostraremos exemplos de cada uma destas utilizaes. Antes de comearmos a codicar a herana em C++ vamos entender seu signicado no mundo dos objetos. Segundo o site www.direitonet.com.br, herana: o patrimnio deixado pela pessoa falecida aos seus herdeiros, ou seja, a totalidade dos direitos e obrigaes de uma pessoa no momento em que vem a falecer. No mundo dos objetos, herana possui um sentido muito parecido com o exposto acima com duas diferenas: que no necessrio que uma classe morra para que outra possa herdar seus atributos e funes membros, nem a classe que herdada perde seus atributos e funes membros para a classe que a herdou; feita uma cpia deles. Vamos comear denindo melhor os conceitos apresentados acima:

4.1

Classe base e Classes Derivadas:

Classe base e Classes Derivadas: Uma relao entre objetos j vista foi a tem um, onde um objeto continha como membros objetos de uma classe distinta da sua. Por exemplo, um carro tem um passageiro. A herana introduz um relacionamento do tipo um, onde um objeto de uma classe um objeto de outra classe tambm. Podemos dizer que um retngulo um quadriltero (assim como um losango e um paralelogramo tambm o so). Assim, dizemos que a classe Retangulo herda da classe Quadrilatero. Dizemos ento que Quadrilatero a classe base enquanto Retangulo a classe derivada. Um dos exemplos mais clssicos de herana a classicao taxonmica dos seres vivos. Mostramos a seguir um exemplo que mostra bem a estrutura de rvore apresentada pela herana:

34

Herana

Vemos que a classe base para todas a classe Carnivora, que poderia apresentar uma funo membro void comerCarne(); (pode-ser argumentar que nem todos carnvoros comero carne da mesma maneira. Mais tarde veremos que isso pode ser resolvido atravs da sobrescrita dessas funes). Seguindo, vemos que a classe Canidae base para Canis e Vulpes, e que Felidae base para Felis e Panthera. Tendo todas essas classes denidas facil criarmos vrias classes novas: podemos ter um Cachorro, um Lobo e um Coiote sem termos que escrever muito cdigo, pois herdamos as caractersticas comuns da classe Canis. Tambm, podemos ter um Leao, um Tigre e uma Pantera como classes derivadas de Panthera, sem nos preocupar com suas caractersticas mais comuns (pois estas j esto escritas). Ou seja, o que zemos foi juntar as partes comuns as classes que queremos denir. Esse um exemplo da reduo de cdigo repetido. Temos tudo pronto. Agora, no entanto, descobrimos que existe um algortimo de alimentao mais eciente, e queremos implement-lo em nossos animais. Se no estivssemos usando herana, seria necessrio escrever a nova funo em cada uma das classes (quanto mais escrevermos, maiores as chances de erros). Mas, graas a herana, modicamos a funo em nossa classe base e todos animais estaro comendo dessa maneira (excetuando as classes onde sobrescrevemos a funo). Esse um exemplo da facilidade de manuteno de software que a herana proporciona. Vamos supor que o objetivo dessas classes era o de implementar uma simulao de zoolgico. O software terminado, testado e passa-se a us-lo. Alguns bugs vo aparecendo e sendo corrigidos e depois de um tempo o software se torna razoavelmente estvel. Aps alguns meses, chegam novos animais, um Leopardo por exemplo. J temos nossa classe Panthera com a qualidade reconhecida. Ao criarmos a classe Leopardo, ser fcil e teremos que nos concentrar em corrigir erros de um cdigo muito menor (apenas a parte especca do Leopardo). Esse um exemplo da reutilizao do cdigo.

4.2 Membros Protected

35

4.2

Membros Protected

At agora utilizamos duas formas de controle de acesso a membros: public e private. Como sabemos, membros public podem ser acessados por todas as funes no programa, enquanto membros private podem ser acessados apenas por funes membros da classe e suas friends. O acesso protected confere uma proteo intermediria entre os acessos public e private. Membros protected podem ser acessados por funes membros e funes membros de classes derivadas, assim como as friends da classe base (cabe ressaltar que membros friends no so herdados pelas classes derivadas e devem ser redeclarados se desejamos que a funo seja friend da nova classe).

4.2.1

Sintaxe

Representamos a herana em C++ utilizando os dois-pontos (:) aps o nome da classe derivada, a que queremos que herde as caractersticas, seguida por um dos trs tipos de herana que temos em C++ (public, private e protected), seguido do nome da classe base. Para declararmos que uma classe Derivada tem como base a classe Base, utilizando herana public, utilizamos o seguinte cdigo: class Derivada : public Base { ... }; O tipo de herana mais utilizado o public. Com herana public, os membros public e protected da classe base so herdados como membros public ou protected na classe derivada, respectivamente. Membros private da classe base no podem ser acessados na classe derivada. No entanto, podemos utilizar funes public ou protected da classe base para acessar seus membros private (como as funes sets e gets).

4.2.2

Exemplos de Herana

Agora vamos apresentar um exemplo de implementao de herana. Iremos criar a classe Canis e a classe Cachorro. A classe Canis ser base para Cachorro. As classes podem ser vistas nos cdigos, 4.1, 4.2, 4.3 Na implementao da classe derivada Cachorro, vemos que seu construtor chama o construtor da classe base Canis, atravs de um inicializador de membros. Sempre devemos antes inicializar a parte da classe Base. Se no chamarmos explicitamente o construtor atravs de um inicializador de membros, o construtor default ser chamado (no havendo construtor default, ser acusado um erro de sintaxe). Os construtores so sempre chamados seguindo a ordem da herana: da classe base mais alta na rvore at a classe mais baixa, no importando a ordem em que so escritos no cdigo. Destrutores so chamados na ordem inversa.

36

Herana

Cdigo 4.1: canis.h


// Header da c l a s s e Canis // Canis . h #i f n d e f CANIS_H #define CANIS_H c l a s s Canis { public : Canis ( const char a_name ) ; void speak ( ) ; protected : char nome ;

}; #endif

Cdigo 4.2: cachorro.h


// Header da c l a s s e Cachorro ; #i f n d e f CACHORRO_H #define CACHORRO_H #include " Canis . h " c l a s s Cachorro : public Canis // Cachorro he r da membros p u b l i c e // p r o t e c t e d de Canis ; { public : Cachorro ( const char a_name , const char a_owner ) ; protected : char owner ; }; #endif // Implementacao da c l a s s e Cachorro .

4.3 Coero de ponteiros entre classes bases e classes derivadas

37

Cdigo 4.3: cachorro.cpp


#include <i o s t r e a m > #include " Cachorro . h " using s t d : : c o u t ; using s t d : : e n d l ; // Header da c l a s s e ; Cachorro : : Cachorro ( const char a_name , const char a_owner ) : Canis ( a_name ) // I n i c i a l i z a c a o da p a r t e Canis do Cachorro . { owner = new char [ s t r l e n ( a_owner ) + 1 ] ; s t r c p y ( owner , a_owner ) ; }

4.3

Coero de ponteiros entre classes bases e classes derivadas

Uma das utilidades que a herana nos proporciona a de sempre podermos tratar nosso objeto como um objeto da classe Base. Supondo nossa simulao de zoolgico, nada seria preciso fazer para tratarmos o novo Leopardo se j tratssemos todos Felinos da mesma maneira. Ou seja, quem trata dos animais enxerga todos como objetos da classe Felidae, assim sendo, basta convertermos nosso Leopardo para Felidae que a funo void tratarFelinos(Felidae *listaFelinos,int quantidade); j resolve o problema. Mas, como fazer isso? A converso pode ser feita em dois sentidos: 1. Um ponteiro para uma classe base sempre pode referenciar automaticamente um objeto de uma classe derivada. Estaremos tratando assim esse objeto como se ele fosse da classe base. Funes e variveis especcas da classe derivada no podero mais ser acessadas por esse ponteiro. 2. Um ponteiro para uma classe base pode ser convertido para um ponteiro de uma classe derivada. No entanto, isso pode ser perigoso se feito sem cuidado. Podemos comear a tratar um Lobo como um Cachorro, e tentarmos levar ele passear, como visto no cdigo 4.4... O problema que o compilador cona, e deixa ns compilarmos e at tentarmos executar esse cdigo! Pior ainda, se no referenciarmos nenhuma varivel que o lobo no tenha, funcionar. Se mais tarde referenciarmos alguma, vamos ter uma bela dor de cabea tentando descobrir o erro. Sugesto: Isso no se faz! Tambm aparece no trecho de cdigo como podemos converter um ponteiro de classe derivada para um ponteiro de classe base: basta fazer uma atribuio que a converso automtica! Podemos, no entanto, realizar essas converses

38

Herana

Cdigo 4.4: Coero


#include " c a n i s . h " #include " c a c h o r r o . h " #include " l o b o . h " i n t main ( ) { Cachorro dogObj ( " Rex " , " Arthur " ) ; Lobo wolfObj ( " Lobo Mau" ) ; Canis c a n i s P t r ; Cachorro dogPtr ; Lobo w o l f P t r ; c a n i s P t r = &wolfObj ; // Ok ! Estamos t r a t a n d o o l o b o // como um Canis :D dogPtr = static_cast<Cachorro >(& c a n i s P t r ) ; // Temos um Canis . . . // Pode s e r um c a c h o r r o . . . s e r a ? dogPtr>l e v a r _ p a s s e a r ( ) ;

com segurana. Novas tcnicas foram introduzidas em verses mais recentes, entre elas: converso dinmica e descoberta de tipo em tempo real. Iremos dar uma breve introduo coero dinmica de ponteiros e como podemos utiliz-la para transitar entre ponteiros de classe base para classe derivada. Seu funcionamento o seguinte: tenta-se realizar a coero. Se bem sucedida, seu retorno o ponteiro coertido, caso contrrio o retorno o ponteiro null (0). Assim sendo, podemos substituir o cdigo antigo pelo em 4.5: Cdigo 4.5: Coero Dinmica de Tipos
// dynamic_cast<Derivada >( Ponteiro_Classe_Base ) i f ( dogPtr = dynamic_cast<Cachorro >( c a n i s P t r ) ) { dogPtr > l e v a r _ p a s s e a r ( ) ; }

Da mesma forma, nossa funo aparentemente genrica que alimenta todos nossos Felinos internamente pode trat-los individualmente, como exemplicado no cdigo 4.6: Como vemos, o mtodo consiste em encadear as tentativas de coeres e dentro dos ifs utilizar os ponteiros convertidos com segurana.

4.4 Sobrescrevendo membros da classe base em uma classe derivada 39

Cdigo 4.6: Mais Coero Dinmica de Tipos


void t r a t a r F e l i n o s ( F e l i d a e l i s t a F e l i n o s , i n t q u a n t i d a d e ) { Felis FelisPtr ; Panthera PantheraPtr ; f o r ( i = 0 ; i < q u a n t i d a d e ; i ++) { i f ( F e l i s P t r = dynamic_cast<Gato >( l i s t a F e l i n o s [ i ] ) ) { // Trata os F e l i n o s a t r a v e s de F e l i s P t r . } e l s e i f ( Panthera = dynamic_cast<Gato >( l i s t a F e l i n o s [ i ]) ) { // Trata os P a n t h e r a s a t r a v e s de P a n t h e r a P t r . } else { // Tratas e os o u t r a s c a s o s . } }

4.4

Sobrescrevendo membros da classe base em uma classe derivada

Ao sobrescrever uma funo de uma classe base estamos redenindo a funo. Quando a funo mencionada na classe derivada, a verso nova automaticamente selecionada. Pode-se selecionar a verso da classe base utilizando-se o operador de resoluo de escopo (::). Sobrescrevemos uma funo ao declararmos na classe derivada uma funo com mesma assinatura que na classe base. A sobrescrita de funes um recurso muito importante para o prximo tpico que Polimorsmo. Quando introduzirmos os conceitos de funes virtuais e polimorsmo que veremos uma utilidade realmente fascinante da sobrescrita de funes. A funo void tratarFelinos(...) poderia dispensar o uso da coero de ponteiros dinmica se cada vez que usssemos FelisPtr->comer(); fosse chamada a ltima funo sobrescrita, no lugar da declarada por Felis. Isso possvel, mas assunto para a prxima aula. O exemplo a seguir mostra a nova denio da classe cachorro e sua implementao com a sobrescrita da funo void speak();. Aps, mostrada um exemplo de sua utilizao com a chamada da funo por um ponteiro de Canis (ser chamada a primeira verso). Os cdigos podem ser encontrados em 4.7, 4.8 e 4.9.

40

Herana

Cdigo 4.7: Header da classe cachorro


/ Cachorro com a f u n c a o s p e a k ( ) / // Header da c l a s s e Cachorro ; #i f n d e f CACHORRO_H #define CACHORRO_H #include " Canis . h " c l a s s Cachorro : public Canis // Cachorro he r da membros p u b l i c e p r o t e c t e d de Canis ; { public : Cachorro ( const char a_name , const char a_owner ) ; void speak ( ) ; // S o b r e s c r i t a de v o i d s p e a k ( ) de Canis ; void l e v a r _ p a s s e a r ( ) ; protected : char owner ;

}; #endif // Implementacao da c l a s s e Cachorro .

Cdigo 4.8: Cachorro com funo speak


// { #i n c l u d e s n e c e s s a r i o s . . . s u p r i m i d o s } Cachorro : : Cachorro ( const char a_name , const char a_owner ) // I n i c i a l i z a a p a r t e Canis do Cachorro . : Canis (a_name) { owner = new char [ s t r l e n ( a_owner ) + 1 ] ; s t r c p y ( owner , a_owner ) ; } void Cachorro : : speak ( ) // S o b r e s c r e v e n d o a f u n c a o v o i d s p e a k ( ) ; { c o u t << " Metodo da c l a s s e b a s e " << e n d l ; Canis : : speak ( ) ; c o u t << e n d l << "Auauau ! ! ! " << e n d l ; } void Cachorro : : l e v a r _ p a s s e a r ( ) { c o u t << name << " , vem ! Sou eu , " << owner << " . Vamos p a s s e a r ? " << e n d l ; }

4.5 Tipos de Herana

41

Cdigo 4.9: Demonstrao da funo speak


i n t main ( ) { Canis c a n i s O b j ( " Canis01 " ) ; // Cria e i n i c i a l i z a um Canis Cachorro dogObj ( " Rex " , " Eu " ) ; // Cria e i n i c i a l i z a um Cachorro Canis c a n i s P t r = 0 ; c a n i s O b j . speak ( ) ; dogObj . speak ( ) ; c a n i s P t r = &dogObj ; c a n i s P t r >speak ( ) ; return 0 ; } // P o n t e i r o para um Canis ;

4.4.1

Output

Canis01 diz: Grrr... Rex diz: Grrr... Au-au-au!!! Rex diz: Grrr...

4.5

Tipos de Herana

At o momento todos exemplos utilizaram o tipo de herana public, o que permite relaes do tipo um (todo Cachorro um Canis tambm). A linguagem C++ permite outros dois tipos de herana: protected e private. Esses tipos so mais recentes e menos usados, principalmente por no permitirem relacionamentos do tipo um. Heranas do tipo protected so usadas muitas vezes como uma forma de composio. Denimos o tipo de herana desejada especicando seu tipo antes do nome da classe base (aps os dois-pontos). A diferena entre os tipos de herana est na forma como os membros herdados da classe base so vistos na classe derivada. Na herana public os dados com escopo protected continuam a ser protected na classe base e os dados com escopo public continuam a ser public na classe base. Na herana protected, os dados com escopo protected e public passam a ser protected. Na herana private, os dados protected e public passam a ser private. Os dados private da classe base nunca podem ser acessados diretamente pelas classes derivadas (isso quebraria o encapsulamento das classes). A tabela a seguir resume o que foi dito:

42 Especicador de Acesso public protected private

Herana Tipo de Herana public protected private public protected private protected protected private oculto oculto oculto

4.6

Herana Mltipla

A herana mltipla uma extenso da herana simples. Nela, a classe derivada herda as funes e dados de diversas classes bases ao mesmo tempo. caracterizada quando uma determinada classe C uma classe B e uma classe A ao mesmo tempo. Dominar a herana simples pode ser conseguido com o que foi apresentado e um pouco de prtica. Heranas simples so muito mais fceis de serem observadas e utilizadas em projetos de software do que a herana mltipla. Esta ltima um recurso muito poderoso de C++, possibilitando formas interessantes de reutilizao de software, mas muito mais sujeita a erros e pode causar diversos problemas de ambigidade. Vamos apenas mostrar um exemplo de herana mltipla, por se tratar de um tpico mais avanado. Nunca se deve utilizar a herana mltipla quando a herana simples for suciente. No exemplo abaixo, criamos as classes Homem e Lobo para criarmos ento a classe derivada Lobisomem atravs da herana mltipla. Lobo derivada de Canis (para treinarmos um pouco mais herana simples), enquanto Homem uma classe completamente nova. Observa-se que ambas as classes possuem as funes speak() (mas que acaba sendo sobrescrita aps), e que ambas possuem o atributo name. Isso um exemplo de ambigidade. Ao tentar criar a funo char *getNome() return name; foi gerado o seguinte erro de compilao: 1>c:\lobisomem.cpp : error C2385: ambiguous access of name 1> could be the name in base Homem 1> or could be the name in base Canis O que mostra os perigos da herana mltipla. No entanto, na sobrecarga de temos um exemplo de um uso interessante da herana mltipla, onde atravs do ponteiro this fomos capazes de chamar as funes speak(); de suas duas classes bases. O cdigo pode ser visto em, 4.10, 4.11, 4.12. Como pode ser observado, para criarmos uma herana mltipla basta colocarmos uma lista de classes base separadas por vrgulas aps os dois pontos depois do nome da classe derivada. No cdigo 4.13 podemos ver o teste e os seus resultados:

4.6.1

Output

Lobisomem de St. Bonnet diz: Grrr... Gilles Garnier diz: Ol pessoal! Vou devorar vocs!

4.6 Herana Mltipla

43

Cdigo 4.10: Classe Homem


// Header da c l a s s e Homem #i f n d e f H_HOMEM #define H_HOMEM c l a s s Homem { public : Homem( const char a_name ) ; void speak ( ) ; protected : char name ;

};

#endif

Cdigo 4.11: Classe Lobo


// Header da c l a s s e Lobo #i f n d e f H_LOBO #define H_LOBO #include " Canis . h " c l a s s Lobo : public Canis // Lobo h er d a membros p u b l i c e p r o t e c t e d de Canis ; { public : Lobo ( const char a_name ) ; }; #endif // . . . . . . . // Implementacao da c l a s s e Lobo . Lobo : : Lobo ( const char a_name ) : Canis ( a_name ) // I n i c i a l i z a c a o da p a r t e Canis de Lobo . // { Nenhum i n i c i a l i z a o extra . }

44

Herana

Cdigo 4.12: Classe Lobisomem


// Implementacao da c l a s s e Lobisomem // Ela h e rd a c a r a c t e r i s t i c a s dos humanos e dos l o b o s , // Tambem acaba p o s s u i n d o d o i s nomes ! ( a m b i g u i d a d e ) Lobisomem : : Lobisomem ( const char human_name , const char wolf_name ) : Homem(human_name ) , Lobo ( wolf_name ) { } void Lobisomem : : speak ( ) { Lobo l o b o p t r = static_cast<Lobo >( t h i s ) ; Homem homemptr = static_cast<Homem >( t h i s ) ; l o b o p t r >speak ( ) ; c o u t << e n d l ; homemptr>speak ( ) ; c o u t << e n d l ; c o u t << " Vou d e v o r a r v o c s ! \ n " ; }

Cdigo 4.13: Tudo funcionando


i n t main ( ) { Lobisomem l o b i s ( " G i l l e s G a r n i e r " , " Lobisomem de St . Bonnet " ) ; l o b i s . speak ( ) ; return 0 ; }

Captulo 5

Funes Virtuais e Polimorsmo


O polimorsmo a capacidade de alguns ponteiros apontarem para diferentes tipos de dados. Essa idia no trivial e est muito conectada ao conceito de herana entre classes, de modo que se faz necessrio um entendimento profundo dos captulos anteriores e uma dedicao especial ao estudo deste.

5.1

Funes Virtuais

Suponha a seguinte hierarquia de classes: Classe Base: FormaGeomtrica Classes Derivadas: Quadrado, Tringulo, Crculo Agora suponha que devemos possuir uma lista de formas, e desenh-las de maneira genrica (sem selecion-las por tipo) na tela. Obviamente, a funo que desenha um crculo, no igual a que desenha um Tringulo ou um Quadrado. Dessa forma, a existncia de uma funo desenha() na classe FormaGeomtrica no serviria para todos os objetos. Porm, a funo que desenha a lista deve ser genrica, de modo que no podemos selecionar separadamente a funo desenha() em cada uma das classes. E agora? O polimorsmo tem como objetivo exatamente a resoluo desse problema. Percebemos, claro, que poderamos solucionar essa situao utilizando switchs, no fosse a restrio genericamente citada acima. Porm, existe uma soluo muito mais elegante e limpa. Ponteiros polimorfos possuem a caracterstica de, sendo da classe base, poderem apontar para uma funo da classe base e escolher dinamicamente a funo adequada na classe derivada a ser executada. Os trechos de cdigo: 5.1, 5.2, 5.3 ajudam na compreenso. Se testarmos o programa acima, veremos que o ponteiro sabe para que tipo de objeto est apontando. Como o polimorsmo implementado um

46

Funes Virtuais e Polimorsmo

Cdigo 5.1: Classe FormaGeometrica


#include <i o s t r e a m > c l a s s FormaGeometrica { public : FormaGeometrica ( ) ; ~ FormaGeometrica ( ) ; v i r t u a l void desenha ( ) const ; i n t getX ( ) const ; i n t getY ( ) const ; i n t getZ ( ) const ; void setX ( const i n t z3 ) ; void setY ( const i n t z2 ) ; void s e t Z ( const i n t z1 ) ; private : int x , y , z ;

};

Cdigo 5.2: Classe Triangulo


#include " formaGeometrica . h " c l a s s T r i a n g u l o : public FormaGeometrica { public : T r i a n g u l o ( ) ; ~Triangulo () ; v i r t u a l void desenha ( ) const ; private : i n t lado1 , lado2 , l a d o 3 ;

};

Cdigo 5.3: Classe Quadrado


#include " formaGeometrica . h " c l a s s Quadrado : public FormaGeometrica { public : Quadrado ( ) ; ~Quadrado ( ) ; v i r t u a l void desenha ( ) const ; private : i n t l a d o ; };

5.2 Ponteiros Polimorfos como Parmetros assunto complexo e no pertence ao escopo deste curso, de modo que veremos apenas o que deve ser feito para que ele acontea. Podemos observar que na classe base a funo desenha() foi declarada como virtual. As declaraes seguintes (nas classes derivadas) so apenas uma questo de clareza, visto que uma vez declarada virtual, uma funo permanece virtual por toda a hierarquia de classes. Essa declarao o que garante que o ponteiro escolher dinamicamente qual a funo adequada. vlido observar que essa escolha dinmica exclusividade de ponteiros. Se a funo for chamada atravs de um objeto, a vinculao ser esttica, e a funo ser a pertencente classe do objeto em questo.

47

5.1.1

Teste de Aprendizado

Construa trs classes: uma classe base Empregado e duas classes derivadas Contratado e Comissionado. As classes devem possuir a funo imprimeSalario, que no caso do Contratado, deve ser um valor xo, e no caso do Comissionado deve ser um valor relativo quantidade de produtos vendidos. Implemente um programa que escreva o salrio de cada comissionado a partir de um ponteiro polimorfo da classe base. Obs: No perca tempo com o clculo do salrio, imprima um valor constante (Ex: O contratado recebe 600 reais/ O comissionado recebe 25% do lucro gerado). O objetivo do exerccio testar o entendimento do polimorsmo, no da matemtica. Obs2: No declare as funes como virtual no .cpp, apenas no .h.

5.2

Ponteiros Polimorfos como Parmetros

s vezes, desejamos criar uma funo genrica para tratar inmeras classes de uma mesma hierarquia. Um caso em que essa tcnica til, por exemplo, caso desejemos derivar nossa classe de uma classe cujo cdigo no nos acessvel. Isso possvel usando um parmetro polimorfo. Como exemplo, utilizemos as classes j declaradas, FormaGeometrica, Triangulo e Quadrado. Caso desejemos, por exemplo, criar uma funo Move, para os dois tipos de forma geomtrica, necessrio passar como parmetro algo genrico, ou seja, um ponteiro para a classe base. Como descrito no cdigo 5.4.

5.2.1

Teste de Aprendizado

1. Essa no seria a melhor maneira de resolver o problema, apenas um modo de reforar o conceito. Qual seria a melhor maneira? 2. Reescreva o programa de modo a utilizar a melhor soluo para o problema, discutida no exerccio 1.

48

Funes Virtuais e Polimorsmo

Cdigo 5.4: Exemplo de Polimorsmo


#include <i o s t r e a m > #include " P o l i m o r f i s m o . h " // Funcao Move void Move ( FormaGeometrica forma , i n t x , i n t y , i n t z ) { forma>setX ( x ) ; forma>setY ( y ) ; forma>s e t Z ( z ) ; } // Programa p r i n c i p a l i n t main ( ) { Quadrado quad ; Triangulo t r i ; FormaGeometrica form ; form = &quad ; Move ( form , 1 0 , 10 , 0 ) ; form = &t r i ; Move ( form , 2 0 , 2 0 , 2 0 ) ; s t d : : c o u t << " x do quadrado : s t d : : c o u t << " y do quadrado : s t d : : c o u t << " z do quadrado : s t d : : c o u t << " x do t r i a n g u l o s t d : : c o u t << " y do t r i a n g u l o s t d : : c o u t << " z do t r i a n g u l o return 0 ;

" << quad . getX ( ) " << quad . getY ( ) " << quad . getZ ( ) : " << t r i . getX ( ) : " << t r i . getY ( ) : " << t r i . getZ ( )

<< << << << << <<

std std std std std std

:: :: :: :: :: ::

endl ; endl ; endl ; endl ; endl ; endl ;

5.3 Classes Base Abstratas e Classes Concretas

49

5.3

Classes Base Abstratas e Classes Concretas

Quando pensamos em uma classe como um tipo de dados, imediatamente se tem a idia de que variveis daquele tipo sero criadas. E isso, obviamente, o que deveria acontecer, pois uma classe sem objetos no tem utilidade nenhuma, no ? No . Tomemos como exemplo a classe FormaGeometrica, citada anteriormente. Existe realmente um objeto FormaGeometrica? Ou cada objeto de FormaGeometrica se encaixaria dentro de uma classe derivada mais especca, mais concreta? Classes como a FormaGeometrica esto implementadas na linguagem C++, e so conhecidas como classes abstratas. Classes abstratas no constituem um tipo, de forma que no podem ser declarados objetos dessas classes. Podem, entretanto, ser declarados ponteiros para essas classes, o que praticamente explicita seu uso extensivo para polimorsmo, como agrupador das classes derivadas. Para informar ao compilador que uma classe abstrata, basta que se declare uma de suas funes como sendo uma funo virtual pura. Para isso, adiciona-se um inicializador = 0 na declarao da funo. Note a sintaxe no exemplo de cdigo 5.5 Cdigo 5.5: Classe Abstrata
c l a s s FormaGeometrica { public : FormaGeometrica ( ) ; ~ FormaGeometrica ( ) ; v i r t u a l void desenha ( ) const = 0 ; };

5.3.1

Teste de Aprendizado

1. Qual a vantagem de usar uma classe abstrata? 2. Modique a classe Empregado gerada anteriormente para ser uma classe abstrata.

Captulo 6

Templates e Excees
Templates um dos recursos mais poderosos da linguagem C++. E ser este o foco do estudo nesta parte do captulo. O conceito consiste, basicamente, em escrever um modelo de cdigo a partir do qual o compilador ser capaz de produzir uma srie de cdigo automaticamente. Existem dois tipos de templates diferentes: de funo e de classe.

6.1

Templates para Funes

Atravs de templates de funo, possvel especicar, com apenas um segmento de cdigo, uma srie de funes correlacionadas, que na verdade so sobrecarregadas. Na verdade, o template um molde para funes, contudo no se especica um tipo para a mesma (o tipo deixado genericamente). Quando ela chamada, o compilador analisa o tipo chamado e escreve pelo usurio a funo como deveria ser com o tipo especicado. Parece mgica, mas to simples quanto parece. Imagine uma funo que soma dois inteiros. Facilmente implementvel, com certeza. Agora, preciso somar dois oats: sem dvida uma funo semelhante. Assim, Somar dois doubles no deve apresentar maiores diculdades. E somar dois vetores (tipo criado pelo usurio)... Todas essas so tarefas semelhantes, que exigem a escrita de cdigo duplicado. Utilizando templates possvel escrever uma nica funo que some o que voc quiser. Deve ser observado que, para tipos criados pelo usurio, os operadores utilizados devem ter sido corretamente sobrecarregados.

6.1.1

Sintaxe

A sintaxe para uso de funes template trivial. Comece pela palavrachave template seguido da lista de tipos denidos pelo usurio entre os sinais de menor e maior. No cdigo 6.1, a declarao normal da funo, referindo ao tipo genrico especicado quando necessrio.

52

Templates e Excees

6.1.2

Exemplo
Cdigo 6.1: Template de Funes

#include <i o s t r e a m > using namespace s t d ; template <c l a s s T> T somaDois (T x , T y ) { return ( x + y ) ; } i n t main ( ) { c o u t << somaDois ( 3 , 5 ) << e n d l ; c o u t << somaDois ( 3 . 5 5 5 , 5 . 3 3 3 ) << e n d l ; c o u t << somaDois ( a , b ) << e n d l ; return 0 ; }

Exerccio: 1. Escreva um template de funo que ordene quaisquer tipos. Teste com os tipos primitivos int e oat. 2. Agora crie uma classe Funcionario bem simples que tenha um atributo int idade. E teste sua funo template para essa classe ordenando os funcionrios por idade (para funcionar provvel que necessite sobrecarregar alguns operadores).

6.2

Template de Classe

Um template de classe extremamente semelhante a um template de funo, contudo, agora as classes que so genricas. Este recurso muito bem utilizado em classes continer, como uma pilha (LIFO Last In First Out) ou uma la (FIFO First In First Out). Dessa forma, possvel especicar uma classe continer genrica que pode vir a armazenar inteiros, strings, ou mesmo funcionrios.

6.2.1

Sintaxe

A sintaxe bem semelhante vista anteriormente, a palavra-chave template seguida dos tipos genricos entre os sinais de menor e maior vem antes da declarao da classe. Observe o cdigo 6.2.

6.3 Introduo a Excees

53

6.2.2

Exemplo

6.3

Introduo a Excees

Agora introduziremos os fundamentos do tratamento de excees. Como o nome diz, excees so situaes fora do comum nos programas. Em geral erros que o programa no consegue resolver, terminando ento a execuo. Por exemplo, uma diviso por zero um erro que fatalmente ir derrubar o programa. Agora, imagine que voc se depara com uma diviso, cujo denominador eventualmente possa vir a ser zero. Ao invs de deixar esta possibilidade de erro em aberto, seria muito melhor detectar o erro e trat-lo adequadamente ou, ao menos, indic-lo para que outros resolvam. Pois disso que se trata basicamente o captulo: detectar excees e tomar uma providncia em relao a elas. Deve car claro que o tratamento de excees especco para quando o erro no pode ser tratado localmente e deve ser reportado. Caso contrrio, o erro deve ser tratado localmente sem a necessidade de um tratamento de excees. Portanto, ao nal do estudo deve ser entendido que o tratamento de excees de C++ foi projetado para hierarquizar o tratamento de erros e tratar casos em que a funo que descobre um erro est impossibilitada de trat-lo (ou no deseja faz-lo para no misturar cdigo de tratamento de erros dentro da sua lgica). Contudo no curso apresentaremos erros que poderiam ser tratados localmente por motivos didticos.

6.4

Try, Throw e Catch

Os trs comandos bsicos para realizar o tratamento de excees so try, throw e catch. throw este o comando que lana uma exceo. Imagine estar desenvolvendo uma parte de um sistema, e percebe a possibilidade de um erro, mas no pode trat-lo neste ponto. Ao invs de ignorar, o correto , ao menos, avisar um possvel usurio quando der um erro. Para isso que serve o comando throw, lanar um erro para que seja tratado em outro ponto, mesmo sem garantia de que este erro ser efetivamente tratado em outro lugar. try este o comando que inicia um bloco que pode gerar erro. Todo comando contido neste bloco vericado antes da execuo para assegurar que um erro no cause o trmino do programa. Caso exista algum comando neste bloco que lana uma exceo atravs do comando throw (visto acima), isto ser detectado para, em seguida, poder ser tratado, o que neste caso signica passar o uxo de execuo para o bloco do comando catch. catch este o comando que realmente trata excees. Caso algum erro tenha sido encontrado no bloco try correspondente, ser procurado um bloco catch subseqente que possa tratar a exceo encontrada. dentro desse bloco que sero tomadas as devidas providncias em relao ao erro.

54

Templates e Excees

Cdigo 6.2: Template de Classe


template < c l a s s T > c l a s s Camaleao { public : Camaleao ( ) ; Camaleao (T n o v a V a r i a v e l ) ; ~Camaleao ( ) ; T getVariavel () ; void s e t V a r i a v e l (T n o v a V a r i a v e l ) ; private : T variavel ; }; template <c l a s s T> Camaleao<T> : : Camaleao ( ) {} template <c l a s s T> Camaleao<T> : : Camaleao (T n o v a V a r i a v e l ) { v a r i a v e l = n o v a V a r i a v e l ; } template <c l a s s T> Camaleao<T> : : ~ Camaleao ( ) { } template <c l a s s T> T Camaleao<T> : : g e t V a r i a v e l ( ) { return v a r i a v e l ; } template <c l a s s T> void Camaleao<T> : : s e t V a r i a v e l (T n o v a V a r i a v e l ) { v a r i a v e l = novaVariavel ; } // t r e c h o de camaleao . cpp #include " Camaleao . h " #include <i o s t r e a m > using namespace s t d ; i n t main ( ) { Camaleao<int> m e u I n t e i r o ( 7 ) ; c o u t << " meu i n t e i r o : " << m e u I n t e i r o . g e t V a r i a v e l ( ) << e n d l ; return 0 ; }

6.4 Try, Throw e Catch Deve car claro que o tratamento de excees no faz milagre. bastante difcil solucionar problemas da forma desejada atravs deste mecanismo. Na verdade, ele muito til para terminar o programa elegantemente quando ocorre uma emergncia. bastante desagradvel para o usurio ver o programa cair, ou fazer coisas que ele realmente no deveria estar fazendo. Ento, se utiliza bastante tratamento de excees para avisar o que aconteceu (Olha, estou fechando porque ocorreu um estouro de memria... desculpe!), e em seguida realizar alguns procedimentos antes de terminar o programa, como, por exemplo, liberar a memria alocada. Sem dvida alguma, bem melhor fazer isto, em vez de deixar o programa executando de modo imprevisvel.

55

6.4.1

Exemplo

Os cdigos 6.3 e 6.4 exemplicam o uso. Cdigo 6.3: Excees


#include <i o s t r e a m > using namespace s t d ; // c l a s s e para o t r a t a m e n t o de uma e x c e c a o de d i v i s a o por z e r o class DivideByZeroException { public : D i v i d e B y Z e r o E x c e p t i o n ( ) : message ( " voce t e n t o u d i v i d i r por zero " ) { } const char what ( ) const { return message ; } private : const char message ; } ; // f u n c a o que l a n c a a e x c e c a o double q u o t i e n t ( i n t numerador , i n t denominador ) { // momento em que a e x c e eh l a n c a d a ! ! ! i f ( denominador == 0 ) throw D i v i d e B y Z e r o E x c e p t i o n ( ) ; return ( double ) numerador / denominador ; }

Note que o comando throw chama o construtor da classe DivideByZeroException criando, dessa forma, um objeto dessa classe. Este objeto ser lanado pelo comando throw, e quando o erro for detectado pelo bloco try o comando catch capturar este objeto e ir tratar adequadamente a exceo. O comando throw pode lanar objetos, ou mesmo tipos primitivos. E ser este tipo que denir qual bloco catch tratar da exceo em questo.

56

Templates e Excees

Cdigo 6.4: Excees funcionando


#include " e x c e c o e s . cpp " // programa t e s t e para t r a t a r uma e x c e c a o i n t main ( ) { int x , y ; double r e s u l t a d o ; c o u t << " D i g i t e d o i s numeros para serem d i v i d i d o s (CTRL + Z para a c a b a r ) : " ; while ( c i n >> x >> y ) { try { resultado = quotient (x , y) ; c o u t << "O q u o c i e n t e eh " << r e s u l t a d o << e n d l ; } catch ( D i v i d e B y Z e r o E x c e p t i o n ex ) { c o u t << " Ocorreu uma e x c e c a o : " << ex . what ( ) ; c o u t << e n d l ; } c o u t << " D i g i t e d o i s numeros para serem d i v i d i d o s (CTRL + Z para a c a b a r ) : " ; } return 0 ; }

catch ( i n t x ) { ... } c a t c h ( double y ) { ... }

Estes so blocos sintaticamente corretos, contudo, costuma-se escolher objetos, pois estes podem transportar alguma informao til para o tratamento da exceo. Caso se deseje capturar todas as excees, de qualquer tipo, utiliza-se reticncias entre os parnteses que sucedem o catch:
c a t c h ( . . . ) {}

Se nenhum bloco catch for capaz de capturar uma determinada exceo, o programa abortado. Caso contrrio, ele continua com a primeira instruo aps o ltimo bloco catch (logo, qualquer parte do bloco try que no tenha sido executada antes do lanamento da exceo ignorada). Cabe ainda ressaltar que os efeitos colaterais do polimorsmo valem aqui tambm. Caso o tipo de alguma exceo seja uma classe me, qualquer classe derivada desta ser capturada pelo respectivo bloco catch da classe me.

6.4 Try, Throw e Catch

57

6.4.2

Exerccio

1) Crie uma situao semelhante ao exemplo em que ocorra um erro por estouro (overow). Utilize variveis que estouram mais comumente, como short int ou byte.

Captulo 7

Processamento de Arquivos
As variveis de programas so temporrias, isto , sero armazenadas na memria do seu computador e, assim que seu programa nalizar, elas sero perdidas. Contudo, existem diversas situaes em que pode ser bem interessante guardar algumas informaes para a prxima vez que o programa executar (imagine no poder salvar seu jogo no meio e continu-lo depois), ou mesmo para utilizar estas informaes de alguma outra forma. Para fazer isto utilizamos arquivos, que so armazenados em dispositivos secundrios de armazenamento, como o disco rgido do seu computador, e, desta forma, torna-se bem mais difcil perder os dados. E neste captulo examinaremos a manipulao de arquivos em C++.

7.0.3

Arquivos em C++

Para realizar o processamento de arquivos em C++, devem ser includos os arquivos de cabealho <iostream> e <fstream>. O cabealho <iostream> inclui objetos e estruturas que so utilizados na entrada e sada padro de dados, mas que tambm so vlidos para manipular arquivos. J o cabealho <fstream> inclui as denies das classes ifstream (para realizar a entrada de um arquivo), ofstream (para realizar a sada para um arquivo) e fstream (para entrada e sada de um arquivo). Todas essas classes herdam das classes denidas em <iostream> de acordo com o esquema abaixo.

60

Processamento de Arquivos

7.0.4

Arquivo Seqencial

Como o nome j diz, um arquivo seqencial simplesmente um arquivo de acesso seqencial, isto , para acessar um dado no m do arquivo preciso percorrer todo o arquivo antes. Para criar um arquivo seqencial, preciso criar um objeto ofstream chamando o seu construtor com dois parmetros: o nome do arquivo e o modo como este aquivo ser aberto.

7.0.5

Modos de Abertura

ios::app Grava toda sada no m do arquivo. ios::ate Abre um arquivo para sada e avana at o m do arquivo. ios::in Abre um arquivo para entrada. ios::out Abre um arquivo para sada. ios::trunc Abre um novo arquivo, eliminando antigo se este j existir. ios::binary Abre um arquivo para entrada ou sada binria. Em seguida, a entrada para o arquivo idntica a entrada padro de dados. Observe o cdigo 7.1 de exemplo, que realiza um cadastro de lmes com os seguintes campos: Cdigo, Nome e Nota. Agora, quando criamos um arquivo seqencial, bem provvel que queiramos abri-lo em uma prxima oportunidade. Para realizar isso, o procedimento anlogo ao de criao de um arquivo seqencial. Primeiro, deve-se criar um objeto ifstream chamando o seu construtor com os seus parmetros: nome do arquivo

61

Cdigo 7.1: Escrevendo em Arquivos


#include <c s t d l i b > #include <i o s t r e a m > #include <o f s t r e a m > i n t main ( ) { // c o n s t r u t o r de o f s t r e a m a b r e o a r q u i v o o f s t r e a m f i l e ( " Movie . dat " , i o s : : out ) ; if ( ! file ) // o p e r a d o r ! s o b r e c a r r e g a d o { c e r r << " Arquivo nao e n c o n t r a d o . . . " << e n d l ; exit ( 1 ) ; } c o u t << " D i g i t e o c o d i g o do f i l m e , o nome e a nota . \ n " << e n d l "; i n t cod ; c h a r nome [ 50 ] ; i n t nota ; w h i l e ( c i n >> cod >> nome >> nota ) { f i l e << cod << << nome << << nota << \ n ; c o u t << " ? " ; } } return 0;

62

Processamento de Arquivos e modo de abertura do arquivo. Em seguida, basta ler os valores do arquivo do mesmo modo que se faz com a entrada de dados padro, trocando cin pelo objeto criado. objeto criado variveis que armazenaro os valores lidos do arquivo Exerccio: Primeiramente, utilize o cdigo dado para criar um arquivo seqencial de lmes. Em seguida, faa um programa que leia deste arquivo seqencial e escreva na tela apenas o nome dos lmes com nota acima de 6.

7.1

Arquivo Aleatrio

Um arquivo seqencial resolve muitas vezes o nosso problema, contudo, apresenta um grave problema de tempo: seu mecanismo pouco eciente. Imagine-se na frente de um caixa eletrnico, querendo realizar uma retirada de dinheiro, e o sistema vericando seqencialmente num arquivo de todas as contas do banco, se sua conta possui o saldo suciente. bem provvel que voc desista de retirar o dinheiro. Para resolver este tipo de problema, utilizamse arquivos de acesso aleatrio. A idia bsica consiste em saber de antemo onde se encontra a informao desejada, e busc-la diretamente no local apropriado. Isto pode ser feito utilizando-se estruturas de tamanho xo. Dessa forma, possvel calcular de forma rpida que o registro de chave 5 iniciar na posio 5 vezes o tamanho da estrutura (considerando iniciar na chave 0). Implementar este tipo de arquivo em C++ tambm no impe muitas diculdades. Os prprios objetos ostream e istream possuem mtodos write e read, respectivamente, nos quais possvel especicar um oset para ir direto para determinado ponto do arquivo. Entretanto, foge um pouco do escopo do curso detalhar todas as tcnicas e mtodos de manipulao de arquivos.

Captulo 8

Standard Template Library


A STL uma parte extremamente importante da biblioteca padro do C++. Na prtica, programadores passam a maior parte do tempo programando somente com a STL, usando pouco as outras facilidades oferecidas pela biblioteca padro completa. Na STL, esto declaradas estruturas de dados muito versteis e diversos algoritmos teis, de forma a facilitar a programao. O uso dessa biblioteca evita que seja necessrio, por exemplo, implementar e depurar listas encadeadas.Alm disso, houve uma grande preocupao com o desempenho das classes e funes, de modo que dicilmente uma implementao prpria pode oferecer vantagens consistentes de performance, sem falar nas facilidades que a STL prov.

8.1

Conhecendo a STL

Pode-se dividir a STL em trs tipos de componentes: containers, iteradores e algoritmos. Containers so estruturas que contm objetos de outras classes, como por exemplo listas, pilhas, mapas, rvores, vetores, etc.. Para que se entenda como funcionam essas classes, extremamente necessrio que se esteja familiarizado com o conceito de templates. Iteradores so, de certa forma, ponteiros. Na realidade eles no so ponteiros, porque eles no apontam simplesmente para uma palavra na memria, e existem operaes que podem ser feitas sobre ponteiros que no podem ser feitas sobre iteradores, como aritmtica de ponteiros, por exemplo. Entretanto, mesmo com estas diferenas, iteradores tm um uso semelhante a ponteiros: eles fornecem uma interface de acesso a elementos internos de containers. Algoritmos so, como o nome diz, algoritmos prontos que a STL fornece. Eles permitem, por exemplo, ordenar containers, obter elementos mximos de conjuntos, e outras coisas interessantes. Com algoritmos STL, pode-se acumular todos os valores de um vetor, essencialmente resolvendo um problema de integrao numrica em uma linha de cdigo. Pode-se tambm aplicar uma funo arbitrria a cada elemento de um con-

64 Continer Vector List Deque String Set Multiset Map Multimap Stack Queue Priority Queue Categoria Sequencial Sequencial Sequencial Sequencial Associativo Associativo Associativo Associativo Adaptador Adaptador Adaptador

Standard Template Library Descrio Inseres/Retiradas rpidas, apenas no m. Acesso direto a qualquer elemento. Inseres/Retiradas rpidas. Inseres/Retiradas rpidas, no incio e no m. Acesso direto a qualquer elemento. Vetor de caracters tpico. Pesquisa rpida. Duplicatas no permitidas. Pesquisa rpida. Duplicatas permitidas. Mapeamento um para um. Pesquisa rpida usando chaves. Duplicatas no permitidas. Mapeamento um para um. Pesquisa rpida usando chaves. Duplicatas permitidas. Primeiro a entrar, ltimo a sair (FILO) Primeiro a entrar, primeiro a sair (FIFO) Mais alta prioridade, primeiro a sair

junto, tambm com uma linha de cdigo. Uma caracterstica interessante dos algoritmos STL que eles so genricos. Por exemplo, o algoritmo sort, que implementa ordenao de listas, verstil o suciente para ordenar quase qualquer vetor de elementos arbitrrios. Este quase deve ser levado em considerao. Para que o sort funcione, a classe dos elementos internos ao vetor deve prover um operador < bem denido. Alm disso, no existem restries, o que implica que o sort pode ordenar elementos de qualquer classe, desde que o predicado de ordem seja especicado para esta classe!

8.2

Continers

Continers so inseridos, de acordo com suas caractersticas, em trs categorias: seqenciais, associativos e adaptadores de containers. A justicativa para essa separao provm do uso de cada um dos grupos de containers. Abaixo, uma lista com os principais containers, alocados em seus respectivos grupos, seguidos de uma descrio breve de sua utilizao.

8.3

Funes Membro Comuns

Existem alguns membros bsicos que so comuns a todos os containers da STL. Estes membros permitem fazer checagens simples, como obter o nmero de elementos em um container, testar se este est vazio, e etc.. A lista abaixo mostra os mtodos comuns mais importantes:

8.4 Funes Membro Especcas Continer Vector List Deque String Set e Multiset Map e Multimap Stack Queue e Priority Queue #include <vector> <list> <deque> <string> <set> <map> <stack> <queue>

65

::size() retorna um size type (convertvel para unsigned int) que representa o nmero de elementos do container ::empty() retorna um valor booleano dizendo se o container est vazio ::begin() retorna um iterador para o incio do container ::end() retorna um iterador para um elemento alm do nal do container ::rbegin() retorna um iterador reverso para o incio reverso do container (ou seja, o ltimo elemento do container) ::rend() retorna um iterador reverso para um elemento antes do incio do container Alm destes, os operadores tpicos de comparao != e == tambm esto implementados para continers, bem como construtores de cpia.

8.4

Funes Membro Especcas

Obviamente, existem algumas funcionalidades especcas de cada container, que precisam ser implementadas com mtodos no-genricos. Um exemplo disso a funo clear(), que apaga todos os valores de um container, e s funomembro dos containers seqenciais ou associativos (tambm conhecidos como containers de primeira classe).

8.4.1

Arquivos de Cabealho

Para utilizar os containers da STL, necessro incluir os arquivos de cabealho daquele container no programa. Abaixo, os arquivos necessrios para cada um dos containers discutido acima.

66

Standard Template Library

8.5

Vector

Um dos containers mais simples e utilizados da biblioteca de templates do C++ a classe Vector. Apesar de ser fundamentalmente diferente de arrays em C, o vector serve para muitos dos mesmos ns. Uma das principais vantagens de se utilizar vector sobre arrays pr-determinados que um vector no tem limite de tamanho (estritamente falando, um vector tem limite de tamanho mximo, mas este valor limitado por uma constante normalmente desprezivelmente grande, denida pela quantidade de memria disponvel na mquina. Este valor pode ser obtido com o mtodo ::max size()) A maioria dos conceitos relativos aos objetos Vector j bastante consolidada em programadores C, de modo que essa apostila mostrar, principalmente, a sintaxe e diferenas bsicas entre o uso de arrays e de vetores da STL. Comecemos com um exemplo bsico, que utiliza vectors apenas para armazenar e mostrar dados. A classe Aluno deve receber um nmero indenido de notas, armazenar em um vector, e imprimir um histrico escolar, a partir desses dados. Sua implementao est em 8.1, 8.2. Cdigo 8.1: Header da classe Aluno
// t r e c h o de a l u n o . h #include <v e c t o r > #include <i o s t r e a m > using s t d : : v e c t o r ; using s t d : : c o u t ; using s t d : : e n d l ; c l a s s Aluno { public : // C o n s t r u t o r Aluno ( ) ; // D e s t r u t o r ~Aluno ( ) ; // A d i c i o n a r nota void a d i c i o n a r N o t a ( i n t nota1 ) ; // Imprimir h i s t o r i c o void i m p r i m i r H i s t o r i c o ( ) ; private : v e c t o r <int> n o t a s ;

};

8.5.1

Teste de Aprendizado

Adicione na classe Aluno uma funo calculaMedia(). Troque o uso de push back() pelo simples notas[n] = nota1.

8.6 Como lidar com capacidade ilimitada, ou como o Vector no mgico Cdigo 8.2: Classe usando um vetor
// t r e c h o de a l u n o . cpp void Aluno : : a d i c i o n a r N o t a ( i n t nota1 ) { n o t a s . push_back ( nota1 ) ; } void Aluno : : i m p r i m i r H i s t o r i c o ( ) { f o r ( i n t i = 0 ; i < n o t a s . s i z e ( ) ; ++i ) c o u t << " Nota : " << n o t a s [ i ] << e n d l ; }

67

O que acontece? Obs.: Utilize vector::size() para pegar o nmero de notas j adicionadas, e, conseqentemente, o ndice da prxima varivel a ser adicionada.

8.6

Como lidar com capacidade ilimitada, ou como o Vector no mgico

primeira vista, vectors parecem desaar as leis bsicas da computao. Eles oferecem sempre o melhor de dois mundos. Temos garantias tericas de que vectors fornecem um tamanho ilimitado de elementos, sabemos que estes elementos esto contguos na memria, e sabemos que o acesso a qualquer elemento tem complexidade constante, bem como a insero no nal e a remoo do nal. No nem um pouco trivial desenvolver uma estrutura de dados com todas estas capacidades ao mesmo tempo. De fato, se levarmos estas denies ao p da letra, tal estrutura de dados impossvel. O que a STL esconde aqui que, na realidade, a insero no feita em tempo constante, mas sim em tempo constante amortizado. O funcionamento desta insero bastante simples: Quando inicializamos um vector, a STL reserva um espao de memria contguo de tamanho, digamos, n, para alguns poucos elementos, e mantm este espao disponvel para nosso vector. Conforme vamos populando ele com elementos, este espao vai sendo ocupado e, quando queremos inserir o n+1-simo elemento, faltaria espao contguo. Quando isto acontece, a STL procura outro local de memria contguo, desta vez com 2n espaos, e move todo o contedo do vector para l. Como o tamanho da nova rea de memria aumenta exponencialmente, a probabilidade de que, dado um push back() qualquer, este precise uma realocao, tende a 0 conforme fazemos mais e mais push backs(). Por isso que chamamos a complexidade de insero de constante amortizada. interessante notar que esta realocao de memria pode invalidar ponteiros sobre o container, e por isso que ponteiros sobre containers no devem ser

68

Standard Template Library usados. Este processo de alocao amortizada justica um mtodo bastante til da classe vector, que o mtodo reserve(). Este mtodo serve para que possamos determinar um tamanho para o espao de memria inicial que o vector deve ocupar. Por exemplo, se soubermos que nosso vector ir ocupar aproximadamente 100 elementos, podemos fazer uma chamada a reserve(100), que ir reservar um espao de memria contgua de 100 elementos para nosso vector. Se nos garantirmos que a insero ir s at o centsimo elemento, a sim esta ter complexidade constante. Note, porm, que o mtodo reserve() no popula o vector com nenhum elemento, de forma que esta chamada no altera o resultado de size() e empty()! O exemplo 8.3 mostra outro detalhe que deve ser observado quando se trata de containers de tamanho arbitrrio: apesar de que podemos, em princpio, inserir quantos elementos quisermos dentro de um vector, s podemos acessar aqueles que j foram inseridos. Isto funciona da mesma forma que arrays em C tradicional, ou seja, se um array tem 5 elementos, o acesso ao elemento 6 (inclusive ao 5) gerar comportamento indenido. importante observar que isto sumariza a diferena entre innitos elementos e nmero arbitrrio de elementos. Um vector deve ser pensado exatamente como um array C que pode ter seu tamanho modicado em tempo de execuo. Cdigo 8.3: Demonstrao de Vetores
#include <i o s t r e a m > #include <v e c t o r > using s t d : : v e c t o r ; using s t d : : c i n ; i n t main ( i n t argc , char argv ) { v e c t o r <int> numeros ; f o r ( i n t i = 0 ; i < 4 ; ++i ) { numeros . push_back ( i ) ; } v e c t o r <int> numeros2 ; numeros2 . r e s e r v e ( 3 ) ; f o r ( i n t i = 0 ; i < 4 ; ++i ) { numeros2 [ i ] = i ; } numeros2 [ 5 0 0 0 ] = 5 ; } return 0 ;

8.7 List

69

8.6.1

Observao

Ao testar no Dev-C++, que usa o gcc 3.4.2, o cdigo acima s gerou erro ao acessar o elemento 5000. Porm, ao executar compilando com o g++ no Linux, um acesso ao item de ndice 4 suciente para gerar um erro de execuo. Dessa forma, esse tipo de acesso gera comportamento indenido, que suciente para que seu uso no seja recomendado.

8.7

List

Alm dos vetores, uma estrutura muito utilizada em programao a lista encadeada. Observe que o nome list no quer dizer que uma lista simplesmente encadeada. De fato, este template STL uma lista duplamente encadeada (para uma lista especicamente simplesmente encadeada, use a estrutura no-padro slist). Listas so recomendadas para tarefas onde necessrio muita performance para inseres no incio ou no nal, e consulta no incio ou no nal. De fato, estas operaes tm complexidade O(1). Operaes arbitrrias no meio da lista, entretanto, como insero, remoo e consulta, tm complexidade O(n). Uma vantagem das listas para os vectors, por outro lado, que o tempo constante de listas de fato constante, e no constante amortizado, pois a estrutura de dados subjacente a uma lista um encadeamento de ponteiros. Quando comeamos a trabalhar com listas, a utilizao de iteradores passa a ser necessria. Enquanto, para varrer um vector, possvel utilizar o operador [], da mesma forma que se varre arrays em C, quando queremos varrer o contedo de uma lista, somos obrigados a utilizar iteradores. O exemplo 8.4 mostra o conceito de iteradores em uma varredura linear simples de uma lista: Inicialmente, a sintaxe destes comandos pode parecer complicada. Lembrese, entretanto, de exatamente como funciona o comando for: O primeiro comando passado dentro dos parnteses executado exatamente uma vez antes do lao ser processado. Neste caso, estamos declarando uma varivel, chamada pos, de tipo list<int>::iterator, e setando esta varivel ao valor especial foo.begin(), que aponta para o primeiro elemento da lista. O segundo comando no parntese do for um teste que ser executado antes de cada passada do for, inclusive da primeira, e que se falhar, o for ser terminado. Aqui, estamos testando se o valor do iterador igual ao valor xo foo.end(), que aponta para um elemento alm do ltimo elemento da lista, de forma que este for varre a lista inteira. Finalmente, o ltimo comando no parntese do for chama o operador++ sobre o iterador, que est denido internamente e signica que o iterador deve receber o valor de seu sucessor imediato. Note que no existe o operador para iteradores. Para varrer uma lista em ordem reversa, reverse iterators devem ser utilizados. Veremos iteradores em mais detalhes na seo apropriada.

70

Standard Template Library

Cdigo 8.4: Iterando sobre uma lista


#include < l i s t > #include <i o s t r e a m > using s t d : : l i s t ; using s t d : : c o u t ; using s t d : : c i n ; i n t main ( ) { l i s t <int> f o o ; f o r ( l i s t <int > : : i t e r a t o r pos = f o o . b e g i n ( ) ; // n o t e que e s t e for pos != f o o . end ( ) ; ++pos ) { // e s t a s e p a r a d o em duas l i n h a s ! c o u t << ( pos ) << e n d l ; } } return 0 ;

8.8

Outros Containers

A seguir, faremos um breve resumo sobre os outros containers mais especcos da STL, e ainda sobre um tipo de dados especco, o pair. Deque Um deque funcionalmente semelhante a um vector. Observe que deque signica double-ended queue, mas isto no quer dizer que um deque seja implementado como uma lista encadeada. De fato, deques so implementados como espaos contguos na memria, de forma que inseres no meio levam tempo linear, mas acesso ao meio leva tempo constante. A principal diferena entre um vector e um deque que o deque possui os mtodos push front e pop front, que fazem papis anlogos ao push back e pop back, mas atuam na frente da lista. Da mesma forma que os vectors, estes mtodos so implementados com uma tcnica de tempo constante amortizado. String Strings implementam um vetor de caracteres, para possibilitar formas de entrada e sada mais alto-nvel do que null-terminated chars em C. Uma string possui complexidades semelhantes ao vector, de forma que tambm mantida como rea contgua de memria, mas o tipo de seus elementos char. Existe uma variante interessante de strings, que so as wstrings, que permitem trabalhar com chars de 16 bits. Normalmente isto acontece quando a interface com algum outro programa exige a utilizao de wide characters, ou seja, caracteres de 16 bits Set Um set , primeiramente, um container associativo. Isto signica que

8.9 Iteradores acesso randmico aos seus elementos no possvel. Entretanto, insero e remoo so executados em tempo O(log n). importante notar que para containers associativos, no possvel especicar o local onde o elemento ser adicionado, pois estes containers so implementados como rvores binrias balanceadas. Isto signica, adicionalmente, que os elementos so automaticamente guardados em ordem dentro do container. Observe que sets no permitem elementos duplicados. As funes nicas mais importantes de sets so insert, remove e nd, que inserem, removem, e localizam, respectivamente, um elemento no set. Multiset Muito semelhante ao set, a principal diferena entre eles que enquanto o set no permite colocao de elementos duplicados, um multiset permite. Map Um Map um container mais interessante, no sentido de que ele permite buscas do tipo chave-informao. Dessa forma, os elementos de um map so pares (std::pair) onde o primeiro elemento representa a chave de busca e o segundo o valor desejado. O map implementado tambm como uma rvore binria balanceada onde a ordenao feita com respeito s chaves. Dessa forma, possvel buscar um valor informando somente uma chave, com complexidade O(log n). No permitem duplicatas Multimap Multimaps so anlogos a multisets, no sentido de que so maps que permitem multiplicidade de elementos. Modicadores de containeres Stacks, Queues e Priority Queues no so containeres propriamente ditos, mas sim modicadores que atuam sobre containeres. Dessa forma, para especicarmos uma stack, por exemplo, primeiramente temos que denir um container normal (um vector, por exemplo), e depois construir a stack informando este vector. O que acontece que os mtodos acessados pelo objeto stack sero limitados a acesso e escrita ao primeiro elemento da pilha, como desejamos. O mesmo acontece para queues e priority queues. Pair Um Pair no um container. Ao invs disso, um pair uma estrutura de dados declarada pela STL que permite formarmos pares de tipos genricos. Para especicar um par inteiro-string, por exemplo, devemos fazer o seguinte: pair<int,string> foo. Os tipos de dados que podem formar pares so arbitrrios, e para acessarmos elementos do pair, existem os atributos .rst e .second.

71

8.9

Iteradores

A denio mais simples de iteradores razoavelmente bvia: iteradores iteram sobre o contedo de containers STL. Ou seja, iteradores so tipos de dados

72

Standard Template Library especcos que a STL implementa para possibilitar uma forma de acesso uniforme a elementos de qualquer tipo de container. A idia de interface uniforme muito importante aqui: se fssemos implementar um conjunto de containers diferentes a partir do zero, teramos, possivelmente, classes para listas, las, arrays, rvores binrias, e etc. Cada uma de nossas classes possuiria um mtodo diferente para acessar os elementos de dentro dos nossos containers, e o programador que fosse utilizlos teria que aprender cada um individualmente. O conceito de iteradores da STL resolve este conito, pois fornece uma nica forma de acesso uniforme a elementos de qualquer container. De certa forma, iteradores podem ser pensados como ponteiros para os elementos dos containers. Esta analogia deve ser tomada com muito cuidado, porm, pois existem coisas que podem ser feitas com ponteiros (apesar de que no so necessariamente recomendadas) que a STL no fornece. O exemplo mais clssico de uma aplicao assim aritmtica de ponteiros. J vimos, na seo que trata com listas, um exemplo de declarao de iteradores. Por isso, vamos mostrar um exemplo que utiliza iteradores sobre sets. Fica bastante claro que a estrutura do comando exatamente igual. A nica coisa que muda o tipo do iterador, que passa a ser do tipo set<int>::iterator. Cdigo 8.5: Demonstrando Iteradores
#include <s e t > #include <i o s t r e a m > using s t d : : l i s t ; using s t d : : c o u t ; using s t d : : c i n ; i n t main ( ) { s e t <int> f o o ; f o r ( l i s t <int > : : i t e r a t o r pos = f o o . b e g i n ( ) ; pos != f o o . end ( ) ; ++pos ) { c o u t << ( pos ) << e n d l ; } return 0 ; }

No exemplo 8.5, podemos notar alguns detalhes interessantes: em primeiro lugar, veja que a STL tambm segue, de certa forma, a analogia de que iteradores so ponteiros. Isto quer dizer que o operator* est denido para iteradores, e tem a mesma semntica que ele tem para ponteiros (ele retorna o valor apontado pelo ponteiro, ou, neste caso, pelo iterador). Alm disso, o operador++ tambm est denido, e ele serve para passar de um iterador para o prximo. Observe que, mesmo que sets no sejam lineares (lembre que so

8.9 Iteradores rvores binrias), o operador++ est bem denido. Finalmente, devemos observar o signicado dos comandos foo.begin() e foo.end() no exemplo acima. foo.begin() um iterador especco que aponta para o primeiro elemento do container trabalhado. O foo.end(), entretanto, no aponta para o ltimo elemento, e sim para um elemento alm do ltimo, seguindo de certa forma o conceito de que ndices em C variam de 0 at n-1, e no at n. Abaixo, mostramos os tipos principais de iteradores e suas utilidades: ::iterator Este tipo de iterador declara os iteradores mais comuns, que acessam elementos do container de forma seqencial da esquerda para a direita e que podem modicar os elementos apontados por eles ::reverse iterator Este tipo de iterador serve para varrer containers de forma reversa. Como o operador no est declarado para iteradores normais, no seria possvel iterar reversamente por um container sem este iterador especial. Observe que o operador++ est denido para este tipo de iterador da mesma forma que para ::iterators, mas para ::reverse iterators a varredura feita em ordem reversa! ::const iterator Semelhante ao ::iterator, mas com a restrio de que os elementos s podem ser acessados, e no modicados, utilizando este iterador. O uso deste iterador existe para podermos varrer containers que foram declarados const. Se tentarmos varrer um const vector<int> com um ::iteretor normal, encontraremos um erro de compilao.

73

::const reverse iterator Anlogo ao ::const iterator, mas se aplica a varredura reversa do container. Alm disso, cada container tem denido quatro iteradores especiais que servem para limitar laos for, entre outros usos. Iremos assumir, para os exemplos abaixo, a existncia de um vector<int> chamado foo, mas de fato estes iteradores esto denidos para qualquer container de qualquer tipo. foo.begin() este iterador do tipo ::iterator, e aponta para o primeiro elemento do container foo.end() iterador do tipo ::iterator, aponta para um elemento alm do ltimo elemento do container. Acesso a (*foo.end()) gera comportamento indenido. foo.rbegin() iterador do tipo ::reverse iterator, e aponta para o ltimo elemento do container. foo.rend() iterador do tipo ::reverse iterator que aponta para um elemento antes do primeiro elemento do container. Acesso a (*foo.rend()) gera comportamento indenido.

74

Standard Template Library

8.10

Algoritmos

Algoritmos tambm so uma parte bastante importante da Standard Template Library, j que implementam funes usadas muito freqentemente de forma eciente, facilitando muito o trabalho do programador. Um exemplo de algoritmo extremamente utilizado o de ordenao. Atravs desse algoritmo, ca fcil organizar um vetor (ou algum outro tipo de estrutura) usando qualquer tipo de relao de ordem. No exemplo 8.6, usaremos a funo sort para ordenar inteiros de forma crescente. Cdigo 8.6: STL Sort
#include <a l g o r i t h m > i n t main ( ) { v e c t o r <int> numeros ; f o r ( i n t i = 0 ; i < 6 ; ++i ) { numeros . push_back ( 5 0 10 i ) ; } c o u t << " Imp ri mir f o r a de ordem " << e n d l ; f o r ( i n t i = 0 ; i < 6 ; ++i ) { c o u t << numeros [ i ] << e n d l ; } s t d : : s o r t ( numeros . b e g i n ( ) , numeros . end ( ) ) ; c o u t << " Imp ri mir em ordem " << e n d l ; f o r ( i n t i = 0 ; i < 6 ; ++i ) { c o u t << numeros [ i ] << e n d l ; }

O algoritmo sort normalmente o algoritmo mais utilizado da STL e, portanto, merece alguma ateno especial. Internamente, o padro STL dita que o algoritmo utilizado para ordenamento o algoritmo introsort. Basicamente, o introsort ordena os elementos inicialmente utilizando um quicksort, e quando o nvel de recurso atinge um determinado limite, o algoritmo passa a executar um heapsort. A vantagem desta abordagem que a complexidade deste algoritmo garantidamente O(n log n). Outro detalhe importante que o algoritmo utilizado pela STL no um algoritmo de ordenamento estvel. Para um algoritmo estvel, utilize a alternativa stable sort. Abaixo, mostramos alguns algoritmos comuns que a STL implementa, com uma breve descrio de sua utilidade:

8.11 Usando a STL com Classes Prprias count(iterator rst, itertor last, const T &value) a funo count da STL recebe dois iteradores (semelhante ao sort) e mais um valor, e retorna o nmero de ocorrncias do valor value dentro do intervalo denido pelos dois iteradores

75

count if(iterator rst, iterator last, predicate pred) esta funo semelhante funo count, mas ela retorna o nmero de elementos dentro do intervalo denido pelos iteradores que satisfaz condio imposta pelo predicado pred, que deve ser uma funo booleana de um argumento do tipo do container dos iteradores utilizados max element(iterator rst, iterator last) retorna um iterador para o elemento de valor mximo dentro do intervalo denido por rst e last. Esta funo utiliza o comparador operator< do tipo do container para fazer as comparaes. min element(iterator rst, iterator last) semelhante a max element, mas retornando um iterador para o elemento mnimo max(const T &v1, const T &v2) dados dois valores de um mesmo tipo, como por exemplo max(1,2), retorna o valor mximo entre eles min(const T &v1, const T &v2) semelhante a max, mas retornando o valor mnimo

8.10.1

Usando o Algoritmo Sort para qualquer Ordenao

O algoritmo sort extremamente amplo, de modo que no se limita a ordenaes crescentes e decrescentes de inteiros. Nessa seo, veremos como utilizar funes prprias para ordenar estruturas atravs do sort. Para a demonstrao, ser usado o exemplo 8.7, que coloca os inteiros em ordem decrescente.

8.11

Usando a STL com Classes Prprias

A biblioteca padro do C++ tem como alicerce, em sua construo, a facilidade de extenso do seu uso. A partir disso, redundante dizer que as estruturas criadas podem ser utilizadas no s com os tipos bsicos da linguagem, mas tambm com classes criadas pelo programador. No exemplo 8.8, ordenaremos nossos Livros pelo ano de publicao (o maior antes), e em caso de igualdade, pelo ttulo do livro. Como se pode ver, basta declarar um operador do tipo <, que determina qual ser o critrio utilizado para denir se um objeto Livro ou no menor do que o outro. Depois, s usar a funo sort no vetor. O C++ ordena automaticamente o vetor utilizando o critrio denido pelo operador.

76

Standard Template Library

Cdigo 8.7: Usando Funo para Sort


#include <a l g o r i t h m > // f u n bool a n t e s ( i n t i , i n t j ) { return ( i > j ) ; } i n t main ( ) { v e c t o r <int> numeros ; f o r ( i n t i = 0 ; i < 6 ; ++i ) { numeros . push_back ( 1 0 i ) ; } c o u t << " Imp ri mir f o r a de ordem " << e n d l ; f o r ( i n t i = 0 ; i < 6 ; ++i ) { c o u t << numeros [ i ] << e n d l ; } s t d : : s o r t ( numeros . b e g i n ( ) , numeros . end ( ) , a n t e s ) ; c o u t << " Imp ri mir em ordem " << e n d l ; f o r ( i n t i = 0 ; i < 6 ; ++i ) { c o u t << numeros [ i ] << e n d l ; }

8.11 Usando a STL com Classes Prprias

77

Cdigo 8.8: STL com Classes Prprias


// t r e c h o de l i v r o . cpp // Operador < // ( v a i d e t e r m i n a r s e um L i v r o d e v e s e r menor na ordem ou nao ) bool L i v r o : : operator <(const L i v r o &book ) const { i f ( anoDePublicacao > book . getAno ( ) ) return true ; else { i f ( anoDePublicacao == book . getAno ( ) ) { i f ( t i t u l o < book . g e t T i t u l o ( ) ) return true ; } } return f a l s e ; }; / t r e c h o de main . cpp s t d : : s o r t ( l i v r o s . b e g i n ( ) , l i v r o s . end ( ) ) ; Ok ! os l i v r o s e s t a r a o o r d e n a d o s . /

78

Standard Template Library

8.11.1

Teste de Aprendizado

1. Crie o operador < na classe Aluno, e ordene os alunos pela mdia das notas 2. Crie uma classe Time, com nmero de vitrias, empates e derrotas, e organize n times em uma tabela de classicao usando: Maior nmero de pontos(vitrias*3 + empates), menos nmero de jogos, maior nmero de vitrias

8.12

ltimas Consideraes

A seguir, falamos sobre algumas consideraes importantes para escrever qualquer cdigo que utilize a STL. Estes itens so resumos de alguns captulos do livro Eective STL, Revised, de Scott Meyers. Seguir estes itens extremamente importante, para garantir que o programa execute de forma previsvel, j que devido complexidade da STL, se no tomarmos cuidado, o programa pode gerar comportamento indenido facilmente. O nmero entre colchetes a numerao de cada item no livro original. Sempre garanta que o objeto usado como tipo de um continer possui cpia bem denida[3]: Grande parte da losoa da STL baseada em cpias dos objetos que utilizamos. Por exemplo, se declaramos um vector<foo> bar, onde foo uma classe declarada por ns, quando inserimos um elemento neste vector (com um push back, por exemplo), a STL no insere exatamente o elemento que queramos, mas sim uma cpia deste. Para tipos de dados bsicos, como ints, oat, e etc., isto no importante. Para classes arbitrrias, por outro lado, isso pode ser um problema. Para estas classes, quando uma cpia feita, o compilador invoca mtodos especcos desta classe: o seu construtor de cpia ou seu operador de cpia, tambm conhecido como operator=. O detalhe aqui que se ns no implementarmos estes mtodos em nossa classe, o compilador assume uma implementao padro que pode ou no fazer o que queremos. Outro problema que se ns de fato implementarmos estes mtodos, devemos nos garantir de que eles esto corretos, seno as operaes feitas sobre continers da STL podem gerar comportamento inesperado e dicultar enormemente a depurao do programa. Em continers, use o membro empty(), ao invs de checar size() == 0[4]; O motivo para esta recomendao bastante simples. A operao empty() tem complexidade O(1), enquanto o teste de size() == 0 pode, em alguns casos, custar O(n). Para continers numerosos, isto se torna um problema.

8.12 ltimas Consideraes Lembre de chamar delete em iteradores antes de apagar o continer[7]; Suponha que tenhamos, por exemplo, um vector<int *> foo (um vector de ponteiros para inteiros). Alm disso, imagine que para popular este vector, utilizamos um lao for que aloca dinamicamente cada elemento com um operador new e coloca o valor no vector, como o exemplo:
v e c t o r <i n t > f o o ; f o r ( i n t i = 0 ; i < 5 ; ++i ) { i n t x = new i n t ; x = 3 ; f o o . push_back ( x ) ; }

79

Cada elemento do vector foo representa uma posio de memria dinmica alocada pelo Sistema Operacional. Quando apagarmos este vector (seja explicitamente ou seja porque seu escopo local est esgotado), o compilador no chama os deletes correspondentes a esses news! Isto signica que, ao declarar um cdigo como o acima, ns somos obrigados a fazer um outro lao semelhante, chamando os deletes correspondentes a cada iterador:
f o r ( i n t i = 0 ; i < 5 ; ++i ) { d e l e t e f o o [ i ] ; }

O exemplo que foi mostrado usa somente vector, mas este raciocnio vale para qualquer container STL. Prera vectors e strings a vetores dinamicamente alocados[13]; Ao utilizar vetores dinamicamente alocados, o programador deve se responsabilizar inteiramente pelo gerenciamento de memria dinmica feito. Para programas simples, isto pode parecer trivial, mas para um projeto mais complexo, gerenciamento de memria dinmica um problema bastante custoso. O programador deve garantir, em seu cdigo, que existe exatamente uma chamada delete para cada chamada new correspondente. Se esta chamada delete no estiver presente, haver com certeza vazamento de memria. Se, por outro lado, a chamada for feita duplicada, o comportamento, de acordo com a denio C++ , novamente, indenido. Com estes problemas, no parece fazer sentido adotar vetores dinmicos ao invs de vectors ou strings, que fazem todo este gerenciamento automaticamente e de forma segura. Garanta que operadores para continers associativos retornam falso para valores iguais[21]; bastante comum, como vimos em um exemplo acima, declarar operadores booleanos para poder implementar continers associativos (como sets, multisets, etc.) de classes arbitrrias. Dessa forma, implementaremos um operator< dentro de uma classe arbitrria qualquer. Em princpio, a STL permite que o comparador utilizado em um set no seja necessariamente o operator<, mas sim qualquer funo que

80

Standard Template Library informarmos, desde que esta satisfaa algumas condies. Assim, possvel, em princpio, estabelecer um set onde o comparador utilizado um operator<=, ao invs de um operator<. Isto uma pssima idia, e o motivo disto simplesmente que a STL espera que elementos iguais retornem falso a uma comparao, para poder, entre outras coisas, no inserir duplicatas em sets. Assim, se nosso comparador retornar qualquer coisa diferente de falso para valores iguais, a STL gerar comportamento indenido.