Sei sulla pagina 1di 57

Centro de Cincias Exatas e de Tecnologia

Departamento de Engenharia Qumica







C++ para Engenharia


Jos Antnio Silveira Gonalves






Fevereiro 2004
2
Primeiro Programa, Biblioteca e DLL (+ Debugging...)

Primeiro Programa

1. Abra o VC 6.0

2. Selecione File New e complete a caixa de dilogo abaixo (na aba Projects), selecionando
Win32 Console Application e fornecendo o nome e a localizao de seu projeto



3. Clique OK.

4. Escolha um projeto simples e clique no boto Finish.



Alguns arquivos foram criados automaticamente pelo VS e includos em seu espao de
trabalho (workspace) que aberto pelo VS.

3



Passeie um pouco pelo ambiente de trabalho do VS, sem se preocupar em entender tudo neste
momento. Note:
As abas ClassView e FileView.
Os 3 tipos de arquivos criados: arquivos-fonte, cabealho e de recursos.
Leia as informaes sobre os arquivos StdAfx.h e StdAfx.cpp no arquivo Readme.txt

Todo programa C++ tem obrigatoriamente uma (nica) funo main (principal), e,
normalmente, muitas outras funes. Uma funo (rotina ou sub-rotina) um pedao de
cdigo que executa uma tarefa especfica. Um programa C++ inicia sempre executando a
funo main. A sintaxe de uma funo :

Tipo-do-valor-de-retorno nome-da-funo ( lista de parmetros passados funo )

O trecho entre { } contm o cdigo executado pela funo.







4
Modifique o arquivo p01_Reynold.cpp, digitando o cdigo a seguir:


Observe que o cdigo:
Define e declara 6 variveis do tipo string (seqncia de caracteres).
Declara mas no define 4 variveis do tipo double.
Usa os dispositivos de entrada e sada padres (cout = monitor; cin = teclado)
juntamente com os operadores de direcionamento >> e << para escrever na tela e
receber valores do teclado. Por exemplo, o comando cout << s2 direciona o valor de
s2 para o dispositivo de sada padro enquanto que o comando cin >> dens armazena
o valor digitado na varivel dens, que s pode armazenar valores do tipo double.
Declara mas no define uma varivel Re do tipo double. Observe que uma dedclarao
pode aparecer em qualquer linha do programa!!
Faz uma conta e armazena o resultado na varivel Re.
Mostra o resultado da conta na tela.

As palavras em azul so palavras reservadas em C++. Note que as palavras string, cout e cin
no so reservadas, ou seja, no fazem parte da linguagem C++!! Como foi possvel usa-las?
Porque algum outro programador definiu estas palavras nas bibliotecas iostream e string que
ns inclumos em nosso programa. Os nomes usados nestas bibliotecas so definidos em um
ambiente de nomes std. Ou seja, o nome completo de cout std::cout. A instruo using
namespace std nos desobriga de ter que usar o nome completo de cout toda vez que feita
uma referencia a cout, o que poderia ser tedioso.

#include "stdafx.h"
#include <iostream>
#include <string>

int main(int argc, char* argv[])
{
using namespace std;

string s1 = "*** Programa para calcular o numero adimensional de Reynolds ***";
string s2 = "Densidade do fluido: ";
string s3 = "Viscosidade do fluido: ";
string s4 = "Velocidade do escoamento: ";
string s5 = "Comprimento caracteristico: ";
string s6 = "O numero de Reynolds eh: ";

double dens, visc, vel, dia;

cout << "\n" << s1 << "\n\n";
cout << s2;
cin >> dens;
cout << s3; cin >> visc;
cout << s4; cin >> vel;
cout << s5; cin >> dia;

double Re;
Re = dens * vel * dia / visc;

cout << "\n" << s6 << Re << "\n\n";

return 0;
}
5
Crie uma verso release de seu programa. Ache o arquivo \Release\p01_Reynolds1.exe
em seu micro, copie-o para um disquete, mude o seu nome para re e distribua-o para seus
amigos! Ele roda melhor a partir do prompt do dos.



