Você está na página 1de 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 Modificadores . . . . . . . .
1.7 Estruturas de Controle . . .
1.8 Funes . . . . . . . . . . .
1.8.1 Funes inline . . . .
1.8.2 Parmetros Default

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

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

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

1
1
2
4
4
5
5
6
6
8
8

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

13
13
14
14
16
17
18
21
21
22
23

3 Sobrecarga
3.1 Quando usar? . . . . . . . . . . . . . . . . .
3.2 Modificador 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 . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

25
25
25
26
28
29
29
30
30

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

SUMRIO

3.8
3.9

3.7.1 Exemplo dos operadores de streams ( e ) . . . . . .


Converso entre Tipos . . . . . . . . . . . . . . . . . . . . . .
Sobrecarregando ++ e - - . . . . . . . . . . . . . . . . . . . .

31
32
32

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

33
33
35
35
35
37
39
41
41
42
42

5 Funes Virtuais e Polimorfismo


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

.
.
.
.
.
.

45
45
47
47
47
49
49

.
.
.
.
.
.
.
.
.
.

51
51
51
52
52
52
53
53
53
55
57

.
.
.
.

59
59
60
60
62

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 Especficas . . . . . . . . . . . . . . . . . . . 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 significativa. Alm disso, atravs de uma organizao
modular e independente, melhora a manutenabilidade e aumenta a confiabilidade 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 eficiente. 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 refletir elementos tanto
do estilo de programao orientada a objetos como programao procedimental
clssica. E isto porque o C++ uma linguagem extensvel, ou seja, podemos
definir novos tipos de tal maneira que eles ajam do mesmo modo que tipos
pr-definidos 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 significa 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 significado 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 fixar 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 prefixao 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 finalizado. 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). Exemplificando: 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 ficaria 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;
float: valores em ponto flutuante (reais);
double: valores em ponto flutuante, 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

Modificadores

Modificadores so utilizados para especificar 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 final na execuo,
como seria normal a uma varivel local, mas guardado para uso posterior. O
cdigo 1.6 demonstra isso.
Cdigo 1.6: Modificadores
// 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 fins 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 refletida 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 qualificador 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

11

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

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 especfico.
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 definimos os dois carros como
objetos dessa classe. A figura abaixo facilita o entendimento.

14

Introduo a Orientao a Objetos

Simplificando, podemos definir uma classe como um tipo de dados, porm


que possui funes em sua definio, 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 figura. 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, ficam 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 verificao 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 confivel, e contribuindo para a
organizao e reutilizao do cdigo.

2.2 Classes

2.2.3

17

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


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 definio
e a implementao, utilizando, respectivamente, arquivos header (.h) e source
(.cpp).
Nos arquivos header, coloca-se idealmente apenas aquilo que define 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 definio 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

19

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

#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 redefined. Para que isso no ocorra, criamos uma
flag 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 flags. 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 definies.
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

2.3

21

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 definir a
composio, basta definir 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 significativas quando usamos constantes. Alm disso, caso seja feita uma tentativa
de modificar 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 infinitamente 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 exemplificado 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 modificador 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

23

const Fred* const p: p um ponteiro constante para um Fred constante,


o que significa que no podemos mudar o Fred e nem para que Fred este
ponteiro aponta.

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, definir vrias funes com o mesmo
nome. Em C++ esta prtica possvel, desde que o conjunto de parmetros
de todas seja diferente. O compilador consegue definir 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 exemplificado no cdigo 3.1.

3.2

Modificador 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 definida 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 definio 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 defina
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

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

};

...

};

27

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 definidos 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, floats 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 significado similar ao significado 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 ficar direita, de modo que ser necessrio
utilizar esse artifcio.

3.5 Sobrecarga de Operadores Unrios

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

29

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 definido 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

Cdigo 3.7: Operadores Binrios Friend


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

da classe ficar 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 exemplificando 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.

31

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 artficio, 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 prefixo, 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 codificar a herana em C++ vamos entender seu significado 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 definindo 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 classificao 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 definidas 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 fizemos foi juntar as partes comuns as classes
que queremos definir.
Esse um exemplo da reduo de cdigo repetido. Temos tudo pronto.
Agora, no entanto, descobrimos que existe um algortimo de alimentao mais
eficiente, 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,
modificamos 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
especfica do Leopardo). Esse um exemplo da reutilizao do cdigo.

4.2 Membros Protected

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.

35

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

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 especficas
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 confia, 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

37

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 exemplificado
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 redefinindo 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 Polimorfismo.
Quando introduzirmos os conceitos de funes virtuais e polimorfismo 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 definio 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

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.
Definimos o tipo de herana desejada especificando 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:

41

42

Herana
Especificador de Acesso
public
protected
private

4.6

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

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 suficiente.
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
Polimorfismo
O polimorfismo 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 polimorfismo 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 polimorfismo implementado um

46

Funes Virtuais e Polimorfismo

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.

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 fixo, 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 polimorfismo, 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.

47

48

Funes Virtuais e Polimorfismo

Cdigo 5.4: Exemplo de Polimorfismo


#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

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 especfica, 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 polimorfismo, 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. Modifique a classe Empregado gerada anteriormente para ser uma classe
abstrata.

49

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 especificar, 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 especifica 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 especificado.
Parece mgica, mas to simples quanto parece. Imagine uma funo que
soma dois inteiros. Facilmente implementvel, com certeza. Agora, preciso
somar dois floats: sem dvida uma funo semelhante. Assim, Somar dois
doubles no deve apresentar maiores dificuldades. 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 definidos pelo usurio entre os sinais
de menor e maior. No cdigo 6.1, a declarao normal da funo, referindo ao
tipo genrico especificado 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 float.
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 fila
(FIFO First In First Out). Dessa forma, possvel especificar 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

