A Linguagem C++
Renato Maia
maia@inf.puc-rio.br
Hello, World!
#include
<iostream>
int main()
{
std::cout << "Hello, world!" << "\n";
}
MÓDULO I
Recursos Básicos
nParte 1:
¨ Tipos ¨ Declarações ¨ Ponteiros ¨ Vetores ¨ Estruturas ¨ Operadores ¨ Expressões ¨ Comandos ¨ Funções nExemplo
¨ Calculadora nParte 2:
¨ Espaços de Nomes ¨ Compilação e LigaçãoTipo Lógico
n
Tipo: bool
n
Literais: true , false
n
Conversões:
¨
true
è 1
¨
false
è 0
¨
0
è false
¨
?0
è true
Tipo Caractere
n
Tipo: char
n
Modificadores: signed , unsigned
n
Literais:
¨
Letras: 'a', 'b', ..., 'Z'
¨
Algarismos: '0', '1', ..., '9'
¨
Especiais: '\n', '\t', '\0'
Tipo Inteiro
nTipo: int
nModificadores:
¨signed , unsigned
¨short , long
nLiterais:
¨Decimal: 20
¨Octal: 020
¨Hexadecimal: 0x20f
Tipos Reais
n
Tipos: float , double
n
Modificadores: long (aplicável ao double)
n
Literais: (não podem conter espaços)
¨
1.23
¨
.23
¨
1.
¨
1.23e10
Tipo Vazio
n
Tipo: void
n
Uso:
¨
Como tipo de retorno de uma função
n
Define funções que não retornam valores
¨
Como tipo de ponteiro
n
Define um ponteiro para tipo indefinido (ponteiro
Enumerações
n
Exemplo:
enum DiaSem { DOM, SEG, TER, QUA, QUI,
SEX, SAB };
n
Uso
¨
Definir um tipo que assume um conjunto de
valores inteiros pré-definidos (enumerados).
Declarações
Especificador Tipo Declarador Iniciação/Definição
char ch ;
string s ;
int count = 1 ;
const double pi = 3.1415926535897932385 ;
extern int error_number ;
const char * name = "Najla" ;
const char * season[ ] = {"spring","summer", "fall","winter};
struct Date { int d, m, y } ;
int day(Date* p){ return p->d; } ;
double sqrt(double) ;
template<class T> T abs(T a) { return a<0 ? –a : a; }
typedef list<int> SmallNums ;
struct User ;
Declarações (observações)
n
Declaração com vários nomes
¨
int a, b = 0;
// apenas o 'b' é iniciado.
¨
int* pa, pb;
// o 'pb' NÃO é um ponteiro.
n
Nomes de identificadores
¨
hello, DEFINED, var23, _123, __,
um_nome_razoavelmente_GRANDE
n
Iniciação (apenas estáticos)
Escopo
int x = 0;
{
int x = 1;
cout << x;
{
int x = 2;
cout << x;
}
x = 3;
cout << x;
}
cout << x;
Declaração de Tipos
n
Exemplos:
typedef char *Pchar;
Pchar p1, p2;
char *p3 = p1;
n
Na realidade a declaração de um typedef
define apenas um sinônimo para algum
tipo.
Ponteiros
n
Uso
<
Outras Formas
char c = 'a';
char **ppc;
char *pc = &c;
char *vetp[15];
char c2 = *pc;
char (*fp)(char*);
pc:
c: 'a'
&c
Iniciação de Ponteiros
n
A linguagem C++ não define uma palavra
reservada para indicar um endereço de
memória inválido ou nulo (e.g. null de
Java). Ao invés disso usa-se o valor 0,
que pode ser atribuído a ponteiros.
Nenhum objeto é criado no endereço 0.
Vetores
n
Dimensões
float d1[3];
float d2[10][20]; // não: float d2[10, 20];
n
Iniciação
int v1[ ] = { 1, 2, 3, 4 }; // v1 é do tipo int[4]
char v3[3] = {'a','b',0}; // não: char v3[2] = {'a','b',0};
int v5[5] = {1,2,3} // equiv.: int v5[3] = {1,2,3,0,0};
int v7[5];
Literais de Cadeias de Caracteres
n Na iniciação de vetores de caracteres
char c2[ ] = "cadeia";
char c3[ ] = { 'c', 'a', 'd', 'e', 'i', 'a', '\0' }; sizeof("cadeia") == 7
n Acesso através de ponteiros irrestritos (vs. ponteiros para const.)
char *pc = "imutavel";
pc[0] = ' '; // erro: resultado indefinido
n Comparação de cadeias
const char *p = "C++"; const char *q = "C++";
if (p == q) cout << "one!\n"; // depende da implementação do C++
n Quebrando cadeias grandes
Ponteiros e Vetores
n
Conversão implícita para ponteiros
char v[ ] = "cadeia"; char *p;
p = v; // conversão implícita
v = p; // erro: não é possível atribuir um ponteiro a um vetor
'c' 'a' 'd' 'e' 'i' 'a' 0
v:
p:
p:
Aritmética de Ponteiros
n
Iteração sobre uma cadeia de caracteres
char v[ ] = "uma cadeia\n"
for (int i = 0
; v[i]
!= 0
; i++
)
use(v[i]);
for (char *p = v
; *p
!= 0
; p++
)
use(*p);
n
Subtração de ponteiros (ou endereços)
int v1[10];
int v2[10];
int i1 = &v1[5] - &v1[3];
// i1 == 2
Constantes
n
Declaração
const int model = 90;
const int v[ ] = { 1, 2, 3, 4, 5 };
// v[i] é const
const int x;
// erro: exige iniciação
n
Ponteiros constantes
char*const cp;
// ponteiro constante
const char*pc;
// ponteiro para uma constante
Referências
n
Definição
int i = 1;
int& r = i;
// r e i se referem ao mesmo elemento
n
Uso
int x = r;
// x = 1
r = 2;
// i = 2
n
Iniciação
int& r2;
// erro: falta iniciação
Ponteiros Genéricos (void *)
n
Recurso de baixo nível
n
Operações
int* pi = &i;
void* pv = pi;// ok: conversão implicita de int* para void*
*pv; // erro: não é possível acessar um void*
pv++; // erro: não é possível incrementar um void*
n
Conversões (cast)
int* pi2 = static_cast<int*>(pv); // conver. explíc.
double* pd1 = pv; // erro
double* pd2 = pi; // erro
Estruturas
n
Declaração
struct address {
char *name;
// "Jim Dandy"
long int number;
// 61
char *street;
// "South St"
char *town;
// "New Providence"
char state[2] ;
// ’N’ ’J’
long zip;
// 7974
Estruturas (cont.)
n
Uso
address jd;
jd.name = "Jim Dandy";
jd.number = 61;
nIniciação
address jd = {
"Jim Dandy",
61, "South St",
"New Providence", {´N´,´J´}, 7974 };
Ponteiro para Estruturas
n
Operador de acesso através de ponteiros
p->m è (*p).m
nUso
address *p = &jd
cout
<< p->name << ´\n´
<< p->number << ´ ´ << p->street << ´\n´
<< p->town << ´\n´
<< p->state[0] << p->state[1] << ´ ´
<< p->zip << ´\n´;
Cópia de Estruturas
address current;
address set_current(address next)
{
address prev = current;
current = next;
return prev;
}
Declaração Antecipada
struct Link {
Link* previous;
Link* successor;
};
struct List;
// definido posterior.
struct Link {
Link* pre;
Link* suc;
List* member_of;
};
struct List {
Link* head;
};
Operadores Relacionais
n
Igual
==
n
Diferente
!=
n
Maior que
>
n
Menor que
<
n
Maior ou igual
>=
n
Menor ou igual
<=
Operadores Aritiméticos
nAdição
+
nSubtração
-nMultiplicação
*
nDivisão
/
nMódulo
%
nInversor de sinal
-nIncremento
--nDecremento
++
Operadores Lógicos
n
E (and)
&&
n
Ou (or)
||
Operadores sobre Bits
n
E bit a bit
&
n
Ou inclusivo bit a bit
|
n
Ou exclusivo (xor) bit a bit
^
n
Complemento
~
n
Deslocamento a esquerda
<<
Operadores de Atribuição
n Atribuição simples = n Multiplicação e atribuição *= n Divisão e atribuição /= n Módulo e atribuição %= n Soma e atribuição += n Subtração e atribuição -=n Deslocamento a esquerda e atribuição <<=
n Deslocamento a direita e atribuição >>=
n E bit a bit e atribuição &=
n Ou inclusivo bit a bit e atribuição |=
Operadores Composicionais
n
Seqüência
<expr> , <expr>
n
Condicional
<cond> ? <true> : <false>
Operadores de Memória Dinâmica
n
Alocação
new
n
Desalocação
delete
Operad. de Conversão de Tipos
n
Conversão estática (tipos relacionados)
static_cast<tipo>(valor)
n
Conversão estática (tipos não relacion.)
reinterpret_cast<tipo>(valor)
n
Conversão dinâmica
dynamic_cast<tipo>(valor)
n
Conversão de C
Operadores de Construção
n
Construtor
tipo (valor)
n
Exemplos
double d = 1.23;
int i = int(d);
complex c = complex(d);
// tipo definido na
biblioteca padrão do C++
Operadores (observações)
n
Ordem de avaliação dos operandos é indefinida.
int a = f() + g()
// g() pode executar primeiro
n
Sempre use parênteses para garantir a
precedência esperada.
if ( (i & mask) == 0 )
// ...
n
Evite escrever expressões complexas e pouco
legíveis. Conte com as otimizações do
Bloco de Comandos
{
<comando>;
<comando>;
<comando>;
...
<comando>;
}
Comandos de Seleção
nCondicional
if (<expr>) <comando>;
else <comando>;
nSeleção
switch (<expr>) {
case <const>:
<comandos>; break;
case <const>:
<comandos>; break;
default:
<comandos>; break;
}
Comandos de Iteração
n
Laço com teste no início
while (<cond>) <comando>;
n
Laço com teste no final
do <comando> while (<cond>);
n
Laço com contador
for (<início>; <cond>; <increm>) <comando>;
for (;;) { /* ... */ }
// para sempre
Comando de Salto
n
O famigerado goto
int i;
int j;
for (i = 0; i<n; i++)
for (j = 0; j<m; j++) if (nm[i][j] == a) goto found;
// não encontrado
// ...
found:
Definição
extern void swap(int*, int*);
// declaração
void swap(int* p, int* q)
// definição
{
int t = *p;
*p = *q;
*q = t;
}
Funções inline
inline int fac(int n)
{
return (n<2) ? 1 : n*fac(n-1);
}
int i = f(6);
è
int i = 720;
Variáveis Estáticas
void f(int a)
{
while (a--)
{
static int n = 0;
// iniciada uma vez
int x = 0;
// iniciada n vezes
cout << "n == " << n++ << ", x == " << x++ << '\n';
}
}
n == 0, x == 0
n == 1, x == 0
n == 2, x == 0
Passagem de Parâmetros
n
Passagem por referência
void increment(int& aa) { aa++; }; int x = 1; increment(x); // x == 2
n
Parâmetros constantes
void f(const int *p) { *p = 1; /* erro! */ }
n
Parâmetros anônimos
void search(table* t, const char* key, const char*)
{
// não é possível utilizar o terceiro parâmetro
Constantes por Referência
n
Referência para valores constantes
double& dr = 1; // error: não pode ser constante
const double& cdr = 1; // ok: double t = double(1) ; const double& cdr = t;
n
Valores constantes por referência
void update(float& i) ; void g(double d, float r)
{
update(2.0f) ; // error: argumento constante
update(r) ; // passa uma referência a r
update(d) ; // error: necessária conversão de tipo
Valor de Retorno
n
Retorno por referência
int vet[5];
int& getcounter(int i) { return vet[i]; }
getcounter(3)++;
n
Retorno de variáveis automáticas
int* fp() { int local = 1;
/* ... */
return &local; }
// cuidado!
Sobrecarga de Funções
void print(double);
void print(long);
void f()
{
print(1L);
// print(long)
print(1.0);
// print(double)
print(1);
// erro: ambígua, print(long(1)) or
//
print(double(1)) ?
Parâmetros Default
void print(int value, int base =10); void f() { print(31) ; print(31,16) ; print(31,2) ; } n
Alternativa
void print(int value, int base) ;
inline void print(int value) { print(value,10) ; }
n
Erros comuns
Parâmetros Variáveis
#include <cstdarg>
void error(int severity, ...) { // "severidade" e mensagens
va_list ap;
va_start(ap, severity) ; // iniciação arg
for (;;) {
char* p = va_arg(ap,char*) ; if (p == 0) break;
cerr << p << ' '; }
va_end(ap) ; // limpa o conteúdo de ap
cerr << '\n';
if (severity) exit(severity) ;
}
Ponteiros para Funções
void print(const char *s) {
/* ... */
}
void (*pf)(const char*);
// ponteiro para função
void f()
{
pf = print;
// também: pf = &print;
pf("error");
// também: (*pf)("error");
Macros
n
Substituições simples
#define
PI 3.141593
n
Macros razoáveis
#define
CASE break;case
#define
FOREVER for(;;)
n
Macros perigosas
#define
SQUARE(a) a * a
Exemplo
Exemplo
n
Entrada
¨
perimetro = 2 * pi * (r = 2.5)
¨
area = pi * r * r
n
Saída
¨
15.708
¨
19.635
Gramática da Calculadora
program:
END
expr_list END
expr_list:
expression ;
expression ; expr_list
expression:
expression + term
expression - term
term
term:
term / primary
term * primary
primary
primary:
NUMBER
NAME
NAME = expression
- primary
( expression )
Tokens
enum Token_value {
NAME, NUMBER, END,
PLUS = '+', MINUS = '-', MUL = '*', DIV = '/',
PRINT = ';', ASSIGN = '=', LP = '(', RP = ')'
};
Token_value
curr_tok = PRINT;
double
number_value;
Leitura de Tokens
Token_value get_token() {
char ch = 0;
do {
// pula espaços em branco, exceto '\n'
if(!cin.get(ch)) return curr_tok = END;
} while (ch != '\n' && isspace(ch)) ;
…
Leitura de Tokens
…
switch (ch) {
case 0:
return curr_tok = END;
case ';':
case '\n':
return curr_tok = PRINT;
case '*':
case '/':
case '+':
case '-':
case '(':
case ')':
case '=':
return curr_tok = Token_value(ch) ;
Leitura de Tokens
…
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '.':
cin.putback(ch) ;
cin >> number_value;
return curr_tok = NUMBER;
Leitura de Tokens
…
default: // NAME, NAME=, ou erro
if (isalpha(ch)) {
string_value = ch;
while (cin.get(ch) && isalnum(ch))
string_value += ch ; cin.putback(ch) ;
return curr_tok = NAME;
}
error("bad token") ;
return curr_tok = PRINT;
} }
Erros
double error(const string& s)
{
no_of_errors++;
cerr << "error: " << s << '\n';
return 1;
Expressão
double expr(bool get) { // adiciona e subtrai
double left = term(get) ; for(;;) switch (curr_tok) {
case PLUS: left += term(true) ; break; case MINUS: left -= term(true) ; break; default: return left; } }
Termo
double term(bool get) { // multiplica e divide
double left = prim(get) ; for (;;) switch (curr_tok) {
case MUL: left *= prim(true) ; break; case DIV: if (double d = prim(true)) { left /= d; break; } return error("divide by 0") ; default: return left; }
Variáveis
n
Armazenar numa tabela que associe
strings a números reais
n
Sugestão: usar o template 'map' da
biblioteca padrão do C++
Primário
double prim(bool get) { // manipula primários
if (get) get_token() ; switch (curr_tok) {
case NUMBER: // constante real
{ double v = number_value; get_token() ; return v; } case NAME: { double& v = table[string_value] ;
if (get_token() == ASSIGN) v = expr(true) ; return v;
Primário
…
case MINUS: // menos unário
return -prim(true) ; case LP:
{ double e = expr(true) ;
if (curr_tok != RP) return error(") expected") ;
get_token() ; // consome o ')'
return e;
}
default:
return error("primary expected") ;
} }
Função main
int main(int argc, char* argv[])
{
table["pi"] = 3.1415926535897932385; // constantes predefinidas
table["e"] = 2.7182818284590452354;
while (cin) {
get_token() ;
if (curr_tok == END) break;
if (curr_tok == PRINT) continue;
cout << expr(false) << '\n'; }
return 0;
Exercícios
1.
Compilar e executar o programa de exemplo mostrado
previamente
2.
Escrever um programa de encriptação que recebe um
texto pela entrada padrão (cin) e devolve um texto
codificado na saída padrão (cout).
¨ A codificação deve ser feita na forma c^chave[i], tal que chave é uma string dada pela linha de comando. Dessa forma quando um texto for novamente codificado com a mesma chave o texto original é recuperado.
¨ Os caracteres da chave devem ser utilizados de forma circular.
¨ Se nenhuma chave for fornecida, então nenhuma codificação deve ser feita.
Teste do Exercício 2
n
Legenda:
¨
encrypt:
nome do seu programa
¨
senha:
chave de criptografia
¨
input.txt:
um arquivo de texto qualquer
¨
output.dat:
arquivo com o texto codificado
¨
back.txt:
arquivo com o texto decodificado
n
Comandos:
C:\>encrypt senha < input.txt > output.dat
C:\>encrypt senha < output.dat > back.txt
Espaços de Nomes
namespace Parser { double expr(bool) ;
double prim(bool get) { /* ... */} double term(bool get) { /* ... */} double expr(bool get) { /* ... */} }
namespace Lexer { enum Token_value {
NAME, NUMBER, END,
PLUS=´+´, MINUS=´-´, MUL=´*´, DIV=´/´, PRINT=´;´, ASSIGN=´=´, LP=´(´, RP=´)´ }; Token_value curr_tok; double number_value; string string_value; Token_value get_token() { /* ... */} }
Nomes Qualificados
double Parser::term(bool get) // note a qualificação Parser::
{
double left = prim(get) ; // nenhuma qualificação necessária
for (;;)
switch (Lexer::curr_tok) { // note a qualificação Lexer::
case Lexer::MUL: // note a qualificação Lexer::
left *= prim(true) ; // nenhuma qualificação necessária // ...
}
// ...
Utilizando Declarações
namespace Parser {double term(bool get);
// ...
using Lexer::curr_tok; // use o curr_tok do Lexer
}
double Parser::term(bool get) { double left = prim(get);
for (;;) switch (curr_tok) { // qualificação Lexer:: é desnecessária
case Lexer::MUL: left *= prim(true) ; // ... } // ... }
Diretivas de Utilização
namespace Parser {double term(bool get);
// ...
using namespace Lexer; // incorpora todas as declarações
}
double Parser::term(bool get) { double left = prim(get);
for (;;) switch (curr_tok) { // qualificação Lexer:: é desnecessária
case MUL: // qualificação Lexer:: é desnecessária
left *= prim(true) ;
// ...
}
// ...
Espaços de Nomes Anônimos
namespace { int a; void f() { /* ... */ } int g() { /* ... */ } } /* Equivalente a: namespace $$$ { int a; void f() { /* ... */ } int g() { /* ... */ } } using namespace $$$; */Alias de Espaços de Nome
// nome muito longo
namespace American_Telephone_and_Telegraph { /* ... */ } American_Telephone_and_Telegraph::String s3 = "Grieg"; American_Telephone_and_Telegraph::String s4 = "Nielsen";
// alias
namespace ATT = American_Telephone_and_Telegraph;
ATT::String s3 = "Grieg"; ATT::String s4 = "Nielsen";
Exercício
n
Dividir o programa de exemplo da calculadora
em módulos (usando espaços de nomes)
¨
Módulo Léxico (namespace Lexer)
n Leitura e interpreção de tokens
¨
Módulo Parser (namespace Parser)
n Interpretação e avaliação de expressões
¨
Módulo de Erros (namespace Error)
Compilação e
Ligação
Compilação Condicional
#ifdef NDEBUG
const bool ARG_CHECK = false; // desabilita verificações
#else
const bool ARG_CHECK = true; // habilita verificações
#endif
void f3(int* p)
{
assert(!ARG_CHECK || p!=0) ; // ou não faz verificação ou p!=0 // ...
Compilação em Partes
lexer.cpp
parser.cpp
error.cpp
main.cpp
<string>
<map>
lexer.h
parser.h
error.h
Cabeçalho do Módulo Lexer
#include <string>
namespace Lexer {
enum Token_value {
NAME, NUMBER, END,
PLUS = '+', MINUS = '-', MUL = '*', DIV = '/', PRINT = ';', ASSIGN = '=', LP = '(', RP = ')' };
extern Token_value curr_tok; extern double number_value; extern std::string string_value;
Token_value get_token(); }
Módulo Lexer
#include "lexer.h" #include "error.h" #include <iostream> #include <cctype> using std::cin;Lexer::Token_value Lexer::curr_tok = Lexer::PRINT;
double Lexer::number_value;
std::string Lexer::string_value;
Guardas de Inclusão
n
Para evitar que um cabeçalho seja incluído diversas
vezes no mesmo arquivo
// error.h: #ifndef CALC_ERROR_H #define CALC_ERROR_H namespace Error { // ... } #endif // CALC_ERROR_H
Ligação com Código C
n A forma de chamada de funções C é diferente das chamadas de C++. É necessário informar ao compilador quando uma função deve ser ligada como uma função C.
extern "C" void funcao_c(int, int); extern "C" {
void uma_funcao_c(int, int);
void outra_funcao_c(int, double); void mais_outra_funcao_c(char*);
}
extern "C" {
#include "modulo_c.h" }
Ponteiros para Funções C
typedef int (*FT)(const void*, const void*) ; // FT tem ligação C++
extern "C" {
typedef int (*CFT)(const void*, const void*) ; // CFT tem ligação C
void qsort(void* p, size_t n, size_t sz,CFT cmp) ; // cmp tem ligação C
}
void isort(void* p, size_t n, size_t sz,FT cmp) ; // cmp tem ligação C++
void xsort(void* p, size_t n, size_t sz,CFT cmp) ; // cmp tem ligação C
extern "C" void ysort(void* p, size_t n, size_t sz,FT cmp) ; // cmp tem ligação C++
int compare(const void*, const void*) ; // compare() tem ligação C++
extern "C" int ccmp(const void*, const void*) ; // ccmp() tem ligação C
void f(char* v, int sz) {
qsort(v,sz,1,&compare) ; // erro
qsort(v,sz,1,&ccmp) ; // ok
isort(v,sz,1,&compare) ; // ok
isort(v,sz,1,&ccmp) ; // erro
Exercício
n
Dividir o programa de exemplo da
calculadora em unidades de compilação
diferentes (usando a mesma estrutura de
módulos do último exercício)
¨
Módulo Léxico (lexer.cpp e lexer.h)
¨
Módulo Parser (parser.cpp e parser.h)
¨
Módulo de Erros (error.cpp e error.h)
¨
Módulo Principal (main.cpp)
MÓDULO II
Mecanismos de Abstração
nParte 1:
¨Classes
¨Objetos
nParte 2:
¨Sobrecarga de
Operadores
¨Classes Derivadas
nParte 3:
¨Herança Múltipla
¨Templates
nParte 4:
¨Exceções
¨Informação de Tipo
Dinâmica (RTTI)
Classes
n
As classes de C++ define um novo tipo
que funcionam como um tipo básico da
linguagem
n
A forma de usar um objeto de uma classe
não deve diferir do uso dos tipos básicos
Funções Membro
class Date {int d,m,y;
void init(int dd, int mm, int yy) ; void add_year(int n) { y += n; }
void add_month(int n) { m += n; } void add_day(int n) { d += n; } };
void Date::init(int dd, int mm, int yy) { // especifica a classe a que pertence
d = dd; m = mm; y = yy; }
Controle de Acesso
class Date {
int d,m, y;
// privado: acessível por funções membro
public:
// público: acessível por qualquer cliente
void init(int dd, int mm, int yy) ;
void add_year(int n)
{ y += n; }
void add_month(int n) { m += n; }
void add_day(int n)
{ d += n; }
};
Classes e Estruturas
n
Estruturas são equivalentes a classes, sendo que o
controle de acesso padrão é o público.
struct X { // ... }
Equivalente a:
class X { public: // ... }Importância Controle Acesso
n
Erros que causam inconsistência de dados
privados são localizados na implementação das
funções membro de acesso
n
Alterações na implementação não são
propagadas aos clientes
n
Simplifica a utilização da classe, pois é
Construtores
class Date {int d,m, y; public:
Date(int dd=0,int mm=0,int yy=0);
// ...
};
Date::Date(int dd, int mm, int yy) {
d = dd ? dd : today.d; m = mm ? mm : today.m; y = yy ? yy : today.y;
// verifica de a data é válida
}
n Inicição de objetos Date Date today(22) ;
Date july4(4, 7, 1983) ; Date birthday(4, 12) ; Date now;
Membros Estáticos
class Date {int d,m, y;
static Date today; public:
Date(int dd=0,int mm=0,int yy=0);
// ...
static void settoday(int, int, int);
};
Date Date::today(17, 11, 2004);
void Date::settoday(int d,int m,int y){
today = Date(dd, mm, yy) }
n Acesso a membros estáticos Date now;
now.settoday(22, 11, 2004); ou
Auto-Referência
n
Todo objeto tem um ponteiro implícito
demominado this que aponta para o próprio
objeto
bool Date::is_the_same(Date& other)
{
return *this == other;
Funções Membro Constantes
n Não alteram os estado do objeto
class Date { int d,m, y; public:
int day() const { return d++; } // erro: tentativa de alteração do estado
int month() const;
// ...
};
inline int Date::month() const { return m; }
n Podem ser chamadas a partir de referências constantes
const Date my_date(12, 4, 1865);
cout << my_date.day() << "/" << my_date.month() << "/" << my_date.year()
Membros Mutáveis
n Alteração de membros através de funções membro
constantes
string Date::string_rep() const { if (!cache_valid) { compute_cache() ; cache_valid = true; } return cache; } class Date {
mutable bool cache_valid; mutable string cache;
void compute_cache() const;
// ...
public:
// ...
string string_rep() const; };
Funções Auxiliares
n
É possível definir funções auxiliares para manipular
objetos
int diff(Date a,Date b) ; bool leapyear(int y) ;
Date next_weekday(Date d) ; Date next_saturday(Date d) ;
n
Função membro vs. função auxiliar
¨ Função membro acessa diretamente o estado privado do objeto
¨ Função auxiliar realiza sua tarefa apenas com as operações da interface pública do objeto.
Funções Amigas
class Matrix; class Vector {
float v[4] ;
// ...
friend Vector multiply( const Matrix&, const Vector&) ; }; class Matrix { Vector v[4] ; // ...
friend Vector multiply( const Matrix&, const Vector&) ;
};
n Permite acessar a interface privada das classes
Vector multiply( const Matrix&m, const Vector& v ) {
Vector r;
for (int i = 0; i<4; i++) { // r[i] = m[i] * v;
r.v[i] = 0; for (int j = 0; j<4; j++) r.v[i] +=m.v[i].v[j] * v.v[j] ; } return r; }
Funções Amigas
n
Funções Membro
class List_iterator { // ... int* next() ; }; class List {friend int* List_iterator::next() ;
// ...
};
n
Classes Amigas
class List {
friend class List_iterator;
// ...
};
¨ Todas funções membro de List_iterator se tornam amigas da classe List
Exercício
n
Implementar o tipo Date, como ilustrado nos
exemplos anteriores:
¨
Construtor default
¨
Definir o valor da data default
n Use uma abordagem similar à função Date::settoday()
¨
Funções para acesso aos dados
n Dia, mês e ano
¨
Funções para adicionar dias, meses e anos
Criação e Destruição
n
Sempre que um objeto não é mais
utilizado, ele deve ser destruído
n
Assim como a inicialiação (ou construção)
de objetos, a destruição pode ser feita
automaticamente pelo compilador ou pode
ser feita explicitamente
Destrutores
struct cache { bool valid; string rep; }; class Date { cache* c;void compute_cache () const;
// ...
public:
Date(int dd=0,int mm=0,int yy=0); ~Date(); // destrutor
// ...
string string_rep() const; };
n Libera os recursos alocados durante a construção do objeto
Date::Date(int dd,int mm,int yy) { c = new cache; c->valid = false; // ... } Date::~Date() { delete c; }
Cópia Default
n
Operação de cópia default
void apaga(Date *dia) {
Date copia = *dia;// copia.c = dia->c; copia.d = dia->d; ...
delete dia; // destrói o objeto, apaga 'c'
cout << "O dia " << copia.string_rep() << "foi apagado\n"; // erro
}
Variáveis Locais
n Construção:
¨ Fluxo de execução encontra a declaração da variável n Destruição
¨ Variável sai do escopo
n Exemplo void f(int i) { if (i>0) { Date aa; // ... } Date bb; // ... }
Memória Dinâmica
n Construção
¨ Explicitamente através do operador new
Date *d = new Date(22, 11, 2004);
n Destruição
¨ Explicitamente através do operador delete
delete d;
n Cuidados
¨ Objetos destruídos
delete dia;
delete dia; // erro
¨ Brechas de memória (memory leaks)
void hoje() {
Date *dia = new Date();
cout << "Hoje é dia " << dia.string_rep() << "\n"; }
Membros de Classes
n Construção
¨ Na construção do objeto a que pertence, na ordem que aparecem na declaração.
n Destruição
¨ Na destruição do objeto o que pertence , na ordem inversa da que aparecem na declaração.
n Inicialização de Membros
class DaySequence { const int days;
Date start; public:
DaySequence(const Date& s, const int i) : days(i), start(s) { } }
Vetores
n
Construção
¨ Na criação do vetor
¨ Apenas é permitido para tipos com um construtor sem parâmetros
n
Destruição
¨ Na destruição do vetor n
Exemplo
Date vds[10];// cria 10 objetos usando o construtor Date::Date()
Date vdd[10] = new Date[10];
Variáveis Locais Estáticas
n Construção
¨ Fluxo de execução encontra a declaração da variável pela primeira vez.
n Destruição
¨ Termino do programa
n Exemplo
// Declaração das variáveis // Construção dos objetos
void f(int i) { f(0); // d1 é construído
static Date d1; f(1); // apenas o d2 é contruído // ... f(2); // nenhum dos objetos é criado
if (i) {
static Date d2;
// ...
} }
Não Locais
n Variáveis não locais:
¨ Variáveis globais
¨ Variáveis em espaços de nomes
¨ Variáveis estáticas de classe
n Construção
¨ Antes no início da função main
¨ Na ordem que suas definições aparecem
n Destruição
¨ Após o término da função main
¨ Na ordem inversa que suas definições aparecem
n Exemplo
class X { static Date memdate; }; // apenas a definição do membro estático
Date date; // declaração de variável global
Date X::memdate; // declaração do membro estático da classe X
Objetos Temporários
n Construção
¨ Na avaliação da expressão que os criam n Destruição
¨ Ao final da avaliação da expressão que os criam
n Exemplo
void f(string& s1, string& s2, string& s3)
{
const char* cs= (s1+s2).c_str() ;
cout << cs; // problema: cs aponta para uma área desalocada
if (strlen(cs=(s2+s3).c_str())<8 && cs[0]==´a´) { // ok // qualquer uso de cs aqui é inválido
} }
Exercício
n Implementar o tipo Table, que deve oferecer a seguinte interface:
struct Name {
enum Gender { male, female } const char* s; Gender g; }; class Table { Name* p; size_t sz; int c; public: Table(size_t s = 15); ~Table();
Name* lookup(const char *) ;
bool insert(Name&) ;
Sobrecarga de
Operadores
Operadores Disponíveis
n
Operadores que podem ser redefinidos
+- * / % ^ &
| ~ ! = < > +=
-= *= /= %= ^= &= |=
<< >> >>= <<= == != <=
>= && || ++ -- ->* ,
-> [] () new new[] delete delete[]
n
Operadores que NÃO podem ser redefinidos
:: (Resolução de escopo) . (Seleção de membros)
Funções Operadoras
n
Função Membro
class complex {
double re, im;
public:
complex(double r, double i=0) : re(r) , im(i) { }
complex operator+(complex) ;
};
n
Função Auxiliar
Casamento de Função Operador
n
Escolha da função operador para um
operador @
¨
Operadores Binários (a@b)
a.operator@(b)
ou
::operator@(a, b)
¨
Operadores Unários (a@)
Exemplos de Sobrecarga de
Operadores
class X {
// members (with implicit 'this' pointer):
X* operator&() ; // & unário (endereço de)
X operator&(X) ; // & binário (e bit a bit)
X operator++() ; // incremento prefixo
X operator&(X,X) ; // erro: ternário
X operator/() ; // erro: / unário
};
// nonmember functions:
X operator-(X) ; // menos unário
X operator-(X,X) ; // menos binário
X operator--(X&,int) ; // decremento posfixo
X operator-(); // erro: nenhum operando
X operator-(X,X,X) ; // erro: ternário
Significados Predefinidos
n As funções operator=, operator[], operator() e operator-> devem ser definidas como funções membro não estáticas
n Apenas alguns operadores já possuem um significado predefinido
= (atribuição, faz cópia dos valores dos membros) & (endereço de, retorna o endereço do objeto)
, (seqüência, retorna o valor do segundo parâmetro)
n Entretanto, esses significados podem ser redefinidos através da sobrecarga de operadores
Cópia de Objetos
n Construtor de cópia
Date::Date(const Date& other) { // exemplo:
c = new cache; // Date copia = data;
c->valid = false;
d = other.d; m = other.m; y = other.y; }
n Operador de atribuição
Date::operator=(const Date& other) { // exemplo:
if (*this != other) { // Date copia(2, 4, 1876);
delete c; // copia = data;
c = new cache; c->valid = false;
d = other.d; m = other.m; y = other.y; }
Procura pela Implementação de
Operadores
n Considere a expressão x@y, (x é do tipo X e y é do tipo Y)
¨ Se X é uma classe e define operator@ como um membro essa função membro como operadora
¨ Caso contrário
n Procura por declarações de operator@ no contexto de x@y
n Adicinonalmente, se X é definido num espaço de nomes N, procura por
declarações de @ em N
n Adicinonalmente, se Y é definido num espaço de nomes M, procura por
declarações de @ em M.
Neste último caso, todas as declarações de operator@ são levadas em consideração para determinar a que se adequa a expressão.
n As mesmas regras para casamento de funções sobrecarregadas são aplicadas a operadores.
Conversões Implícitas
bool operator==(complex,complex) ;
void f(complex x, complex y)
{
x==y; // significa operator==(x,y)
x==3; // significa operator==(x,complex(3))
3==y; // significa operator==(complex(3),y)
}
Operadores de Conversão
Explícita
class Tiny { char v;
void assign(int i) { if (i&~077) v=0; else v=i; } public:
Tiny(int i) { assign(i) ; }
Tiny& operator=(int i) { assign(i) ; return *this; }
operator int() const { return v; } // conversão para int
};
n Atenção
Tiny::operator int() const { return v; } // certo
Ambigüidades
n Sempre que a combinação de construtores e operadores de conversão gerarem ambiguidade na resolução de funções sobrecarregadas, o compilador informará o erro.
n Tome cuidado com conversões inesperadas (ou a falta delas)
class Quad { public: Quad(double) ; // ... }; Quad operator+(Quad,Quad) ;
void f(double a1, double a2)
{
Quad r1 = a1+a2; // adição com precisão dupla
Quad r2 =Quad(a1)+a2; // força aritmética da classe quad
Construtores Explícitos
n
Considere
complex z = 2; // inicializa z com complex(2)
String s = 'a'; // cria uma string com int('a') elementos
n
Solução
class String {
// ...
explicit String(int n) ; // prealoca n bytes
String(const char* p) ; // valor inicial é uma string C
string p };
Operador de Indexação
class Assoc { struct Pair {
string name; double val;
Pair(string n ="", double v =0) :name(n) , val(v) { }
};
vector<Pair> vec;
// privado para prevenir cópia
Assoc(const Assoc&) ;
Assoc& operator=(const Assoc&) ;
public:
Assoc() {}
double& operator[](const string&) ; void print_all() const;
};
double& Assoc::operator[](const string& s) {
vector<Pair>::iterator p=vec.begin();
for (; p!=vec.end(); ++p)
if (s == p->name) return p->val;
vec.push_back(Pair(s,0)) ;
return vec.back().val;
}
void Assoc::print_all() const {
vector<Pair>::const_iterator p=vec.begin();
for (; p!=vec.end(); ++p)
cout << p->name << ": " << p->val << ´\n´; }
Operador de Chamada de Função
class Add {
complex val;
public:
Add(complex c) { val = c; } // salva o valor
Add(double r, double i) { val = complex(r,i) ; }
void operator()(complex& c) const { c += val; } // adiciona valor
};
complex vc[10];
void for_each(Add& func) { for (int i = 0; i < 10; i++) func(vc[i]); } void main() {
Add func(2, 3); for_each(func); }
Operador de Dereferência
n
Declaração (operador unário)
class Ptr { /* ... */ X* operator->(); }; n
Transformação
void f(Ptr p) { p->m = 7; // (p.operator–>())–>m = 7 } nUso
void g(Ptr p) { X* q1 = p->; // erro de sintaxe X* q2 = p.operator->();// ok }Operadores Unários Posfixos
class Ptr_to_T { T* p; T* array; int size; public:Ptr_to_T(T* p, T* v, int s) ; // associa a pos. p do vetor v de tam. s
Ptr_to_T& operator++() ; // prefixo
Ptr_to_T operator++(int) ; // posfixo
Ptr_to_T& operator--(); // prefixo
Ptr_to_T operator--(int) ; // posfixo
T&operator*() ; // prefixo
Classes Derivadas
class Employee { string name; Date hiring_date; shot department; // ... public:void print() const;
string get_name() const {
return name; }
// ...
};
class Manager : public Employee {
set<Employee*> group;
short level;
// ...
public:
void print() const;
// ...
Estrutura de Objetos
Employee:
name
department
...
name
department
...
group
level
...
Manager:
Polimorfismo
void f(Manager m1, Employee e1) {
list<Employee*> elist; elist.push_front(&m1); elist.push_front(&e1); }
void g(Manager mm, Employee ee) {
Employee* pe= &mm; // ok: todo Gerente é um Empregado
Manager* pm= ⅇ // erro: nem todo Empregado é um Gerente
pm->level = 2; // desastre: ee não tem um 'level'
pm = static_cast<Manager*>(pe) ; // força bruta, mas funciona
pm->level = 2; // ok: pm aponta para mm que tem um 'level'
Implement. de Funções Membro
n Não é possível acessar membros privados em classes derivadas
void Manager::print() const {
cout << "Name: "<< name << '\n'; // erro: name é privado
cout << "\tDept:\t"<< department << '\n'; // erro: department é privado
cout << "\tLevel:\t" << level << '\n';
cout << "\tTeam:\t" << group.size() << '\n'; }
n É necessário usar a interface pública (e protegida, i.e. protected)
void Manager::print() const {
Employee::print(); // cuidado: print() não qualificado causa recursão
cout << "\tLevel:\t" << level << '\n';
cout << "\tTeam:\t" << group.size() << '\n'; }
Controle de Acesso
n
Escopo Privado (private)
¨
Nomes podem ser usados em funções membro e
funções amigas da classe.
n
Escopo Protegido (protected)
¨
Nomes podem ser usados em funções membro e
funções amigas da classe e nas funções membro e
funções amigas das suas classes derivadas.
n
Escopo Público (public)
Acesso a Membros Herdados
n
class X : public B {
/* ... */
};
¨ Acesso aos membros públicos e protegidos de B e conversão de X* para B* só pode ser feito em funções membro e amigas de X.
n
class Y : protected B {
/* ... */
};
¨ Acesso aos membros públicos e protegidos de B e conversão de Y* para B* só pode ser feito em funções membro e amigas de Y e de suas classes derivadas.
n
class Z : private B {
/* ... */
};
¨ Acesso aos membros públicos de B e conversão de Z* para B* só pode ser feito pode ser feito de qualquer função.
Adicionalmente, o acesso aos membros protegidos de B só pode ser feito em funções membro e amigas de Z e de suas classes derivadas.
Construção
n Classe Base
Employee(const string& n, int d) : name(n), department(d)
{
// …
}
n Classe Derivada
Manager(const string& n, int d, int lvl)
: Employee(n, d), level(lvl) // não é possível iniciar diretamente // os membros de Employee
{
// …
Cópia Fatiada
n
Exemplo
void f(const Manager& m) {
Employee e = m; // constrói e a partir da parte Employee de m
e = m; // atribui a parte Employee de m a e
}
n
Cuidado
void slice_copy(Employee ee) {
ee.print(); }
// ...
Manager m("John Gee", 1, 4);
Funções Virtuais
n
Definição
class Employee {
// ...
virtual void print() const;
};
n
Exemplo
void virtual_call(Employee* ee) {
ee->print(); // a função print adequada é chamada
}
// ...
Manager m("John Gee", 1, 4); virtual_call(&m);
Classes Abstratas
n Permite definir classes com funções sem implementação
class Shape { public:
// funções virtuais puras
virtual void rotate(int) = 0; virtual void draw() = 0; virtual bool is_closed() = 0;
// ...
};
n Não é possível criar objetos de classes abstratas
Shape s; // erro: Shape é abstrata
n Classes abstratas somente são utilizadas como classe base
class Point { /* ... */ };
class Circle : public Shape { public:
// define funções virtuais herdadas
void rotate(int) { } void draw() ;
bool is_closed() { return true; }
Circle(Point p, int r) ;
private:
Point center;
int radius;
Ponteiros para Membros
struct Class {const char* memb_data;
virtual void memb_func() = 0;
};
typedef void (Class::*PMF)() ; // tipo ponteiro para função membro
typedef const char* Class::*PMD; // tipo ponteiro para membro de dado
void f(Class* p) {
PMF pmf = &Class::memb_func(); p->memb_func() ; // chamada direta
(p->*pmf)() ; // chamada através de ponteiro para membro
PMD pmd = &Class::memb_data; p->memb_data() ; // acesso direto
p->*pmd = "string" ; // acesso através de ponteiro para membro
Propósito de Classes Concretas
n
Classes concretas são usadas para definir novos tipo
(e.g. classe Date) que façam tarefas simples e de forma
eficiente.
n
Se o comportamento de uma classe concreta não é
adequado, uma nova classe deve ser construída. Se
possível utilizando a classe concreta, da mesma forma
se utiliza um int na classe Date.
n
Sempre tente definir um bom conjunto de tipos como
base da sua aplicação e defina esses tipos através de
classes concretas
Problema
n
Suponha um sistema de simulação onde cada
elemento simulado realiza uma tarefa e possui
uma representação visual dessa tarefa.
n
Cada elemento tem o comportamento de uma
tarefa e de algo visual.
n
Definimos duas classes:
¨
Task: define o comportamento relativo a execução de
uma tarefa
¨
Displayed: define o comportamento relativo a
exibição visual de informações
Solução com Herança Múltipla
n Para cada elemento simulado a sua repesentação visual (Displayed) é muito
dependente da sua tarefa (Task).
n A implementação da
simulação de um satélite pode ser feita numa única classe que herde as interfaces e implementações fornecidas pelas classes Task e
Displayed. Task dados funções Displayed dados funções Satellite dados funções
Herança Múltipla
n
Classe com herança múltipla
class Satellite : public Task, public Displayed {
// ...
};
n
É possível acessar membros das duas classes base
void f(Satellite& s) {
s.draw() ; // Displayed::draw()
s.delay(10) ; // Task::delay()
s.transmit() ; // Satellite::transmit()
Herança Múltipla
n
Objetos de classes com herança múltipla de comportam
como um objeto de cada classe base
void highlight(Displayed*) ;
void suspend(Task*) ;
void g(Satellite* p) {
highlight(p) ; // passa um ponteiro para a parte Displayed
suspend(p) ; // passa um ponteiro para a parte Task
Resolução de Ambigüidade
class Task {// ...
virtual debug_info* get_debug() ;
};
class Displayed {
// ...
virtual debug_info* get_debug() ;
};
void f(Satellite* sp) {
debug_info* dip = sp->get_debug() ; // erro: ambíguo
dip = sp->Task::get_debug() ; // ok
dip = sp->Displayed::get_debug() ; // ok
Redefinição de Membros
n
Definir na classe derivada o comportamento adequado
class Satellite : public Task, public Displayed {
// ...
debug_info* get_debug() // defefine Task::get_debug() e // Displayed::get_debug()
{
debug_info* dip1 = Task::get_debug() ;
debug_info* dip2 = Displayed::get_debug() ;
return dip1->merge(dip2) ;
} };
Classes Base Duplicadas
struct Link {Link* next; };
class Task : public Link {
// o Link é usado para manter // a lista de todas Tasks.
// ...
};
class Displayed : public Link {
// o Link é usado para manter // a lista de todos Displayed. // ... }; Task dados funções Displayed dados funções Satellite dados funções Link dados funções Link dados funções
Classes Base Duplicadas
n