Criando uma biblioteca de funes para engenharia qumica

Modifique seu programa da seguinte maneira:


Observe:
O clculo do nmero de Reynolds feito em uma funo separada da funo main. A
funo main ento chama a funo Reynolds no momento que necessita dela. So
vrias as vantagens dessa tcnica, incluindo melhor manuteno de programas
#include <iostream>
#include <string>

double Reynolds (double dens, double visc, double vel, double D)
{
if ((visc > 0) &&
(dens > 0) &&
(vel >= 0) &&
(D >= 0)) return (dens * vel * D / visc);
else return -1;
}

int main(int argc, char* argv[])
{
using namespace std;

string s1, s2, s3, s4, s5, s6;
s1 = "*** Programa para calcular o numero adimensional de Reynolds
***";
s2 = "Densidade do fluido: ";
s3 = "Viscosidade do fluido: ";
s4 = "Velocidade do escoamento: ";
s5 = "Comprimento caracteristico: ";
s6 = "O numero de Reynolds eh: ";

double dens, visc, vel, dia;

cout << "\n" << s1 << "\n\n";
cout << s2; cin >> dens;
cout << s3; cin >> visc;
cout << s4; cin >> vel;
cout << s5; cin >> dia;

double Re = Reynolds(dens, visc, vel, dia);

cout << "\n" << s6 << Re << "\n\n";

return 0;
}
6
grandes, e possibilidade de chamar a funo Reynolds vrias vezes dentro do mesmo
programa.
Uso da clusula condicional if-else
Verificao de erro para tornar a funo Reynolds mais robusta.

A funo Reynolds muito usada em EQ. Que tal coloca-la em uma biblioteca de funes
comuns para EQ? Assim, outras pessoas vo poder us-la sem ter que defini-la novamente.

Crie um arquivo novo do tipo cabealho, em uma diretoria qualquer em sua HD. No adicione
o arquivo novo ao seu ambiente de trabalho.

Digite o seguinte neste novo arquivo:


Apague a definio da funo Re do arquivo p01_Reynolds1.cpp e inclua as seguintes linhas
nos locais apropriados:

Compile e rode o programa.

Experimente digitar deq_ufscar:: e veja as funes disponveis neste ambiente de nomes.

Separe o arquivo em dois: .h e ponto .cpp

// EQ primeira biblioteca

#if !defined(EQ_W3R456HSF__INCLUDED_)
#define EQ_W3R456HSF__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000

namespace deq_ufscar
{

// Funo para calcular o nmero de Reynolds em um escoamento
// autor: Z Antnio
// Includa em 10/01/2004
double Reynolds (double dens, double visc, double vel, double D)
{
if ((visc > 0) &&
(dens > 0) &&
(vel >= 0) &&
(D >= 0)) return (dens * vel * D / visc);
else return -1;
}

// Acrescente outras funes

} // namespace deq_ufscar

#endif // EQ
#include "C:\...\eq.h"

using namespace deq_ufscar;
7
Transformando a biblioteca em uma DLL

Funes j testadas (exaustivamente!) devem ser includas em uma DLL.

Crie um projeto novo, vazio, do tipo Win32 dynamic-link library no VS com o nome eqdll.
Acrescente dois arquivos ao projeto:



Depois de compilado na configurao release, encontre 3 arquivos criados em seu HD:
C:\...\eqdll\eqdll.h
C:\...\eqdll\Release\eqdll.lib
C:\...\eqdll\eqdll.dll
Qualquer pessoa que usar sua DLL precisar destes 3 arquivos. Como exemplo, vamos usar a
nossa DLL em nosso programa para calcular o nmero de Reynolds.
No arquivo p01_Reynolds1.cpp, ao invs de incluir o arquivo eq.h, inclua o eqdll.h.
Clique em Project, Settings e na caixa Settings for escolha all configurations.
Escolha a aba Link.
Acrescente o nome eqdll.lib ao final da linha na caixa Object/library modules.
Copie o arquivo eqdll.lib para a a pasta \...\p01_Reynolds1
Copie o arquivo eqdll.dll para ambas as pastas \...\p01\Reynolds1\Debug e \Release.
Arquivo eqdll.h
// eqdll.h