6.2.2

6.3

Exemplo

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 ficar claro que o tratamento
de excees especfico 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 final 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 verificado 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
significa passar o fluxo 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.

53

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

6.4.1

Exemplo

Os cdigos 6.3 e 6.4 exemplificam 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
definir qual bloco catch tratar da exceo em questo.

55

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 polimorfismo 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

6.4.2

Exerccio

1) Crie uma situao semelhante ao exemplo em que ocorra um erro por


estouro (overflow). Utilize variveis que estouram mais comumente, como short
int ou byte.

57

Captulo 7

Processamento de Arquivos
As variveis de programas so temporrias, isto , sero armazenadas na
memria do seu computador e, assim que seu programa finalizar, 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 definies 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 definidas 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 fim 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 fim do arquivo.


ios::ate Abre um arquivo para sada e avana at o fim 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 filmes 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
filmes. Em seguida, faa um programa que leia deste arquivo seqencial e
escreva na tela apenas o nome dos filmes 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 eficiente.
Imagine-se na frente de um caixa eletrnico, querendo realizar uma retirada
de dinheiro, e o sistema verificando seqencialmente num arquivo de todas as
contas do banco, se sua conta possui o saldo suficiente. 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 fixo. 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 dificuldades. Os prprios objetos ostream e istream possuem mtodos write e read,
respectivamente, nos quais possvel especificar um offset 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 dificilmente 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

Standard Template Library


Continer
Vector

Categoria
Sequencial

List
Deque

Sequencial
Sequencial

String
Set
Multiset
Map

Sequencial
Associativo
Associativo
Associativo

Multimap

Associativo

Stack
Queue
Priority Queue

Adaptador
Adaptador
Adaptador

Descrio
Inseres/Retiradas rpidas, apenas no fim.
Acesso direto a qualquer elemento.
Inseres/Retiradas rpidas.
Inseres/Retiradas rpidas, no incio e no fim.
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 suficiente 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 definido. Alm disso, no existem restries, o que implica
que o sort pode ordenar elementos de qualquer classe, desde que o predicado
de ordem seja especificado para esta classe!

8.2

Continers

Continers so inseridos, de acordo com suas caractersticas, em trs categorias: seqenciais, associativos e adaptadores de containers. A justificativa 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 Especficas


Continer
Vector
List
Deque
String
Set e Multiset
Map e Multimap
Stack
Queue e Priority Queue

65
#include
<vector>
<list>
<deque>
<string>
<set>
<map>
<stack>
<queue>

::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 final 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 Especficas

Obviamente, existem algumas funcionalidades especficas 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 fins. 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, definida 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 indefinido 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 ;
}

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 desafiar 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 final e a remoo
do final. No nem um pouco trivial desenvolver uma estrutura de dados com
todas estas capacidades ao mesmo tempo. De fato, se levarmos estas definies
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

67

68

Standard Template Library


usados. Este processo de alocao amortizada justifica 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 indefinido. importante observar
que isto sumariza a diferena entre infinitos elementos e nmero arbitrrio de
elementos. Um vector deve ser pensado exatamente como um array C que pode
ter seu tamanho modificado 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

8.6.1

69

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 suficiente para gerar um erro de execuo. Dessa
forma, esse tipo de acesso gera comportamento indefinido, que suficiente 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 especificamente simplesmente encadeada, use a estrutura no-padro
slist).
Listas so recomendadas para tarefas onde necessrio muita performance
para inseres no incio ou no final, e consulta no incio ou no final. 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 fixo 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 definido internamente e
significa 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 especficos da STL, e ainda sobre um tipo de dados especfico, o pair.
Deque Um deque funcionalmente semelhante a um vector. Observe que
deque significa 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 significa 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 especificar o local onde o elemento ser adicionado, pois estes containers so implementados como
rvores binrias balanceadas. Isto significa, 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 find, 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.
Modificadores de containeres Stacks, Queues e Priority Queues no so
containeres propriamente ditos, mas sim modificadores que atuam sobre
containeres. Dessa forma, para especificarmos uma stack, por exemplo,
primeiramente temos que definir 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 especificar 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 .first e .second.

8.9

Iteradores

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

71

72

Standard Template Library


especficos 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, filas,
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 conflito, 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 definido 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 definido, 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

73

rvores binrias), o operador++ est bem definido. Finalmente, devemos observar o significado dos comandos foo.begin() e foo.end() no exemplo acima.
foo.begin() um iterador especfico 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 modificar 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 definido 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 modificados, 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.
::const reverse iterator Anlogo ao ::const iterator, mas se aplica a varredura
reversa do container. Alm disso, cada container tem definido 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 definidos 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
indefinido.
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 indefinido.

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 eficiente, facilitando muito o trabalho do programador. Um exemplo de
algoritmo extremamente utilizado o de ordenao. Atravs desse algoritmo,
fica 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

75

count(iterator first, 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 definido pelos
dois iteradores
count if(iterator first, iterator last, predicate pred) esta funo semelhante funo count, mas ela retorna o nmero de elementos dentro do
intervalo definido 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 first, iterator last) retorna um iterador para o
elemento de valor mximo dentro do intervalo definido por first e last.
Esta funo utiliza o comparador operator< do tipo do container para
fazer as comparaes.
min element(iterator first, 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 definir 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 definido 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

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

77

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 classificao 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 Effective 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 indefinido 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 definida[3]: Grande parte da filosofia 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, float, 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
especficos 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 dificultar
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 ) ;
}

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 significa
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.
Prefira 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 definio C++ , novamente, indefinido. 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

79

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

Você também pode gostar