Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
x
x x
para qualquer nmero x dentro da faixa de representaes possveis.
Se a relao acima vlida, pode-se garantir que 1 + 1. Ou seja, o nmero epsilon
funciona na prtica como o zero do computador. Com 4 bytes (float), epsilon da ordem de
10
-7
. Com 8 bytes (double), epsilon da ordem de 10
-16
.
Em engenharia normalmente se usa o tipo double. Alm da preciso maior, do
comportamento mais confivel quando se trabalha perto do zero, deve-se levar em
considerao que muitas contas seguidas com nmeros no exatos vo aumentando
sucessivamente o erro. A IEEE recomenda pelo menos 10 bytes para trabalhos de engenharia!
12
A velocidade de processamento praticamente a mesma para floats e doubles. O nico ponto
negativo no uso do double ao invs do float que a necessidade de memria dobra....
Como sempre, deve-se tomar cuidado quando converses so necessrias:
double d = 3.84;
int m = d + 2; // m = 5, houve perda de informao
Por default, constantes em ponto flutuante so double. Pode-se forar uma constante a ser
float usando-se 3.14f ou 3.14F ou long double usando-se 3.14l ou 3.14L.
A biblioteca <limits> contm funes para calcular os limites numricos dos tipos bsicos:
#include <iostream>
#include <string>
#include <math.h>
#include <limits>
using namespace std;
int main(int argc, char* argv[])
{
cout << "\n*** Tamanho de tipos e limites numericos ***\n\n";
cout << "\nno. bytes em short: " << sizeof(short);
cout << "\nno. bytes em int: " << sizeof(int);
cout << "\nno. bytes em long: " << sizeof(long);
cout << "\nno. bytes em char: " << sizeof(char);
cout << "\nno. bytes em bool: " << sizeof(bool);
cout << "\n\nCaracteristica ";
cout << " short int unsigned";
cout << "\n--------------------------------------------------------";
cout << "\nmenor numero ";
cout.width(8); cout << numeric_limits<short>::min();
cout.width(14); cout << numeric_limits<int>::min();
cout.width(14); cout <<numeric_limits<unsigned int>::min();
cout << "\nmaior numero ";
cout.width(8); cout << numeric_limits<short>::max();
cout.width(14); cout << numeric_limits<int>::max();
cout.width(14); cout <<numeric_limits<unsigned int>::max();
cout << "\n--------------------------------------------------------";
cout << "\n\nno. bytes em float: " << sizeof(float);
cout << "\nno. bytes em double: " << sizeof(double);
cout << "\nno. bytes em long double: " << sizeof(long double);
cout << "\n\n";
float fpi = atan(1.0)*4.0;
double dpi = atan(1.0)*4.0;
long double ldpi = atan(1.0)*4.0;
cout.precision(30);
cout << "pi (float): " << fpi << '\n';
cout << "pi (double): " << dpi << '\n';
cout << "pi (long double): " << ldpi << '\n';
cout << "\nCaracteristica ";
cout << " float double long double";
cout << "\n--------------------------------------------------------------";
cout << "\nmenor exp binario ";
13
Em um PC, intel Pentium-4, a sada do programa acima mostrada a seguir. Note que no
existe distino entre long e int, e entre double e long double!!
*** Tamanho de tipos e limites numericos ***
no. bytes em short: 2
no. bytes em int: 4
no. bytes em long: 4
no. bytes em char: 1
no. bytes em bool: 1
Caracteristica short int unsigned
--------------------------------------------------------
menor numero -32768 -2147483648 0
maior numero 32767 2147483647 4294967295
cout.width(7); cout << numeric_limits<float>::min_exponent;
cout.width(14); cout << numeric_limits<double>::min_exponent;
cout.width(14); cout <<numeric_limits<long double>::min_exponent;
cout << "\nmaior exp binario ";
cout.width(7); cout << numeric_limits<float>::max_exponent;
cout.width(14); cout << numeric_limits<double>::max_exponent;
cout.width(14); cout <<numeric_limits<long double>::max_exponent;
cout << "\n# dig. bin. mantissa";
cout.width(7); cout << numeric_limits<float>::digits;
cout.width(14); cout << numeric_limits<double>::digits;
cout.width(14); cout <<numeric_limits<long double>::digits;
cout << "\n--------------------------------------------------------------";
cout << "\nmenor exp decimal ";
cout.width(7); cout << numeric_limits<float>::min_exponent10;
cout.width(14); cout << numeric_limits<double>::min_exponent10;
cout.width(14); cout <<numeric_limits<long double>::min_exponent10;
cout << "\nmaior exp decimal ";
cout.width(7); cout << numeric_limits<float>::max_exponent10;
cout.width(14); cout << numeric_limits<double>::max_exponent10;
cout.width(14); cout <<numeric_limits<long double>::max_exponent10;
cout << "\n# dig. dec. mantissa";
cout.width(7); cout << numeric_limits<float>::digits10;
cout.width(14); cout << numeric_limits<double>::digits10;
cout.width(14); cout <<numeric_limits<long double>::digits10;
cout << "\n--------------------------------------------------------------";
cout.precision(5);
cout << "\nmenor numero ";
cout.width(14); cout << numeric_limits<float>::min();
cout.width(14); cout << numeric_limits<double>::min();
cout.width(14); cout <<numeric_limits<long double>::min();
cout << "\nmaior numero ";
cout.width(14); cout << numeric_limits<float>::max();
cout.width(14); cout << numeric_limits<double>::max();
cout.width(14); cout <<numeric_limits<long double>::max();
cout << "\n--------------------------------------------------------------";
cout << "\nepsilon(1+epsilon=1)";
cout.width(14); cout << numeric_limits<float>::epsilon();
cout.width(14); cout << numeric_limits<double>::epsilon();
cout.width(14); cout <<numeric_limits<long double>::epsilon();
cout << "\n--------------------------------------------------------------";
cout << "\n\n";
return 0;
}
14
no. bytes em float: 4
no. bytes em double: 8
no. bytes em long double: 8
pi (float): 3.1415927410125732
pi (double): 3.1415926535897931
pi (long double): 3.1415926535897931
Caracteristica float double long double
--------------------------------------------------------------
menor exp binario -125 -1021 -1021
maior exp binario 128 1024 1024
# dig. bin. mantissa 24 53 53
--------------------------------------------------------------
menor exp decimal -37 -307 -307
maior exp decimal 38 308 308
# dig. dec. mantissa 6 15 15
--------------------------------------------------------------
menor numero 1.1755e-038 2.2251e-308 2.2251e-308
maior numero 3.4028e+038 1.7977e+308 1.7977e+308
--------------------------------------------------------------
epsilon(1+epsilon=1) 1.1921e-007 2.2204e-016 2.2204e-016
--------------------------------------------------------------
Tipo caractere
char c = A;
ocupa 1 byte e pode assumir 2
8
= 256 valores diferentes. Algumas implementaes usam
nmeros de 0 at 256 (unsigned) e outras de -128 at 127. Por isso muito perigoso fazer
converso de int para char e vice-versa.
Caracteres especiais:
\n = new line \t = tab horizonatal \0 = character nulo
\\ = character \ \ = character \ = character
Tipo lgico
Bool b = true;
Ocupa 1 byte e pode ter apenas dois valores: verdadeiro ou falso.
Em converso de tipo, qualquer valor diferente de zero convertido em true.
bool b = 7; // b= true
int i = b+2; // i = 3
Tipo nulo (void)
Usado principalmente no retorno de funes que no retornam valores ou na converso
(casting) de ponteiros.
Outros tipos sero vistos posteriormente em captulo separado.
15
Comandos e expresses
Todo programa manipula os tipos atravs de linhas com comandos. Em C++, cada linha
termina com ;. Um comando pode ser:
Declarao
Atribuio
Atribuio composta
Condicional
Iterao (loop)
Salto (goto)
Chamada de funo, retorno de funo.
Declarao
int i;
Uma declarao cria um objeto de um certo tipo em um programa. Um espao na memria
alocado para o objeto e o endereo desse espao fica sendo conhecido do programa.
Em C++ no se pode usar um objeto sem antes declara-lo
A declarao pode vir em qualquer linha do programa!
double x, y, z; // declarao mltipla
int i = 3; // declarao junto com atrtibuio inicial
(inicializao)
Escopo de um objeto
Por quanto tempo um objeto existe na memria?
Existem dois escopos: local ou global:
Global = existe durante todo o programa
Local = existe dentro de um bloco delimitado por chaves correspondentes { ... } Quando a
execuo do programa sai de dentro do bloco, o objeto destrudo. O espao ocupado na
memria fica livre.
Declarao com a palavra reservada extern:
arq1.h
int x = 10;
arq2.h
extern int x;
int y = 3 + x;
16
Declarao com a palavra reservada const:
Constante, macros e funes inline
#define PI 3.1415926 // Ainda muito usado pela Microsoft
#define SQ(x) ((x)*(x)) // Macro com argumento
SQ(a+b) ser transformado em ((a+b)*(a+b))
int x = 5;
int main(int argc, char* argv[])
{
int x = 25; // OK! mesmo nome, # escopo!
int y = ::x; // :: refere-se a varivel global
cout << y << '\n'; // y = 5
{
int z = x;
cout << z << '\n'; // z = 25
int x = 38;
int t = :: x;
cout << t << '\n'; // t = 5
t = x;
cout << t << '\n'; // t = 38
// No h como acessar o x = 25 de
// dentro desse bloco, mas ele ainda no foi destrrudo!
} // Destrudos: z; x = 38; t que foram declarados
// dentro do bloco que se finda
int z = x;
cout << z << '\n'; // z = 25
return 0;
} // Todas os objetos destrudos, fim do programa.
const double pi = 3.1415926;
pi = 4; // erro
const int x; // erro, uma constante tem que ser inicializada quando criada
#define SQ(x) ((x)*(x))
#define IMPRIME_CABECALHO cout<<"\n*** Programa 1 ***\n";cout<<"Autor: Ze\n\n";
int main(int argc, char* argv[])
{
double a = 3;
double rm = SQ(a);
IMPRIME_CABECALHO
cout << a << "\n";
cout<< rm << "\n" ;
cout << "\n\n";
return 0;
}
17
Atribuio
x = y + 5;
no cria objetos novos, x j existe na memria
lvalue: x -> escreve, armazena
rvalue: y + 5 -> expresso a ser lida, avaliada
So muitas as expresses usadas em atribuies. As expresses usam diferentes operadores.
Expresses aritmticas (operadores: + - * / %):
13/4 = 3 !! (13 e 4 so duas constantes inteiras. O resultado inteiro)
Expresses relacionais (operadores: == > < >= <= !=):
int x = 5;
bool b = (x == 1); // b = false
tomar cuidado com valores prximos a zero:
double x, y;
// contas para calcular x e y
if (x == y) .... // ruim!!
if (fabs(x-y) < 1e-7) ..... // bom!!! #include <math.h>
Expresses lgicas (operadores && || ):
int i, j = 2;
bool k = ((i=0) && (j=3)); // j == 2
bool k = ((i=4) || (j=5)); // j == 2
Outras expresses e operadores: ver livros.
Atribuio composta
x += 5; // x = x + 5; x == 15
x -= 5; // x = x - 5; x == 10
x *= 5; // x = x * 5; x == 50
x /= 5; // x = x / 5; x == 10
double x = 13/4;
cout << x << '\n'; // x = 3
x = 13.0 / 4;
cout << x << '\n'; // x = 3.25 (o 4 convertido para double)
18
Devem ser preferidas, pois economizam tempo de CPU. Na atribuio normal, os valores de x
e 5 so trazidos aos registradores, operados, e o resultado armazenado no endereo de x. Na
atribuio composta, em algumas implementaes, a valor na memria e simplesmente
atualizado.
Incremento e decremento
int i = 4, j = 4;
i++; // i == 5
++j; // j == 5;
int m = i++; // m == 5, i == 6
int n = ++j; // j == 6, n == 6
m = --i; // i == 5, m == 5
n = j--; // n == 6, j == 5
Condicional
if (condio) comando
if (condio)
{
comandos ...
}
if (condio)
{
comandos ...
}
else if
{
comandos ...
}
... outros else ifs ....
else
{
comandos ...
}
double y = 5.0;
if (x == 0)
{
cout << "Denominador zero\n";
}
else
{
y /= x;
}
Switch
switch (i)
{
case 0:
// faa algo
break;
case 5:
// faa algo
break;
default:
19
//faa algo
}
Loops
for (inicializao; condio; expresso)
{
comandos
}
1. Inicializa
2. Testa a condio
3. Executa os comandos
4. Aplica a expresso
for (int i=3; i<50; i*=2)
{
cout << i << '\n';
}
for (int n=3; n>5; n*=2)
{
cout << n << '\n';
}
for (int m=3; m<50; m*=2)
{
if (m == 6) continue;
cout << m << '\n';
if (m == 24) break; // imprime 3, 12 e 24
}
m = 20; // m est no escopo!
cout << m << '\n';
int x = 0;
for ( ; ; )
{
cout << ++x << '\n';
if (x == 5) break;
}
for (double y=1.3; y<300.14; y*=2.17)
{
cout << y << '\n';
}
20
Vetores (arrays)
Para tipo qualquer T, T[n] aloca espao contnuo na memria para n objetos do tipo T. Os
objetos so indexados de 0 at n-1.
A biblioteca padro fornece tipos (valarray e vector) em que o tamanho do array pode ser
desconhecido durante a compilao. Ambos valarray e vector so vetores unidimensionais,
mas podem ser adaptados para matrizes. A biblioteca padro do C++ (distribuda em todas as
implementaes do C++) no suporta matrizes diretamente, mas existem muitas bibliotecas de
matemtica disponveis com tipos matrizes definidos. Normalmente, estes tipos so
construdos a partir do tipo valarray. Tipos especficos desenvolvidos para lidar com vetores
(valarray, vector) e matrizes so muito mais poderosos que a implementao nativa de arrays
do C++ e devem ser usados no lugar daquela.
double v[3];
v[0] = 1.0;
double x = v[0];
double a[4] = {14.5, 12.4, 11.1, 20.6}; // vale
int b[] = { 1, 2, 3, 4};
int c[2];
c = {1, 2}; // erro. Este tipo de inicializao
// atribuio s vale na inicializao
double mt[2][5];
// 2 linhas indexadas 0 e 1
// 5 colunas indexadas 0, 1, 2, 3 e 4
// Diferente do Fortran:
// 5 arrays unidimensionais de mt[0] e mt[1].
// Armazenamento linha por linha
for (int lin=0; lin<2; lin++)
{
for (int col=0; col<5; col++)
{
mt[lin][col] = 0.0;
}
}
double y[][3] = { {1,2,3}, {4,5,6} };
double ax[2][2][3] = {
{ {11, 22, 0}, {55, 0, 0} }, // ax[0]
{ {-1, -2, 0}, [-5, 6, 0} } // ax[1]
};
int n;
// calcula n durante o programa
double a[n]; // *** ERRO ***
const int m = 2000;
int k = 2*m + 100;
double a[k]; // VALE, pois k conhecido na compilao
char filename[30] = "arquivo1.txt"; // escondido '\0'
21
O tipo valarray foi otimizado para velocidade com perda (em relao ao vector) de alguma
flexibilidade, generalidade e checagem de tipo. Por exemplo, o valarray no checa se dois
vetores tm mesmo tamanho antes de som-los ou multiplic-los. Valarray no cresce
dinamicamente (exceto por copia), vector cresce.
O tipo vector foi otimizado para flexibilidade de uso, com perda de velocidade. O vector
muito poderoso, mas no deve ser preferido em clculos matemticos com vetores e matrizes
grandes pois o programa pode ficar mais lento. Para clculos matemticos, prefira valarray.
Vamos estudar o valarray melhor depois de estudarmos classes (porque o valarray uma
classe=tipo) e templados. Entretanto, aqui vai um pequeno exemplo de sua utilizao:
// #include<valarray>
int n;
cout << "n = "; cin >> n; cout << "\n\n"; // runtime
if (n)
{
valarray<double> v1(n);
valarray<double> v2(n);
for (int i=0; i<n; i++)
{
v1[i] = i - 3;
v2[i] = i * 2;
}
cout << "v1 tem " << v1.size() << " elementos: ";
for (i=0; i<n; i++)
{
cout << v1[i] << " ";
}
cout << "\n\n";
cout << "v2 tem " << v2.size() << " elementos: ";
for (i=0; i<n; i++)
{
cout << v2[i] << " ";
}
cout << "\n\n";
valarray<double> vsoma(n);
vsoma = v1 + v2;
cout << "v1 + v2 = ";
for (i=0; i<n; i++)
{
cout << vsoma[i] << " ";
}
cout << "\n\n";
valarray<double> vmul(n);
vmul = v1 * v2;
cout << "v1 * v2 = ";
for (i=0; i<n; i++)
{
cout << vmul[i] << " ";
}
cout << "\n\n";
}
else
{
cout << "\nNao existem vetores sem elementos";
}
22
Ponteiros
T* um tipo capaz de armazenar endereos de objetos do tipo T. Neste sentido, diz-se que T*
aponta ou um ponteiro para T. Por exemplo:
Ponteiros e arrays esto intimamente ligados em C++. Considere a seqncia de comandos:
int n = 100;
double* a;
a = new double [n];
O operador new cria na memria uma seqncia de n objetos do tipo T especificado (no caso
do exemplo, 100 objetos do tipo double, em seqncia). O operador new retorna um valor do
tipo T*, que aponta para o primeiro objeto da seqncia. atravs desse ponteiro que a
seqncia pode ser manipulada.
new double [n]; // como vou acessar a seqncia recm criada?
Quando n = 1, pode ser omitido, como em int* iii = new int.
Para percorrer a seqncia de n objetos, pode-se utilizar aritmtica de ponteiro ou
simplesmente o operador []:
int* pi; // p um ponteiro para inteiros, ou a varivel p de um tipo
// capaz de armazenar um endereo de alguma varivel do tipo int
int i = 5;
pi = &i; // armazena o endereo de i na varivel pi
// & operador "endereo de"
double x = 5.0;
pi = &x; // ILEGAL, pi no pode armazenar endereos de doubles
int j = 10;
pi = &j; // agora pi aponta para j e no mais para i
// O valor da varivel apontada pelo ponteiro pode ser obtido pelo uso do
// operador de "indireo" ou "de-referencia" *. Por exemplo:
int m = *pi // agora m tem o valor igual ao de j, pi continua apontando para j
// possvel tambm modificar o valor da varivel apontada usando-se o ponteiro:
*pi = 5; // agora j (a varivel apontada por pi) passa a ter o valor 5
int n;
cout << "n = "; cin >> n; cout << "\n\n";
double* a = new double [n];
for (int i=0; i<n; i++)
{
a[i] = i*3;
}
for (i=0; i<n; i++)
{
cout << *(a+i) << "\n";
}
23
A criao de vetores atravs do operador new e sua manipulao atravs de ponteiros
permitem criar vetores de tamanhos no conhecidos durante a compilao.
Objetos criados por new so globais e no so destrudos at que o programa termine ou que o
comando delete[] seja utilizado: delete [] a; ou delete iii;
A sintax T* a; equivalente a T *a; o que acaba sendo um tanto confuso....
int* i, j; equivale a int *i, j;
em ambos os casos cria-se um ponteiro de inteiros e um inteiro. (confuso e deve ser evitado)
int* ap[n] um array de n ponteiros que apontam para variveis to tipo int..
int (*ap)[n] um nico ponteiro que aponta para um array de n inteiros .....
Ponteiros mltiplos
int** ppi declara um ponteiro para um ponteiro para inteiros. Tb chamado ponteiro duplo.
int* pi;
int** ppi = π
Usando-se o operador new e o conceito de ponteiros duplos, possvel criar uma matrix de
elementos de um tipo T. Um ponteiro pode apontar para uma seqncia de valores. Se um
ponteiro aponta uma seqncia de ponteiros, sendo que cada um dos ponteiros dessa
seqncia apontam para um conjunto de valores, est formada a matriz:
pi[0] pi[0][0] pi[0][1] pi[0][2]
pi[1] pi[1][0] pi[1][1] pi[1][2]
pi (ponteiro duplo, que aponta para uma seqncia de ponteiros, cada um dos quais
aponta para uma seqncia de inteiros.)
O bloco de comandos que cria est matriz, em tempo de execuo, :
Agora a matriz est criada e pode ser acessada normalmente:
int lin;
int col;
cout << "linhas = "; cin >> lin;
cout << "colunas = "; cin >> col;
int** pi = new int* [lin]; // cria uma sequencia de "lin" ponteiros para int
// pi[0], pi[1], ..., pi[lin-1]
for (int i=0; i<lin; i++)
{
pi[i] = new int [col];
}
24
O nome de um array criado no estilo int n[5] pode ser usado como ponteiro para seu
primeiro elemento.
int n[5];
n[1] = 3;
cout << *(n+1);
Deslocamento de ponteiros podem ser teis para melhorar o entendimento de um programa,
principalmente quando se trabalha com matrizes.
int* ns = new int [6];
ns -= 1999; // ns foi deslocado em 1999 int e no aponta pra nada conhecido
ns[1999] = 60; // O elemento 1999 na verdade o primeiro elemento!!
// o antigo ns[0]
ns[2000] = 59;
//
cout << *(ns+2000);
//retorne ao antigo antes de deletar:
ns += 1999;
delete[] ns;
Ponteiros void* e nulos so muito usados em C++. Void* aponta pra um objeto de tipo
desconhecido. O ponteiro nulo no aponta para nada. Usam-se tais ponteiros em locais em
que o tipo do ponteiro no conhecido a priori.
Referncia
Existem variveis do tipo T&, denominadas referncias. So nomes alternativos.
int i;
int& r = b; // r e i so sinnimos
Uma referncia precisa ser inicializada quando declarada e no pode mudar. Usa-se para
passar argumentos para funes (ver a seguir).
for (int lin=0; lin<nlin; lin++)
{
for (int col=0; col<ncol; col++)
{
pi[lin][col] = (lin*col) + 4;
}
}
for (lin=0; lin<nlin; lin++)
{
for (int col=0; col<ncol; col++)
{
cout.width(5); cout << pi[lin][col];
// cout.width(5); cout << *(pi[lin]+col); funciona do mesmo jeito
}
cout << '\n';
}
Delete[] pi;
25
Funes
Um procedimento que recebe ou no entradas (argumentos, parmetros) e retorna ou no
algum valor.
Valor-retorno nome-da-funo(lista de parmetros ou argumentos)
// declarao ou prottipo, necessrio quando a funo chamada antes de sua definio:
double square(double);
void imprime(double);
// definio (uma nica vez)
double square(double x)
{
return (x*x);
}
void imprime(double x)
{
cout << x;
}
A funo chamada em algum lugar do programa:
// prottipos, aqui ou em um arquivo .h
main()
{
double x = 4.0;
double x2 = square(x);
imprime(x2);
}
// definies, aqui ou em outro arquivo .cpp
Overloading de funes
int square(int);
double square(double);
double a = square(5); // vai chamar int square(int)
densidade converte_para_SI(densidade);
viscosidade converte_para_SI(viscosidade);
Passagem de argumentos por valor e por referncia
int pass_val(int x)
{
x += 5; // varivel local x
return (x+10);
} // for a de escopo, o x local destrudo
26
int i = 5;
int j = pass_val(i); // i == 5, j == 20. Uma cpia de i passada.
int pass_ref(int& x)
{
x += 5; // a varivel passada modificada!
return (x+10);
}
int i = 5;
int j = pass_ref(i); // i == 10, j == 20. i foi modificado!
Ponteiros poderiam ser usados ao invs da referncia:
int pass_ref(int* x)
{
*x += 5; // a varivel para qual x aponta modificada!
return ((*x)+10);
}
int i = 5;
int* pi = &i;
int j = pass_ref(pi); // i == 10, j == 20. i foi modificado!
Matrizes e vetores so sempre passadas como ponteiros ou referencias:
int soma_elementos_matriz(int** m, int nlin, int ncol)
{
int soma = 0;
for (int lin=0; lin<nlin; lin++)
{
for (int col=0; col<ncol; col++)
{
soma += m[lin][col];
}
}
return soma;
}
Retornando valores
int* fa()
{
int local = 5;
return &local; // erro. Varivel destruda ao final da funo
}
int* f()
{
int* local = new int; // com new, O endereo existe fora da funo
*local = 5;
return local; // OK!
}
27
A funo f() seria chamada por outra funo do seguinte modo:
{
int* pi = f();
int x = *pi;
cout << x;
delete pi; // No esquea de livrar o espao na memria depois que
// no precisar mais do ponteiro criado com new
}
Uma funo so pode retornar um valor de um certo tipo. Entretanto, pensar que isso uma
limitao um erro. O tipo retornado pode ser um tipo definido pelo usurio, que, por
exemplo, poderia conter inmeros valores simples. O valor retornado pode ser uma matriz
inteira ou um vetor. Considere a multiplicao de uma matriz por um vetor:
#include <iostream>
using namespace std;
double* ma_x_ve(double** ma, double* ve, int n, int m)
{
double* resultado = new double [n];
for (int i=0; i<n; i++)
{
resultado[i] = 0.0;
for (int j=0; j<m; j++)
{
resultado[i] += ma[i][j] * ve[j];
}
}
return resultado;
}
int main()
{
int n = 2;
int m = 3;
// Cria a matriz
double** ma = new double* [n];
for (int i=0; i<n; i++) ma[i] = new double [m];
// Cria o vetor
double* ve = new double [m];
// Atribui valores a matriz e ao vetor
for (i=0; i<n; i++)
{
for (int j=0; j<m; j++)
{
ma[i][j] = j;
}
}
for (int j=0; j<m; j++)
{
ve[j] = j;
}
28
Funes com argumentos default
int func (int a, int b = 5, int c = 2);
Operadores como funes
Um operador (exemplo, +) pode ser visto como uma funo que recebe parmetros, tem um
nome, e retorna um valor.
double operator+ (double a, double b);
// Calcula o produto e imprime na tela
double* c = ma_x_ve(ma, ve, n, m);
for (i=0; i<n; i++)
{
cout << "\n" << c[i];
}
// Livra a memria na ordem inversa de criao para ponteiros multiplos
for (i=0; i<n; i++) delete[] ma[i];
delete[] ma;
delete[] ve;
delete[] c;
cout << "\n\n";
return 0;
}
29
Ponteiros para funes ( + typedef)
using namespace std;
typedef double tipo;
typedef tipo (*pfx)(tipo);
tipo trapezoidal(tipo a, tipo b, pfx f, int n);
tipo simpson(tipo a, tipo b, pfx f, int n);
tipo funcao(tipo x)
{
return x*x*x;
}
int main(int argc, char* argv[])
{
tipo xi = -1;
tipo xf = 2;
tipo n = 100;
tipo resultado_trape = trapezoidal(xi, xf, funcao, n);
tipo resultado_simps = simpson(xi, xf, funcao, n);
cout << "*** Calculo de Integrais ***" <<"\n\n";
cout.precision(30);
cout << "Integral pelo metodo trapezoidal, com n = " << n << ": " <<
resultado_trape;
cout << "\n";
cout << "Integral pelo metodo de simpson, com n = " << n << ": " <<
resultado_simps;
cout << "\n\n";
return 0;
}
tipo trapezoidal(tipo a, tipo b, pfx f, int n)
{
tipo h = (b-a)/n;
tipo soma = (f(a)+f(b))*0.5;
for (int i=1; i<n; i++) soma += f(a+i*h);
return (soma*h);
}
tipo simpson(tipo a, tipo b, pfx f, int n)
{
tipo h = (b-a)/n;
tipo soma = (f(a)+f(b))*0.5;
for (int i=1; i<n; i++) soma += f(a+i*h);
tipo soma_meio = 0.0;
for (i=1; i<=n; i++) soma_meio += f(a+(i-0.5)*h);
return (soma+2*soma_meio)*h/3.0;
}
30
Templado para funes
template<class T> T trapezoidal(T a, T b, T (*f)(T), int n);
template<class T> T simpson(T a, T b, T (*f)(T), int n);
Podemos reescrever nossa biblioteca eq.h e eq.cpp de modo a trabalhar com tipos genricos.
O templado uma maneira de passar para uma funo argumentos referentes ao tipo dos
parmetros e do valor de retorno. Vrios parmetros podem ser passados, e no apenas 1,
como no exemplo acima.
O compilador faz o trabalho e gera automaticamente todas as funes que sero usadas
durante a execuo do programa. Portanto, no h nenhuma sobrecarga durante a execuo.
Isso tem sido aproveitado em programas de engenharia para otimizar os clculos (veremos
adiante como podemos eliminar o ponteiro para a funo usando classes e templados).
Perigo: nem todos os tipos vo funcionar nas definies de trapezoidal e simpsom (por
exemplo, tipos int ou string). Tente com int e veja se o programa fornece um resultado.
Funes _AXPY (ax+y, x e y vetores, a escalar). S (real), D (duplo), C e Z (complexos).
Argumentos da funo main()
Exerccios:
Reorganize a biblioteca do deq (tente usar templados e veja o que acontece) e compile uma
DLL. Inclua na biblioteca uma funo para achar a raiz de uma funo pelo mtodo da
biseco.
Escreva um programa para calcular integrais definidas e razes de funo usando a DLL.
#include <iostream>
#include <stdlib.h>
int main(int argc, char* argv[])
{
if (argc != 3)
{
std::cout << "\nNumero de parametros incorreto\n";
}
else
{
int n = atoi(argv[1]);
int m = atoi(argv[2]);
std::cout << "\nSoma = " << (m+n) << "\n";
}
std::cout << "\n\n";
return 0;
}
31
namespace deq_ufscar
{
typedef double (*pfx)(double);
// Funo para achar uma raiz de f(x) pelo mtodo da biseco
// Verso com chamada recursiva
// Extrada do livro de Yang
// Incluida por Z Antnio em 14/01/2004
// a, b Pontos inicial e final do intervalo de busca
// f f(x) definida em [a, b] ou [b, a]
// u = f(a)
// delta Tolerancia da raiz, a funo retorna quando o intervalo
// menor que delta
// epsn Tolerancia residual, a funo retorna quando o residuo
// menor que epsn
// maxit No. maximo de iteraes permitidas
// itern No. de iteraes at o momento. Deve ser inicializado
// para 1 toda vez que a funo chamada em main
double bisectionr(double a, double b, pfx f, double u, double delta,
double epsn, int maxit, int& itern);
// Outras funes
} // namespace deq_ufscar
double deq_ufscar::bisectionr(double a, double b, pfx f, double u,
double delta, double epsn, int maxit,
int& itern)
{
double e = (b-a)*0.5; // diminue o tamanho do intervalo pela metade
double c = a + e; // pega o ponto do meio do intervalo
double w = f(c); // o valor da funo no ponto do meio
// testa condio de parada
if ( (fabs(e)<delta) || (fabs(w)<epsn) || itern++ > maxit) return c;
// escolhe um dos dois intervalos criados pela diviso no meio
// o teste u.v <0 poderia causar overflow ou underflow!
((u>0 && w<0) || (u<0 && w>0)) ? (b=c) : (a=c, u=w);
// chama a funo novamente com o novo intervalo
return deq_ufscar::bisectionr(a, b, f, u, delta, epsn, maxit, itern);
}
32
Criando novos tipos em C++
Se voc tivesse que construir um carro, que conjunto de ferramentas voc preferiria:
Conjunto A Conjunto B
motor chapas de ao
chassis tarugos de borracha
pneus folhas de plstico
Vale a pena escrever tipos especficos quando eles sero reutilizados...
C++: programas e projetos grandes, desenvolvidos em equipe
Exemplos de tipos especficos:
Soldados em jogos como Comandos (move, chuta, tem vida,...);
Nmeros complexos;
Matrizes
Grandezas fsicas: densidade, viscosidade
Vetores no espao 3D pra manipular velocidades, foras, ...
Substncia qumica (fuso, ebulio, eq. estado, toxicidade, formula molecular, ...);
Tubulaes (rugosidade, comprimento, material, ...);
Reator;
Estgio de equilbrio (composio das correntes de entrada e sada, critrio de
equilbrio, condies operacionais (T e P), balano de massa e energia, ...)
Como lidar com um reator ou com estgios de equilbrio? 2 abordagens: Funes
(procedimentos) (possivelmente em um mdulo), ou um objeto? Programao orientada a
procedimentos ou a objetos? C++ pensa em objetos. Fortran em procedimentos.
Tipos bsicos
Aplicao
especfica
C, Basic, Fortran
Tipos especficos
C++
33
C++ tem 3 mecanismos para construo de novos tipos:
Enumerado
Estrutura
Classe
Enumerados
Objeto que pode assumir um conjunto pequeno de valores.
Sintaxe: enum nome-do-tipo-novo {valor1, valo2, ..., valorn};
Exemplo:
Observaes:
Cada valor recebe um nmero inteiro, a partir do zero;
Pode-se mudar a atribuio do valor inteiro (SIMPSON =20);
Cuidado na converso de enum a inteiros e vice-versa;
Nomes utilizados (TRAPEZOIDAL) no podem ser reutilizados no escopo do enum;
Operaes definidas automaticamente: Atribuio e comparao
mid m = SIMPSON (atribuio)
if (m == SIMPSON) ..... (comparao)
enum mdi {TRAPEZOIDAL, SIMPSON, RUNGE_KUTTA2}:
template<class T> T integra(T (*f)(T), T a, T b, int n=100, mdi m = SIMPSON)
{
switch (m)
{
case TRAPEZOIDAL:
{
// ....
}
break;
case SIMPSON:
{
// ....
}
break;
//.....
case default:
{
cout << "Houve algum erro.";
// ...
}
}
}
34
Sobrecarga de operadores:
Qual seria o resultado de cout << m ou cin >> m ??
#include <iostream>
#include <string>
using namespace std;
enum mdi {TRAPEZOIDAL, SIMPSON, RUNGE_KUTTA2, MDI_NAO_ESPECIFICADO};
ostream& operator<<(ostream& os, mdi valor);
istream& operator>>(istream& is, mdi& valor);
int main()
{
mdi m;
cout << "Metodo de integracao: ";
cin >> m;
cout << "\nVoce escolheu o metodo: " << m;
cout << "\n\n";
return 0;
}
ostream& operator<<(ostream& os, mdi valor)
{
switch (valor)
{
case TRAPEZOIDAL:
{
os << "trapezoidal";
}
break;
case SIMPSON:
{
os << "Simpson";
}
break;
case RUNGE_KUTTA2:
{
os << "Runge-Kutta de segunda ordem";
}
break;
default:
{
os << "Metodo nao especificado";
}
}
return os;
}
istream& operator>>(istream& is, mdi& valor)
{
string ValorDigitado;
bool TenhoValor = false;
while (!TenhoValor)
{
is >> ValorDigitado;
TenhoValor = true;
if (ValorDigitado == "trapezoidal" ||
ValorDigitado == "Trapezoidal" ||
ValorDigitado == "TRAPEZOIDAL")
valor = TRAPEZOIDAL;
else if
(ValorDigitado == "simpson" ||
ValorDigitado == "Simpson" ||
ValorDigitado == "SIMPSON")
valor = SIMPSON;
else if
(ValorDigitado == "runge_kutta2" ||
ValorDigitado == "Runge-Kutta2" ||
ValorDigitado == "RUNGE_KUTTA2")
valor = RUNGE_KUTTA2;
else if
(ValorDigitado == "exit")
valor = MDI_NAO_ESPECIFICADO;
else
{
cout << "Nao conheco este metodo.\n"
cout << "Tente novamente ou digite \"exit\" para sair:";
TenhoValor = false;
}
}
return is;
}
35
Estruturas
Como mantemos estruturas agrupadas em Fortran?
O ponto . um operador.
Os operadores de atribuio e de cpia so gerados automaticamente pelo compilador.
Podemos definir novos operadores para a estrutura ou definir funes para manipulao da
estrutura.
Raramente se usa estruturas em C++, pois as classes fazem tudo que as estruturas fazem, e
muito melhor.
#include <string>
#include "data.h"
#include "tel.h";
struct aluno // Nenhum "aluno" (objeto) criado aqui
{
int RA;
string nome;
string endereco;
Tel telefone; // Tel e Data so outros tipos definidos pelo usurio
Data nascimento;
}; // No esquea do ponto e virgula aqui.
aluno* a = new aluno[10000]; // 10000 objetos do tipo aluno criados
a[547].nome = "Jose da Silva";
cout << a[547].nome;
a[548] = a[547];
36
Classes
Define um tipo e como objetos deste tipo se comportam.
Partes pblicas e privadas
Construtores (quantos desejar) e um destruidor
Funes membro, funes amigas, funes normais
Operadores so sobrecarregados
// arquivo classpoint.h
class point2d
{
// Variveis membro
private:
double x; // coordenada x
double y; // coordenada y
public:
// Construtores
// Default
point2d() {x=0; y=0;}
// Tpico
point2d(double px, double py = 0) {x=px; y=py;}
// Construtor de cpia (gerado automaticamente)
point2d(const point2d& p) {x=p.x; y=p.y;}
// Destruidor
~point2d() { }
// Outras funes membro
public:
// Muda as coordenadas do ponto
void move(double nx, double ny) { x=nx; y=ny; }
void move(point2d np);
private:
// Funes membro podem ser pblicas ou privadas
// Outras funes amigas
public: // funes amigas podem estar na parte publica
// ou privada, indiferente
friend double norm(point2d p);
// Operadores
// atribuio (gerado automaticamente) =
// update += -= *= /= %=
// incremento ++ --
// arit binrio + - * / %
// arit unrio + -
// booleano ! && ||
// comparao == != < <= > >=
// Entrada/Sada >> <<
friend istream& operator>>(istream&, point2d&);
friend ostream& operator<<(ostream&, point2d);
};
// Note o ponto-e-virgula depois da chave
37
A classe (tipo) point2d pode ser usado da seguinte maneira:
// arquivo classpoint.cpp
#include <cmath>
#include "classpoint.h"
// Muda as coordenadas do ponto
/*void point2d::move(double nx, double ny)
{
x = nx;
y = ny;
} */
void point2d::move(point2d np)
{
x = np.x;
y = np.y;
}
// Calcula a distncia at a origem
inline double norm(point2d p)
{
return (sqrt(p.x*p.x + p.y*p.y));
}
// Operadores
// Entrada
istream& operator>>(istream& in, point2d& p2d)
{
double x, y;
in >> x;
in >> y;
p2d.move(x, y);
return in;
}
// Sada
ostream& operator<<(ostream& out, point2d p2d)
{
out << p2d.x << ' ' << p2d.y;
return out;
}
// arquivo main.cpp, programa para utilizar a classe point2d
#include <iostream>
#include <fstream> // para arquivos
#include <string>
#include <ctime>
#include "classpoint.h"
using namespace std;
int main()
{
point2d a(2,3);
point2d b, c, d;
b = point2d(3,4);
38
c.move(4.0,5.0);
d = c;
point2d e = a; // Construtor de cpia
cout << a << '\n';
cout << b << '\n';
cout << c << '\n';
cout << d << '\n';
cout << e << '\n';
// cout << "\nEntre as coordenadas x e y de um ponto:\n";
// cin >> d;
// cout << "\nO ponto d vale agora: " << d << "\n";
int n = 10;
point2d* f = new point2d[n];
cout << "\nPontos inicializados com " << f[n-1] << "\n\n";
for (int i=0; i<n; i++)
{
double x = 1.0 * i;
double y = x*x;
f[i].move(x, y);
cout << f[i] << '\n';
}
// imprime o vetor de pontos para um arquivo
/* string fn_cmm;
cout << "\nNome do arquivo para salvar resultados: ";
cin >> fn_cmm;
const char* fn_c = fn_cmm.c_str();
ofstream fs(fn_c, ios_base::out); // abre arquivo para escrita
fs << "Resultados - Funo f(x) = x*x\n\n";
for (i=0; i<n; i++)
{
fs << f[i] << '\n';
}
fs.close(); // no esquea de fechar o arquivo aberto
*/
delete[] f;
// Tempo gasto com os diferentes mecanismos de inicializao
const double CTPS = 1700.0; // CLOCK_TICKS_PER_SECOND
long tm0 = time(0); // calendar time
long ck0 = clock(); // CPU time
int lin = 1000;
int col = 25000;
point2d** f2 = new point2d* [lin];
for (i=0; i<lin; i++)
{
f2[i] = new point2d [col];
double x = 1.0 * i;
double y = x*x;
for (int j=0; j<col; j++)
{
f2[i][j].move(x, y);
// f2[i][j] = point2d(x, y);
}
}
39
Observaes
Diferena entre interface e implementao (information hidding)
Manuteno: mudanas nas variveis ou na implementao pode no afetar a interface
Encapsulao
Se no for includo, o compilador tenta gerar automaticamente 4 funes: construtor default,
construtor de cpia, destruidor, e operador de atribuio.
Dica: Nunca defina construtores de cpia ou operadores de atribuio (prefira os gerados
automaticamente) exceto quando h ponteiros entre as variveis-membros da classe. Nestes
casos, as cpias ou atribuies geradas automaticamente pelo compilador ir copiar o
ponteiro, o que significa que dois objetos da classe vo apontar para o mesmo endereo, o que
no correto. Nestes casos, defina seu prprio construtor de cpia e operador de atribuio.
T :: T(const T&); // construtor de cpia
T& T::operador==(const T&); // operador de atribuio
Uma funo amiga pode ser amiga de muitas classes.
Variveis estticas: somente uma por tipo em todo o programa, ao invs de uma por tipo
objeto criado no programa. Usos: valor default
Construo eficiente de objetos de uma classe quando h objetos de outras classes como
membros:
line::line(point2d a, point2d b, bool dir) : p1(a), p2(b)
{
direcao = dir;
}
Se uma classe no tem construtores ou tem construtor default, pode-se criar um vetor de
objetos da classe: point2d a[100];
T (X::*pf)(T) um ponteiro para uma funo membro da classe X
Exerccios: classe para integrao, classe para EDOs.
long tm1 = time(0); // calendar time
long ck1 = clock(); // CPU time
cout << "\nWall time: " << difftime(tm1, tm0) << " segundos";
cout << "\nCPU time: " << double (ck1-ck0)/CTPS << " segundos";
for (i=0; i<lin; i++) delete[] f2[i];
delete[] f2;
cout << "\n\n";
return 0;
}
40
Mais a respeito de sobrecarga de operadores
Existem muitas maneiras diferentes de se definir operadores para classes. Um operador
binrio, por exemplo, para realizar a operao z1 + z2 (retornando um valor sem modificar os
valores de z1 e z2), pode ser definido como uma funo membro da classe, ou amiga, ou at
uma funo normal. Os argumentos z1 e z2 podem ser passados por valor ou por referncia.
T operator+(T a, T b); // funo normal ou amiga, passagem por valor
T operator+(T& a, T& b); // funo normal ou amiga, passagem por referncia
T X::operator+(T b); // funo membro, a+b chama a.operator+(b);
T X::operator+(T& b); // funo membro, a+b chama a.operator+(& b);
Vamos escrever uma classe para complexos e examinar as diferenas entre estas opes.
Como lidar com expresses do tipo z = 2 + z1 ou z = z1 + 2?
Desvantagem de definir operator+ como membro;
Vantagem em se definir operaes com tipos misturados, para evitar converses.
Converso: um construtor com um argumento define uma regra de converso:
complex (double a);
Ex: escreva classes para vetores e matrizes, e fornea um conjunto grande de operadores.
Discutindo a questo da eficincia do programa cientfico, em termos de sobrecarga de
operadores. Se x, y e z so vetores e a um escalar, efetue a seguinte operao:
z = a * x + y; ou saxpy(a, x, y, & z); ???
Templados (Gabaritos)
Templados podem ser aplicados a classes inteiras, e no somente a funes.
O templado uma maneira de permitir que um ou mais tipos sejam passados como
parmetros para funes ou classes.
Templados no geram informaes durante a execuo do programa!!!! Um compilador
precisa saber de antemo quais verses de um templado sero usadas durante o programa. O
compilador ento gera vrias cpias do templado, uma para cada verso que ser utilizada.
Isso chamado de polimorfismo em tempo de compilao (polimorfismo em tempo de
execuo possvel atravs do conceito de hierarquia de classes, e ser visto posteriormente).
Polimorfismo em tempo de compilao no gera absolutamente nenhuma sobrecarga de
tempo de execuo de um programa. Ao contrrio, templados podem ser usados para
melhorar muito a eficincia dos programas, como veremos.
Para aprender como se usam templados em uma classe, como as funes membro e amigas da
classe so declaradas e definidas, e como so chamadas em um programa, vamos examinar
uma classe para vetores na forma de templados:
41
// arquivo vrttemplate.h
//
#include <iostream>
#include <cmath>
using namespace std;
/* **********
CLASSE vrt
********** */
template<class T> class vtr // (ou template<typename T> class vtr)
{
// *** Variveis membro ***
private:
unsigned int tam; // tamanho do vetor
T* elems; // ponteiro para os elementos do vetor
public:
// *** Construtores ***
// Default
vtr<T>(unsigned int t=0, T d=0); // todos elems = d
// Tpico
vtr<T>(unsigned int, T*); // inicializa com valores fornecidos
// De cpia (aqui necessrio pois construo usa operador "new")
vtr<T>(const vtr<T>& v);
// *** Destrutor ***
~vtr<T>() { delete[] elems; }
// *** Funes para acesso direto as variveis membro ***
unsigned int size() const { return tam; }
// *** Outras funes membro ***
// aumenta ou diminui um vetor
// retorna o maior elemento em valor absoluto
T maxnorm() const;
// retorna a raiz quadrada da soma dos quadrados dos elementos
T twonorm() const;
// Produto interno entre dois vetores
friend T dot(const vtr<T>&, const vtr<T>&);
// *** Operadores ***
// atribuio = (aqui no gerado automaticamente)
vtr<T>& operator=(const vtr<T>&);
// update += -= *= /= %=
vtr<T>& operator+=(const vtr<T>&);
vtr<T>& operator-=(const vtr<T>&);
// incremento ++ --
// arit unrio + -
friend vtr<T> operator+(const vtr<T>&);
friend vtr<T> operator-(const vtr<T>&);
// arit binrio + - * / %
friend vtr<T> operator+(const vtr<T>&, const vtr<T>&);
friend vtr<T> operator-(const vtr<T>&, const vtr<T>&);
friend vtr<T> operator*(T, const vtr<T>&);
friend vtr<T> operator*(const vtr<T>&, T);
friend vtr<T> operator*(const vtr<T>&, const vtr<T>&);
friend vtr<T> operator/(const vtr<T>&, T);
42
// booleano ! && ||
// comparao == != < <= > >=
// subscrito [] ()
T& operator[](int i) const { return elems[i]; }
// Entrada/Sada >> <<
// friend istream& operator>>(istream&, vtr<T>&);
friend ostream& operator<<(ostream&, vtr<T>&);
};
// Funo auxiliar (no pertencente a classe) para reportar erros.
void error(char* v)
{
cout << v <<" Programa terminou.\n\n";
exit(1);
}
/* ****************************
Implementao da classe vrt
**************************** */
// Construtor default com todos elems = d
template<class T> vtr<T>::vtr<T>(unsigned int t, T d)
{
elems = new double [tam = t];
for (unsigned int i=0; i<tam; i++) elems[i] = d;
}
// Construtor Tpico - inicializa com valores fornecidos
template<class T> vtr<T>::vtr<T>(unsigned int t, T* e)
{
elems = new T [tam = t];
for (unsigned int i=0; i<tam; i++) elems[i] = *(e + i);
}
// Construtor de cpia (aqui necessrio pois construo usa operador
"new")
template<class T> vtr<T>::vtr<T>(const vtr<T>& v)
{
elems = new T [tam = v.tam];
for (unsigned int i=0; i<tam; i++) elems[i] = v[i];
}
// *** Operadores ***
// atribuio =
template<class T> vtr<T>& vtr<T>::operator=(const vtr<T>& v)
{
if (this != &v) // cuidado com self-assignment
{
if (tam != v.tam) error("Vetores de tamanhos diferentes em
atribuio.");
for (unsigned int i=0; i<tam; i++) elems[i] = v[i];
}
return *this;
}
// update += -= *= /= %=
template<class T> vtr<T>& vtr<T>::operator+=(const vtr<T>& v)
{
if (tam != v.tam) error("Vetores de tamanhos diferentes em operao
+=.");
43
for (unsigned int i=0; i<tam; i++) elems[i] += v[i];
return *this;
}
template<class T> vtr<T>& vtr<T>::operator-=(const vtr<T>& v)
{
if (tam != v.tam) error("Vetores de tamanhos diferentes em operao -
=.");
for (unsigned int i=0; i<tam; i++) elems[i] -= v[i];
return *this;
}
// arit unrio + -
template<class T> vtr<T> operator+(const vtr<T>& v) { return v; }
template<class T> vtr<T> operator-(const vtr<T>& v) { return
(vtr<T>(v.tam) - v); }
// arit binrio + - * / %
template<class T> vtr<T> operator+(const vtr<T>& v1, const vtr<T>& v2)
{
if (v1.tam != v2.tam) error("Vetores de tamanhos diferentes em soma.");
vtr<T> temp = v1; // causaria erro com construtor de copia automtico
pois
temp += v2; // teramos dois ponteiros apontando para os mesmos
elems
return temp; // temp destrudo aqui
}
template<class T> vtr<T> operator-(const vtr<T>& v1, const vtr<T>& v2)
{
if (v1.tam != v2.tam) error("Vetores de tamanhos diferentes em soma.");
vtr<T> temp = v1;
temp -= v2;
return temp;
}
template<class T> vtr<T> operator*(T s, const vtr<T>& v)
{
vtr<T> temp(v.tam);
for (unsigned int i=0; i<v.tam; i++) temp[i] = s * v[i];
return temp;
}
template<class T> vtr<T> operator*(const vtr<T>& v, T s)
{
return(s * v);
}
template<class T> vtr<T> operator*(const vtr<T>& v1, const vtr<T>& v2)
{
if (v1.tam != v2.tam) error("Vetores de tamanhos diferentes em
multiplicao.");
unsigned int n = v1.tam;
vtr<T> temp(n);
for (unsigned int i=0; i<n; i++) temp[i] = v1[i] * v2[i];
return temp;
}
template<class T> vtr<T> operator/(const vtr<T>& v, T s)
{
if (!s) error("Diviso por zero em diviso de vetor por escalar.");
return ((1.0/s)*v);
}
// Entrada/Sada >> <<
// friend istream& operator>>(istream&, vtr<T>&);
44
template<class T> ostream& operator<<(ostream& os, vtr<T>& v)
{
for (unsigned int i=0; i<v.tam; i++)
{
os << v[i] << " ";
if (i%5 == 4) os << "\n"; // imprime 5 elems em 1 linha
}
return os;
}
// *** Outras funes membro ***
// retorna o maior elemento em valor absoluto
template<class T> T vtr<T>::maxnorm() const
{
T norm = fabs(elems[0]);
// for (unsigned int i=1; i<tam; i++) norm = max (norm, fabs(elems[i]);
// max() em <algorithm>
for (unsigned int i=1; i<tam; i++)
{
T temp = fabs(elems[i]);
if (norm < temp) norm = temp;
}
return norm;
}
// retorna a raiz quadrada da soma dos quadrados dos elementos
template<class T> T vtr<T>::twonorm() const
{
T norm = elems[0] * elems[0];
for (unsigned int i=1; i<tam; i++) norm += (elems[i]*elems[i]);
return sqrt(norm);
}
// Produto interno entre dois vetores
template<class T> T dot(const vtr<T>& v1, const vtr<T>& v2)
{
if (v1.tam != v2.tam) error("Vetores de tamanhos diferentes na funo
dot.");
T temp = v1[0] * v2[0];
for (unsigned int i=1; i<v1.tam; i++) temp += v1[i] * v2[i];
return temp;
}
Se a funo main() contm:
vtr<float> fv(20);
vtr<double> dv(500);
vtr<double> dv2(500, 3.14);
dv += dv2;
O compilador ir gerar veres da classe vetor para float e double, e somente os construtores,
destruidores, operadores e funes que esto sendo chamados pelo programa. Funes no
usadas no so geradas pelo compilador.
45
Abaixo um exemplo de uma funo main() que utiliza o templado definido acima:
Especializaes so possveis para tipos particulares. Por exemplo, se T um complexo, as
funes para norma mxima e a funo amiga dot no funcionaro corretamente. Veja os
livros para aprender como definir especializaes para estes casos.
No exemplo acima, apenas um tipo foi passado como argumento pelo templado. possvel
passar mais de um argumento:
template<typename T1, typename T2, typename T3> uma classe ou funo.
// arquivo vtrtemplatemain.cpp
//
#include "matrixtemplate.h"
using namespace std;
int main()
{
int m = 10;
double* v = new double [m];
for (int i=0; i<m; i++) v[i] = i * i + 10;
vtr<double> v1(m, v); // cria v1 de v
cout << v1 << "\n";
vtr<double> vv; // vetor vazio
int s = vv.size();
cout << "vv.tam = " << s << "\n\n";
vtr<double> v2(m); // m elementos iguais a zero
for (i=0; i<m; i++) v2[i] = 5 * i - 10;
cout << v2 << "\n";
vtr<double> v3(m, 5.8);
vtr<double> v4 = - v1 + 3.3 * v3;
cout << (- v1 + 3.3 * v3) << "\n";
v4 += v2;
cout << v4 << "\n";
vtr<double> v5 = - v1 * v4;
cout << v5 << "\n";
double a = dot(v1, v5);
cout << "dot(v1,v5) = " << a << "\n";
cout << "maxnorm(v1) = " << v1.maxnorm() << "\n";
cout << "twonorm(v1) = " << v1.twonorm() << "\n";
cout << "\n";
delete[] v;
cout << "\n\n";
return 0;
}
46
possvel passar outros parmetros (alm de tipos) para classes e funes atravs de
templados:
Esta tcnica pode ser usada para trabalhar com grandezas fsicas dimensionais. Abaixo uma
idia de como fazer isso, baseado no livro de Barton. O texto abaixo no completo e no vai
funcionar como est. Mais desenvolvimento necessrio para torn-lo til.
template<class C, class T, C val, int i> class MinhaClasse // 4
argumentos
{
private:
T v[i];
int sz;
C d = val;
public:
MinhaClasse() : zi(i) { ... }
};
// m = massa
// l = comprimento
// t = tempo
// q = carga
// k = temperatura
// i = intensidade luminosa
// a = angulo
template<int m, int l, int t, int q, int k, int i, int a> class physical
{
private:
double val;
public:
// Construtor default
physical() : val(1.0) { }
public:
double& value() { return val; }
const double& value() const { return val; }
physical& setToZero() { val = 0.0; return *this; }
// a funo abaixo existe para uma quantidade fsica unitria com dimenses
static physical unit() { return physical(); }
//Operadores
physical& operator+=(const physical& rhs) { val+=rhs.val; return *this;}
physical& operator-=(const physical& rhs) { val-=rhs.val; return *this;}
physical& operator*=(double s) { val *= s; return *this; }
physical& operator/=(double s) { val /= s; return *this; }
};
template<int m, int l, int t, int q, int k, int i, int a>
physical<m, l, t, q, k, i, a>
operator*(double s, const physical<m, l, t, q, k, i, a>& fs)
{ return (physical<m, l, t, q, k, i, a>::unit() *= s); }
47
typedef physical<1, 0, 0, 0, 0, 0, 0> mass;
typedef physical<0, 1, 0, 0, 0, 0, 0> length;
typedef physical<0, 0, 1, 0, 0, 0, 0> time;
typedef physical<0, 0, 0, 1, 0, 0, 0> charge;
typedef physical<0, 0, 0, 0, 1, 0, 0> temperature;
typedef physical<0, 0, 0, 0, 0, 1, 0> intensity;
typedef physical<0, 0, 0, 0, 0, 0, 1> angle;
typedef physical<0, 1, -1, 0, 0, 0, 0> velocity;
typedef physical<0, 1, -2, 0, 0, 0, 0> acceleration;
typedef physical<1, 1, -2, 0, 0, 0, 0> force;
typedef physical<1, 2, -2, 0, 0, 0, 0> energy;
typedef physical<1, -3, 0, 0, 0, 0, 0> density;
typedef physical<1, -1, -1, 0, 0, 0, 0> viscosity;
const mass kg;
const length m;
const time s;
// Funes para multiplicar e dividir objetos da classe physical
//
*************************************************************************
template<int m1, int l1, int t1, int q1, int k1, int i1, int a1,
int m2, int l2, int t2, int q2, int k2, int i2, int a2>
physical<m1+m2, l1+l2, t1+t2, q1+q2, k1+k2, i1+i2, a1+a2>
operator*(physical<m1, l1, t1, q1, k1, i1, a1> lhs,
physical<m2, l2, t2, q2, k2, i2, a2> rhs)
{
return ((physical<m1+m2, l1+l2, t1+t2, q1+q2, k1+k2, i1+i2,
a1+a2>::unit()
* lhs.value()) * rhs.value());
}
template<int m1, int l1, int t1, int q1, int k1, int i1, int a1,
int m2, int l2, int t2, int q2, int k2, int i2, int a2>
physical<m1-m2, l1-l2, t1-t2, q1-q2, k1-k2, i1-i2, a1-a2>
operator/(const physical<m1, l1, t1, q1, k1, i1, a1> lhs,
const physical<m2, l2, t2, q2, k2, i2, a2> rhs)
{
return (physical<m1-m2, l1-l2, t1-t2, q1-q2, k1-k2, i1-i2, a1-
a2>::unit()
* (lhs.value() / rhs.value()));
}
//
*************************************************************************
int main()
{
// density ro = 5 * kg; erro de compilao
mass m1 = 5 * kg;
mass m2 = 2 * kg;
physical<2,0,0,0,0,0,0> mq = m1 * m2;
// acceleration a = 9.8 * m / (s * s);
// force f = m * a;
cout << "\n\n"; return 0;
}
48
Uma vez que templados no geram sobrecarga em tempo de execuo, podem ser usados para
otimizar procedimentos de clculo numrico. Por exemplo, uma rotina de integrao que
deveria receber um ponteiro da funo a ser integrada, pode ser definida com um templado, e
o compilador vai gerar a ligao entre a funo a ser integrada e a rotina de integrao durante
a compilao, evitando mltiplas chamadas a funo a ser integrada durante a execuo.
// arquivo integraEff.h
// Contm a funo de integrao simpsom definida de diversas maneiras
// para testes de eficiencia
typedef double tipo;
typedef tipo (*pfx)(tipo);
// *****************************************************************
tipo simpson(tipo a, tipo b, pfx f, int n)
{
// Observe quantas vezes esta rotina chama a funo a ser integrada.
// Gasta-se tempo no s para fazer a conta, mas tambm para chamar a
// funo. Se a funo pudesse ser escrita aqui dentro,
// economizaramos tempo de execuo. Isso pode ser feito, sem
// sacrifcio da generalidade, atravs do uso de templados.
tipo h = (b-a)/n;
tipo soma = (f(a)+f(b))*0.5;
for (int i=1; i<n; i++) soma += f(a+i*h);
tipo soma_meio = 0.0;
for (i=1; i<=n; i++) soma_meio += f(a+(i-0.5)*h);
return (soma+2*soma_meio)*h/3.0;
}
// *****************************************************************
template<class T, class Fo> simpsonE1 (T a, T b, Fo f, int n)
{
T h = (b-a)/n;
T soma = (f(a)+f(b))*0.5;
for (int i=1; i<n; i++) soma += f(a+i*h);
T soma_meio = 0.0;
for (i=1; i<=n; i++) soma_meio += f(a+(i-0.5)*h);
return (soma+2*soma_meio)*h/3.0;
}
// *****************************************************************
template<class T> class integralF
{
T inferior;
T superior;
T (*func)(T);
public:
// construtor
integralF(T a, T b, T (*f)(T)) {inferior = a; superior = b; func = f;}
// Funes auxiliares
void mudalimites(T an, T bn) {inferior = an; superior = bn; }
// funes para integrao
T simpsonFM(int n);
// acrescente outras funes ...
};
template<class T> T integralF<T>::simpsonFM(int n)
{
T h = (superior - inferior)/n;
T soma = (func(inferior)+func(superior))*0.5;
for (int i=1; i<n; i++) soma += func(inferior+i*h);
49
T soma_meio = 0.0;
for (i=1; i<=n; i++) soma_meio += func(inferior+(i-0.5)*h);
return (soma+2*soma_meio)*h/3.0;
}
// *****************************************************************
template<class Fo, class T> class integralFO
{
T inferior;
T superior;
Fo func;
public:
// construtor
integralFO(T a, T b, Fo f) {inferior = a; superior = b; func = f;}
// Funes auxiliares
void mudalimites(T an, T bn) {inferior = an; superior = bn; }
// funes para integrao
T simpsonFME(int);
// acrescente outras funes ...
};
//template<class S, class Fo> S simpsonFME(integralFO<S>& inte, Fo func,
int n)
template<class Fo, class T> T integralFO<Fo, T>::simpsonFME(int n)
{
T h = (superior - inferior)/n;
T soma = (func(inferior)+func(superior))*0.5;
for (int i=1; i<n; i++) soma += func(inferior+i*h);
T soma_meio = 0.0;
for (i=1; i<=n; i++) soma_meio += func(inferior+(i-0.5)*h);
return T(soma+2*soma_meio)*h/3.0;
}
// *****************************************************************
#include <iostream>
#include <ctime>
#include<cmath>
#include "integraEff.h"
using namespace std;
tipo f_p_i (tipo x)
{
// return ((5*x*x*x - 4*x*x - 7*x +10)/(x*x*x+x*x+1));
return (x*x);
}
inline tipo f_p_i_i (tipo x)
{
// return ((5*x*x*x - 4*x*x - 7*x +10)/(x*x*x+x*x+1));
return (x*x);
}
template<class T> class f_p_i_o // FUNCTION-OBJECT
{
public:
T operator()(T x)
{
// return ((5*x*x*x - 4*x*x - 7*x +10)/(x*x*x+x*x+1));
return (x*x);
}
};
50
int main()
{
tipo li = 0.0;
tipo ls = 3000.0;
int n = 10000;
const tipo CTPS = 1700.0;
// *****************************************************************
// Integrao usando ponteiro para funo simples
tipo resultado = 0.0;
long ck0 = clock();
for (tipo i = li; i<ls; i++)
{
resultado += simpson(i, i+1, f_p_i, n);
}
cout << "Integral usando ponteiro para funcao simples: ";
cout << resultado;
long ck1 = clock();
cout << "\nCPU time: " << tipo (ck1-ck0)/CTPS << " segundos\n\n";
// *****************************************************************
// Integrao usando ponteiro para funo inline
tipo resultado2 = 0.0;
long ck2 = clock();
for (tipo i2 = li; i2<ls; i2++)
{
resultado2 += simpson(i2, i2+1, f_p_i_i, n);
}
cout << "Integral usando ponteiro para funcao inline: ";
cout << resultado2;
long ck3 = clock();
cout << "\nCPU time: " << tipo (ck3-ck2)/CTPS << " segundos\n\n";
// *****************************************************************
// Integrao usando templado e objeto ao invs de ponteiro
tipo resultado3 = 0.0;
long ck4 = clock();
for (tipo i3 = li; i3<ls; i3++)
{
resultado3 += simpsonE1(i3, i3+1, f_p_i_o<tipo>(), n);
}
cout << "Integral usando templado e objeto ao inves de ponteiro: ";
cout << resultado3;
long ck5 = clock();
cout << "\nCPU time: " << tipo (ck5-ck4)/CTPS << " segundos\n\n";
// *****************************************************************
// Integrao usando uma classe para encapsular funes de integrao
long ck6 = clock();
integralF<tipo> inte(li, li+1, f_p_i);
tipo resultado4 = inte.simpsonFM(n);
for (tipo i4 = li+1; i4<ls; i4++)
{
inte.mudalimites(i4, i4+1);
resultado4 += inte.simpsonFM(n);
}
cout << "Integral usando classe para encapsular + ponteiro simples: ";
cout << resultado4;
long ck7 = clock();
cout << "\nCPU time: " << tipo (ck7-ck6)/CTPS << " segundos\n\n";
51
// *****************************************************************
// Integrao usando uma classe para encapsular funes de integrao
// E tambm objeto de funo para evita ponteiro
long ck8 = clock();
integralFO<f_p_i_o<tipo>, tipo> inte2(li, li+1, f_p_i_o<tipo>());
tipo resultado5 = inte2.simpsonFME(n);
for (tipo i5 = li+1; i5<ls; i5++)
{
inte2.mudalimites(i5, i5+1);
resultado5 += inte2.simpsonFME(n);
}
cout << "Integral usando classe para encapsular + objeto de funcao: ";
cout << resultado5;
long ck9 = clock();
cout << "\nCPU time: " << tipo (ck9-ck8)/CTPS << " segundos\n\n";
// *****************************************************************
cout << "\n\n"; return 0;
}
Falamos anteriormente do tipo valarray, disponvel na biblioteca padro do C++. O valarray
um tipo (classe) definido como templado. Agora que j conhecemos a teoria tanto de classes
quanto de templados, podemos compreender melhor como funcionam objetos do tipo
valarray.
Exerccio: Vamos usar o arquivo de ajuda do C++ para aprendermos mais sobre o valarray.
Hierarquia de classes
Templados permitem polimorfismo em tempo de compilao.
Hierarquia de classes permite polimorfismo em tempo de execuo (pode-se, por exemplo,
trabalhar genericamente com ponteiros que apontam para um objeto de um tipo no conhecido
durante a compilao, e converte-lo no tipo certo durante a execuo).
Hieraquia de classes a grande sacada da programao orientada a objetos!! Entretanto, do
ponto de vista de programas cientficos, que em geral consomem muito tempo de execuo, o
polimorfismo em tempo de execuo pode ser um fator a mais para aumentar este tempo, e
no contribui para a otimizao do programa.
A idia bsica :
Uma classe nova (chamada de derivada ou sub-classe) pode ser derivada de uma ou mais
classes j existentes (chamadas de classes base ou superclasses). A classe derivada herda
(em ingls, inherits) todos os membros da classe base (variveis e funes), e pode
acrescentar mais alguns membros prprios. A herana pode ter diferentes nveis de acesso. O
polimorfismo vem do fato que um objeto de uma classe derivada pode ser atribudo a uma
varivel de classe base atravs de ponteiros e referncias.
Um exemplo simples: Uma classe para pontos em 2D pode ser derivada de uma classe para
pontos em 1D.
pt1D Pt2D
52
A classe pt2D tem 2 variveis-membros, ou seja, as coordenadas x e y. Por esse motivo, o
construtor da classe pt2D recebe dois argumentos: um para inicializar a varivel x e o outro
para inicializar a varivel y. Repare, entretanto, que apesar de ter ganho a varivel x por
herana, a classe pt2D no pode por a mo nesta herana diretamente: as variveis privadas
da classe pt1D continuam sendo privadas, ou seja, podem ser acessadas diretamente somente
pelas funes-membro da prpria classe pt1D. Note que tanto o construtor quanto a funo
imprime() da classe pt2D acessaram a varivel x atravs de funes pblicas da classe pt1D.
Por no poder ser acessada pelas funes-membro exclusivas da classe pt2D, a varivel x da
classe pt2D chamada de varivel invisvel.
cout << x << " " << y; // seria um erro usar essa linha na classe pt2D
Para evitar isso, poderamos usar variveis protegidas ao invs de privadas. Tais variveis so
iguais as privadas, exceto que podem ser acessadas livremente pelas classes derivadas. Troque
o palavra private por protected antes da declarao de x na classe pt1D, e a linha acima passa
a ser vlida na definio da funo imprime() na classe pt2D. Tente! Quais as vantagens e
desvantagens de cada uma destas opes?
Desvantagem: No haveria sentido em ter variveis privadas. Por exemplo, se algum
escrevesse uma classe C1, e impedisse o acesso as variveis atravs da palavra chave private,
um outro autor simplesmente desprotegeria tudo usando uma classe C2 derivada de C1!
Vantagem: a classe derivada teria acesso direto a todas as suas (porque x pt2D) variveis.
A funo GetX() uma funo de pt1D e tambm de pt2D. Um objeto do tipo pt2D pode
usar a funo: pt2D p2; double x = p2.GetX()
Observe que o construtor de pt2D chama o construtor de pt1D na lista de inicializao!
Um objeto do tipo pt2D pode estar em qualquer lugar em que um objeto do tipo pt1D poderia
estar: Um ponteiro para pt2D pode ser usado aonde um ponteiro do tipo pt1D esperado sem
necessidade de converso explicita. O contrrio, s atravs de converso explicita.
Vamos testar o seguinte programa para usar as classes pt1D e pt2D:
class pt1D
{
private:
double x; // coordenada x
public:
pt1D(double a = 0) { x = a; } // construtor
void imprime() const { cout << x; } // imprime o valor de x.
double GetX() const { return x; }
};
class pt2D: public pt1D // Note a sintaxe da derivao
{
private:
double y;
public:
pt2D(double a=0, double b=0): pt1D(a) { y = b; }
void imprime() const { pt1D::imprime(); cout << " " << y; }
double GetY() const { return y; }
};
53
Funes virtuais Uma pea fundamental do polimorfismo em tempo de execuo!
Coloque a palavra reservada virtual na frente da funo imprime() da classe base pt1D:
virtual void imprime() { ... }
Compile e execute o programa. Notou alguma diferena?
Acrescente o cdigo seguinte a funo main() e teste-o com e sem a palavra virtual em
pt1D::imprime();
Com funes virtuais, o programa pode se o objeto apontado pelo ponteiro de um tipo ou de
outro (entre classes base e derivadas), e pode chamar a funo sobre-escrita correta.
int main()
{
pt1D p1; // pontos p1 (1D) e p2 (2D) criados
p1 = pt1D(2); // atravs de dois mecanismos. O
pt2D p2(5,6); // mecanismo para criar p1 mais caro.
double x2 = p2.GetX(); // GetX uma funo de p2. x2 = 5.
double y2 = p2.GetY();
cout << "\n(" << x2 << "," << y2 << ")\n";
p2.imprime(); // imprime() de p2 usada. Diz-se que a
// funo imprime() foi sobre-escrita.
pt1D* ppp1D; // ponteiro para pontos em 1D
pt2D* ppp2D; // ponteiro para pontos em 2D;
ppp1D = &p2; // Endereo de um objeto pt2D onde era esperado pt1D. OK!
// ppp2D = &p1; // Erro!
ppp2D = static_cast<pt2D*>(&p1); // converso explcita necessria
cout << "\n"; ppp1D->imprime(); // imprime() de pt1D usada para
// imprimir a varivel x de p2 !
cout << "\n"; ppp2D->imprime(); // imprime() de pt2D usada para imprimir
// um objeto pt1D. x=2; y=imprevisvel!
cout << "\n\n"; return 0;
}
int opcao = 1;
pt1D* ppqp; // ponteiro para qualquer ponto! (classe base)
while (opcao==1 || opcao==2)
{
cout << "\nVoce deseja imprimir o ponto 1 ou 2? ";
cin >> opcao;
switch (opcao)
{
case 1:
ppqp = &p1; ppqp->imprime(); break;
case 2:
ppqp = &p2; ppqp->imprime(); break;
default:
break;
}
}
54
Polimorfismo pode ser obtido atravs do uso de funes virtuais e os objetos precisam ser
manipulados atravs de ponteiros ou referencias, nunca diretamente.
Muitas vezes destruidores precisam ser virtuais tambm, ou os objetos no sero destrudos
corretamente. Como regra, sempre que existir alguma funo virtual em uma classe base, o
destruidor da mesma tambm deve ser virtual.
Classes abstratas
Dado o poder das funes virtuais, as vezes interessante definir classes abstratas que no
fazem nada, apenas servem como classe base comum a outras classes. Por exemplo, sejam 3
classes, triangulo, quadrado e circulo. Todas poderiam ser derivadas de uma classe comum
denominada formaGeometrica. Esta seria uma classe meramente abstrata, e serviria apenas
para conter as funes virtuais comuns as 3 classes, como a funo desenhe(). Uma forma
geomtrica uma abstrao: nenhum objeto pode ser criado sem uma forma concreta, ou seja,
somente objetos do tipo triangulo, quadrado e circulo podem ser criados, e nunca um objeto
do tipo formaGeometrica. Poderamos ainda definir outra classe abstrata, chamada poligono,
com uma funo virtual gire() (No h vantagem em girar um circulo, mas pode-se girar
tringulos ou quadrados). A classe polgono seria derivada de formaGeometrica herdando
assim, a funo virtual desenhe(). As classes triangulo e quadrado seriam derivadas de
polgono, enquanto circulo seria derivada diretamente de formaGeometrica.
Uma classe se torna abstrata quando suas funes virtuais so puras, sendo definidas assim:
virtual void desenhe() = 0; // funo desenhe() da classe formaGeometrica
A classe formaGeometrica no pode fazer nada nesta funo, pois ela no tem informaes
suficientes para desenhar algo. Cada classe concreta ter sua prpria verso desta funo. O
polimorfismo obtido porque todas so derivadas da classe base abstrata.
Herana mltipla
possvel derivar uma classe de mais de uma classe base. Por exemplo, uma classe
circulo_dentro_de_triangulo poderia ser derivada ao mesmo tempo de circulo e de triangulo:
class circulo_dentro_de_triangulo: public circulo, public triangulo
O que voc acha que a classe abaixo conteria:
class gasoduto: public gas, public duto
Assim se consegue rapidamente uma classe que herda propriedades de dois ou mais objetos.
A densidade, viscosidade e temperatura do gs vm da classe gas, juntamente com todas as
equaes de estado termodinmico. As dimenses, rugosidade e material vem da classe duto.
Portanto, metade da classe j est pronta. Basta acrescentar funes especficas para lidar com
o escoamento, como por exemplo, funes para calcular a velocidade, o fator de atrito e a
perda de carga!
55
Uma desvantagem que o cdigo fica mais propenso a erros de ambigidade. SE ambas as
classes base definem um operador ou uma funo de nome igual, e elas no so sobre-escritas
na classe derivada, muitos erros de ambigidade podero aparecer no programa.
E se ambas as classes circulo e triangulo fossem derivadas de uma classe comum
formaGeometrica? Isso significaria que a classe circulo_dentro_de_triangulo herdaria a
classe formaGeometrica duas vezes?
Run-Time Type Information (RTTI)
O mecanismo descrito acima para permitir polimorfismo no informa o programador qual o
tipo do objeto que est sendo apontado por um ponteiro, apenas garante que o programa vai
saber o tipo certo e assim usar as funes corretas. Entretanto, em algumas situaes, o
programador precisa saber explicitamente qual o tipo do objeto que pretende manipular. Esta
informao pode ser obtida por dois mecanismos.
Antes de usar qualquer destes mecanismos, preciso compilar o programa com opo para
obteno de RRTI. Clique em Project Settings C/C++, selecione a cetegoria C++
Language e marque a caixa Enable RTTI.
O primeiro mecanismo o dynamic_cast. Acrescente as classes A e B conforme abaixo em
seu programa que tem as classes pt1D e pt2D.
formaGeometrica formaGeometrica
triangulo circulo
Circulo_dentro_de_triangulo
formaGeometrica
triangulo circulo
Circulo_dentro_de_triangulo
class A
{
private:
double a;
public:
virtual void imprime() const { cout << a; }
};
class B
{
private:
double b;
public:
virtual void imprime() const { cout << b; }
};
56
Modifique a funo main():
O outro mecanismo o typeid, que exige a incluso da biblioteca <typeinfo>. Acrescente as
seguintes linhas a funo main():
int main()
{
pt1D p1;
p1 = pt1D(2);
pt2D p2(5,6);
// Se p1 for um objeto do tipo pt1D, ento p1D_p1 vai ter o endereo
de p1
// Caso contrrio p1D_p1 ser nulo.
pt1D* p1D_p1 = dynamic_cast<pt1D*>(&p1);
if (p1D_p1) cout << "p1 e do tipo pt1D\n";
else cout << "p1 nao e do tipo pt1D\n";
pt1D* p1D_p2 = dynamic_cast<pt1D*>(&p2);
if (p1D_p2) cout << "p2 e do tipo pt1D\n";
else cout << "p2 nao e do tipo pt1D\n";
pt2D* p2D_p1 = dynamic_cast<pt2D*>(&p1);
if (p2D_p1) cout << "p1 e do tipo pt2D\n";
else cout << "p1 nao e do tipo pt2D\n";
pt2D* p2D_p2 = dynamic_cast<pt2D*>(&p2);
if (p2D_p2) cout << "p2 e do tipo pt2D\n";
else cout << "p2 nao e do tipo pt2D\n";
A objA;
B objB;
A* pA = dynamic_cast<A*>(&objA);
B* pB = dynamic_cast<B*>(&objA);
if (pA) cout << "\nobjA e do tipo A\n";
if (pB) cout << "objA e do tipo B\n";
pA = dynamic_cast<A*>(&objB);
pB = dynamic_cast<B*>(&objB);
if (pA) cout << "objB e do tipo A\n";
if (pB) cout << "objB e do tipo B\n";
cout << "\n\n"; return 0;
}
cout << "\n\n" << typeid(p1).name();
cout << "\n" << typeid(p2).name();
// p1D_p2 um ponteiro para um objeto pt1D, mas tem o
//endereo de um pt2D
cout << "\n" << typeid(*p1D_p2).name();
cout << "\n" << typeid(objA).name();
cout << "\n" << typeid(objB).name();
57
Polimorfismo em tempo de execuo aumenta o tempo de execuo de um programa. Os
mecanismos para RTTI exigem ainda mais tempo do que o mecanismo com funes virtuais.
Prefira funes virtuais. S use dynamic_cast ou typeid quando absolutamente necessrio.
Sempre que possvel, use templados ao invs de funes virtuais.
(Ex: implementao da funo para multiplicar matrizes por vetores)