#if !defined(EQDLL_H_1234__INCLUDED_)
#define EQDLL_H_1234__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

namespace deq_ufscar
{

// Funo para calcular o nmero de Reynolds em um escoamento
// autor: Z Antnio
// Incluida em 10/01/2004
__declspec(dllexport) double Reynolds (double dens, double visc, double vel,
double D);

} // namespace deq_ufscar

#endif // EQDLL_H_1234__INCLUDED_
Arquivo eqdll.cpp
// eqdll.cpp

#include "eqdll.h"

__declspec(dllexport) double deq_ufscar::Reynolds (double dens, double visc,
double vel, double D)
{
if ((visc > 0) &&
(dens > 0) &&
(vel >= 0) &&
(D >= 0)) return (dens * vel * D / visc);
else return -1;
}
8
Compile nas verses debug e release.
Copie em um disquete apenas os arquivos p01_Reynolds.exe (release) e eqdll.dll
(release), e rode o programa a partir do disquete. Observe que o programa no roda se
a dll no estiver no disquete.

Observe que os programadores que utilizam a sua DLL tm acesso apenas aos cabealhos de
suas funes (a interface) e no conhecem a implementao das funes, ao contrrio do caso
que o arquivo .h continha tambm as definies das funes.

Experimente colocar o #include \...\eqdll.h no arquivo StdAfx.h ao invs de no
p01_Reynolds1.cpp.

A DLL criada bastante flexvel e pode ser usada em diferentes ambientes de trabalho, como
em programas para UNIX, Visual Basic, etc...


Introduo ao conceito de novos tipos

Voc no sentiu dificuldade em escrever um comprimento como 0.0254 ao invs de 2.54 cm
ou 1 inch? Um comprimento, ou qualquer outra grandeza dimensional no est bem
representado por um tipo double. Falta uma unidade!

Suponha que ao usar a funo deq_ufscar::Reynolds um programador passe os argumentos na
ordem trocada, ou seja, o valor da densidade no lugar da viscosidade e vice-e-versa. A funo
no tem como detectar este erro e poder levar dias at que o programador saiba porque um
programa est usando as equaes para um escoamento laminar quando sabemos que o
escoamento turbulento.

Ambos estes problemas seriam sanados se existisse um tipo densidade ou comprimento que
pudesse ser utilizado ao invs do double. Ao invs de definir a varivel

double diametro;
diametro = 5;

definiramos:

comprimento diametro;
diametro = comprimento(5, cm);

ou comprimento dimetro(5, cm); ou ainda comprimento dimetro = 5 * cm;

Seria possvel passar para a funo Reynolds no apenas 4 valores reais, mas uma densidade,
por exemplo, 1 g/cm3, uma viscosidade (1 cP), e assim por diante.

C++ permite a definio de novos tipos! Alias, C++ foi criado exatamente para que fosse
possvel definir novos tipos personalizados. Se voc no vai definir novos tipos em seus
programas, no use C++! Use C puro (ou Fortran, Basic, etc...)!

Tipo, em C++, sinnimo de classe!
9
Tipos

Todo programa manipula objetos. Cada objeto de um tipo. O tipo do objeto determina seu
comportamento, incluindo quais operaes ele realiza, quais valores ele pode assumir e
quanto espao na memria ele precisa.

Tipos
Primitivos
D. Usurio
Aritmtico
Caractere
Lgico
Nulo
Ponteiro
Referncia
Vetores
Enumerado
Estrutura
Classe


Tipos Aritmticos:

Nmeros inteiros

int = signed int = signed
unsigned int = unsigned
short int = short
short unsigned int
long int = long
long unsigned int

Dependendo da mquina, um int ocupa 4 bytes (32 bits), um short 2 bytes, e um long 6 bytes.
O nmero inteiro positivo 15 armazenado como abaixo em 32 bits: 1 para o sinal e 31 para o
nmero.

0 0000000 00000000 00000000 00001111


Sinal

10
Com 32 bits disponveis, podem-se armazenar os nmeros de -2
31
at 2
31
-1, ou, em decimal,
de -2.147.483.648 at 2.147.483.647. Um short, com apenas 16 bits, armazena de -32768 at
32.767. Um unsigned short, que no necessita do bit da esquerda para o sinal, de 0 at 65535.

Quando chega-se nos limites de armazenagem e adiciona-se 1, ocorre overflow ou underflow.
A conta feita, o programa no reclama, mas o nmero armazenado no o nmero que a
lgica do programa exige. Portanto, os resultados do programa estaro errados!!

Para o computador manipulando um tipo short, 32.767 + 1 pode ser igual a -32.768 !!!
(Nmeros negativos so armazenados como complementos). Experimente a seguinte linha de
cdigo:

short si = 32767;
cout << si;
si = si + 1;
cout << si;

Converso de tipos inteiros

Um tipo pode ser convertido em outro implicitamente ou explicitamente. A converso
explicita chamada de CASTING:

int i = 3;
short si = 3;
long li = i; // converso implcita.
li = long (si); // converso explcita
li = i + si; // converso implcita.

Os nmeros i e si no so convertidos em long em seu espao de armazenamento na memria
(eles continuam tendo seus tipos originais), mas sim quando eles ao trazidos da memria para
os registradores que vo efetuar a conta. Assim, sero dois nmeros long que sero somados,
e o resultado ser um long.

Tome sempre muito cuidado com converso de tipos, pois pode ocorrer perda de informao:

long li = 50000;
short si = li; // converso implcita com perda de informao.
cout << si; // no meu PC, si = - 15536

Ao escrevermos uma constante inteira em uma expresso (ex: i = a + 5;), o computador
assume por default que a constante do tipo int. Se quisermos constantes do tipo unsigned ou
long podemos escrever 5U ou 5L;

Nmeros com ponto flutuante

float
double
long double

11
O nmero 4,5 pode ser escrito, em base 10, como 4 x 10
0
+ 5 x 10
-1
. Em sistema binrio, seria
escrito como:

1 x 2
2
+ 0 x 2
1
+ 0 x 2
0
+ 1 x 2
-1
= 2
2
x ( 1 + 0 x 2
-1
+ 0 x 2
-2
+ 1 x 2
-3
).

(Tente com 4,4 !!)

Assim, para armazenarmos um numero com ponto flutuante em sistema binrio, podemos
armazenar uma srie de n bits (mantissa) representando as posies 2
-1
, 2
-2
, ..., 2
-n
e um
nmero inteiro mcorrespondente ao expoente que eleva o dois que fica fora dos parnteses.
No exemplo acima, com oito bits, convencionando que seriam usados os dois primeiros para o
sinal do nmero e do expoente m, outros dois para o expoente e os quatro restantes para a
mantissa, teramos:

0 0 10 0010

Preste ateno ao bit escondido !!!

Em muitas mquinas, 4 bytes ou 32 bits so usados para armazenar um nmero do tipo float:

1 bit para o sinal do nmero
1 bit para o sinal do expoente
7 bits para o expoente (que portanto pode ir at 2
7
1 = 127
23 bits para a mantissa

Um double normalmente tem 8 bytes (64 bits) sendo

1 bit para o sinal do nmero
1 bit para o sinal do expoente
10 bits para o expoente (que portanto pode ir at 2
10
1 = 1023
52 bits para a mantissa

Um long double ocupa, em algumas mquinas, 12 bytes.

Devido ao nmero finito de bits usados para representar nmeros em um computador, a
representao do nmero pode ser diferente do nmero em si (muitos nmeros tm
representao exata). Entretanto, para uma dada representao (4, 8 ou 12 bytes), possvel
calcular o erro mximo epsilon definido por:

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 = &pi;

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)

Potrebbero piacerti anche