Professional Documents
Culture Documents
http://ctp.di.fct.unl.pt/~pg/cpp2003
Fevereiro de 2003
por
Pedro Guerreiro
pg@di.fct.unl.pt, http://ctp.di.fct.unl.pt/~pg
Departamento de Informática
Faculdade de Ciências e Tecnologia
Universidade Nova de Lisboa
2829-516 Caparica, Portugal
Objectivos do C++
A linguagem de programação C++ foi inventada por Bjarne
Stroustrup, nos laboratórios Bell, com o objectivo de:
Ser um C melhor.
Suportar a programação com tipos abstractos.
Suportar a programação orientada pelos objectos.
Suportar a programação genérica. Uma linguagem tão
ambiciosa, com objectivos
tão vastos, provavelmente é
complicada /
Bibliografia Complementar
Introduction to Algorithms, Thomas H. Cormen, Charles E.
Leiserson, Ronald L. Rivest, Clifford Stein, 2001.
Object-Oriented Software Construction, Bertrand Meyer,
1997.
Elementos de Programação com C, ..., 2001.
E ainda:
Cadeias de caracteres: tipo std::string.
Em rigor, este não é um tipo básico. É sim uma
classe da biblioteca STL.
Point Um ponto
Pointp1;
p1;
Point p2;
Point p2; Outro ponto
Point
Pointa[10];
a[10]; Um quadro (array) com 10 pontos, indexados de 0 a 9.
A classe Point
class
classPoint
Point{{
Há quatro private:
private:
qualidades de double
doublex;
double
x;
doubley;y; Construtores
funções membro: public:
public:
Point();
Point();
Point(double
Point(doublex,x,double
doubley);
y);
Point(const
Point(const Point&other);
Point& other);
• Construtores virtual
virtual~Point();
~Point();
Destrutor
• Destrutor virtual
virtualdouble
virtual
doubleX()
X()const;
const; Selectores
virtual double Y()const;
double Y() const;
• Selectores virtual
virtualvoid
voidTranslate(double
Translate(doubledx,
dx,double
doubledy);
dy);
virtual
virtual void Scale(double fx, doublefy);
void Scale(double fx, double fy); Modificadores
• Modificadores virtual void Rotate(double angle);
virtual void Rotate(double angle);
virtual
virtualdouble
doubleDistanceTo(const
DistanceTo(constPoint&
Point&other)
other)const;
const;
virtual
virtualdouble
doubleAngle()
Angle()const;
const;
virtual Mais selectores
virtual double Modulus()const;
double Modulus() const;
virtual
virtualvoid
voidWrite()
Write()const;
const;
virtual
virtual void WriteLine()const;
void WriteLine() const;
};
};
Point(double
Point(doublex,x, double
double y);
y); Este é o construtor elementar: inicializa com x, y.
Point(const
Point(const Point&
Point& other);
other); Este é o construtor de cópia: inicializa com os valores
dos membros de dados do argumento other.
//
// ...
...
Point Inicialização por defeito.
Exemplo: Pointp;
p;
Point
Pointq(3.0,
q(3.0,4.0);
4.0); Inicialização elementar.
Point
Pointr(q);
r(q); Inicialização por cópia.
Point
Pointz[32];
z[32]; Os 32 elementos do quadro z são inicializados por defeito.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 11
Destrutor
Cada classe tem um destrutor.
O destrutor de uma classe é chamado implicitamente quando
um objecto da classe é destruído. Nós programamos o
destrutor, mas não o chamamos.
//
// ...
...
virtual
virtual ~Point();
~Point(); O nome do destrutor numa classe X é ~X.
//
// ...
...
Regra: todos os destrutores são declarados virtual.
Os destrutores servem para libertar a Note bem: em rigor, não é o
destrutor que “destrói” o objecto.
memória que foi alocada dinamicamente O objecto é destruído por um
pelo objecto durante a sua existência, no outro meio e o destrutor é
invocado automaticamente
momento em que o objecto está prestes a nessa altura para “arrumar a
deixar de existir. casa”.
virtual
virtual void
void Write()
Write() const;
const;
virtual
virtual void WriteLine() const;
void WriteLine() const;
Os nomes das funções sugerem o seu significado. Note que
as funções X e Y são indispensáveis para consultar os valores
dos membros de dados x e y, que são privados.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 13
Modificadores
Os modificadores são as funções da classe que modificam o
estado do objecto. Não devolvem qualquer informação.
//
// ...
...
virtual
virtual void
void Translate(double
Translate(double dx,
dx, double
double dy);
dy);
virtual void Scale(double fx, double fy);
virtual void Scale(double fx, double fy);
virtual
virtual void
void Rotate(double
Rotate(double angle);
angle);
// ...
// ...
Os nomes das funções sugerem o seu significado.
Regra: todos os modificadores são declarados virtual e
retornam void.
Retornar void significa não retornar nada. Usando uma nomenclatura mais convencional, as
funções C++ que retornam void são procedimentos, e as funções C++ que retornam valores
mesmo são funções no sentido habitual.
Infelizmente, as letras
acentuadas e os cês
cedilhados saem mal na
consola /
Exercícios 1
1. Escreva uma função main que aceite as coordenadas de
dois pontos e calcule os dois pontos que dividem o
segmento definido pelos dois primeiros em três partes
iguais.
2. Escreva uma função main que aceite dois números reais
representando a parte real e a parte imaginária de um
número complexo e calcule, por intermédio da classe Point,
a parte real e a parte imaginária da raiz quadrada desse
número complexo. Investigue no ficheiro Point.cpp para
descobrir como é que se calcula a raiz quadrada de um
número double. Não se esqueça do ficheiro de inclusão
<cmath>.
3. Escreva uma função main para calcular os vértices do
triângulo equilátero com centro na origem e base horizontal,
dado o comprimento do lado.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 18
Passagem de argumentos
Em C++, os argumentos podem passar de três maneiras: por
valor, por referência e por referência constante.
class
classPoint
Point{{ Passagem por valor.
...
...
virtual
virtualdouble
doubleDistanceTo(Point
DistanceTo(Pointother)
other)const;
const;
...
... class
classPoint
Point{{
};
}; Passagem por referência.
...
...
virtual
virtualdouble
doubleDistanceTo(Point&
DistanceTo(Point&other)
other)const;
const;
...
...
}; class
classPoint
Point{{
}; Passagem por referência constante.
...
...
virtual
virtualdouble
doubleDistanceTo(const
DistanceTo(constPoint&
Point&other)
other)const;
const;
...
...
};
};
Ao passar sempre por valor ou por referência constante, garantimos que o valor dos argumento
não muda. Para mudar os valores de variáveis usamos ou a afectação ou um modificador da
classe respectiva.
A passagem por referência não constante usa-se em casos particulares,
por exemplo, quando os argumentos representam ficheiros:
virtual
virtualvoid
voidWrite(std::ostream&
Write(std::ostream&output
output==std::cout)
std::cout)const;
const;
virtual
virtual void WriteLine(std::ostream& output = std::cout)const;
void WriteLine(std::ostream& output = std::cout) const;
virtual
virtualvoid
voidRead(std::istream&
Read(std::istream&input
input==std::cin);
std::cin);
A instrução return
Sintaxe: Semântica:
return expressão; Avalia a expressão e termina a
função onde ocorre, devolvendo o
valor calculado.
double
doublePoint::X()
Point::X()const
const double
doublePoint::DistanceTo(const
Point::DistanceTo(constPoint&
Point&other)
other)const
const
{{ {{
return
returnx;
x; return
return::sqrt(::pow(x
::sqrt(::pow(x--other.x,
other.x,2)
2)++::pow(y
::pow(y--other.y,
other.y,2));
2));
}} }}
double
doublePoint::Angle()
Point::Angle()const
const ::sqrt(x): raiz quadrada de x.
{{
return ::atan2(y, x): arcotangente
return::atan2(y,
::atan2(y,x);
x);
}} de y/x.
double ::pow(x, y): x elevado a y.
doublePoint::Modulus()
Point::Modulus()const
const
{{
return
return::sqrt(x*x
::sqrt(x*x++y*y);
y*y);
}}
Afectações mistas
Normalmente, a variável e a expressão são do mesmo tipo. As
únicas excepções razoáveis a esta regra envolvem os tipos
simples:
Tem o efeito esperado. Não há problema.
int
intn;
n;
double
doublex;
x; Inseguro: truncaria a parte decimal. O compilador gera warning:
bool
boolb;
b; “'=' : conversion from 'double' to 'int', possible loss of data.”
char c;
char c;
OK: false dá zero, true dá 1.
...
...
Inseguro: zero dá false, não zero dá true. O compilador gera
xx==n;
n; warning:” 'int' : forcing value to bool 'true' or 'false' (performance
nn==x;
x; warning)”
nn==b;
b; OK: n fica com o valor numérico de c.
bb==n;
n;
OK, mas inseguro pois o valor de n pode não ser representável
nn==c;
c; no tipo char.
cc==n;
n;
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 26
Conversão explícita
Evitam-se aqueles warnings fazendo uma conversão explícita
para o tipo da variável. No caso conversão para int usa-se o
int
intn;
n;
operador static_cast<T>, no caso da
double
doublex; x; conversão para bool, usa-se o operador
bool
boolb;b; != (operador de desigualdade).
char
charc;
c;
...
...
xx==n;
n;
nn==static_cast<int>(x);
static_cast<int>(x); Nota: usa-se static_cast<double> para forçar
conversão para double em expressões:
nn==b;
b;
bb==nn!= int
intn;
!=0;
0; n;
int
intsum;
sum;
nn==c; ...
...
c;
cc==n; double
doubleaverage
average==static_cast<double>(sum)
static_cast<double>(sum)//n;
n;
n;
Use Em vez de
Eis a tabela dos operadores x += y x=x+y
de afectação não simples x -= y x=x-y
mais usuais: x *= y x=x*y
x /= y x=x/y
x %= y x=x%y
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 28
Afectações de classe
Podemos afectar um objecto de um tipo classe a outro? Sim,
podemos. Significa a afectação membro a membro:
...
...
Point
Pointp(4,
p(4,7);
7);
Point
Point q(0,2);
q(0, 2);
...
...
p.Rotate(1.57079632679489661923);
p.Rotate(1.57079632679489661923);
q.Scale(0,
q.Scale(0,-1);
-1);
...
...
qq==p;p; Afectação membro a membro.
...
...
Frequentemente o que queremos é a construção por cópia e não a
afectação:
...
... ...
...
Point
Pointp(4,
p(4,7);
7); Point
Pointp(4,
p(4,7);
7);
...
... ...
...
p.Rotate(1.57079632679489661923);
p.Rotate(1.57079632679489661923); p.Rotate(1.57079632679489661923);
p.Rotate(1.57079632679489661923);
Point
Pointq;
q; Point
Pointq(p);
q(p);
qq==p;p; Mau estilo: inicialização logo ...
... Bom estilo...
...
... seguida de afectação.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 29
double
doublePoint::DistanceTo(const
Point::DistanceTo(constPoint&
Point&other)
other)const
const
{{
return
return::sqrt(::pow(x
::sqrt(::pow(x--other.x,
other.x,2)
2)++::pow(y
::pow(y--other.y,
other.y,2));
2));
}}
Escrevendo
Em C++, escreve-se usando o operador de inserção <<:
void
voidPoint::Write(std::ostream&
Point::Write(std::ostream&output)
output)const
const
{{
output
output<<
<<xx<<
<<""""<<<<y;y;
}}
void
voidPoint::WriteLine(std::ostream&
Point::WriteLine(std::ostream&output)
output)const
const
{{
Tecnicamente, este std::endl é um manipulador que quando
Write(output);
Write(output); enviado para uma stream de escrita provoca uma mudança
output
output<<
<<std::endl;
std::endl; de linha.
}}
Definindo os construtores
Os construtores são funções especiais que têm uma sintaxe
especial. Sempre que possível inicializamos os membros de
dados na lista de inicializadores:
Point::Point():
Point::Point():
x(0),
x(0), A lista de inicializadores vem entre aquele sinal de dois pontos a seguir ao
y(0)
y(0)
{{ cabeçalho e a chaveta a abrir. Só os construtores é que têm lista de
}} inicializadores.
Funções de teste
#include
#include<iostream>
<iostream>
Para simplificar a organização do ////...
...
nosso programa, usamos funções de #include
#include"Point.h"
"Point.h"
teste. A função main limita-se a void
voidTestPoints();
TestPoints();
void
voidTestDoubleOutput();
chamar uma das funções de teste. TestDoubleOutput();
int
intmain()
main()
Cada função de teste tem um protótipo, que constitui a {{
//TestPoints();
//TestPoints();
respectiva declaração. O protótipo é indispensável TestDoubleOutput();
TestDoubleOutput();
porque as função vão ser chamadas na função main, return
return0;
0;
e as definições só aparecem depois. }}
void
voidTestPoints()
TestPoints()
{{
Tipicamente, a função main chama apenas uma das //...
funções de teste. As outras ficam em comentário. //...
}}
Exercícios 2
1. Defina uma classe Segment para representar segmento de
recta. Preveja funções para o calcular comprimento do
segmento, para obter o ponto médio, para escalar o segmento,
para transladar, para rodar o segmento em torno da origem e
ainda em torno das extremidades e em torno do ponto médio,
para ver se um ponto pertence a um segmento, para ver se
dois segmentos se intersectam e, caso se intersectem, calcular
o ponto de intersecção, etc.
2. Defina uma classe Triangle para representar triângulos, com
funções para a área e para o perímetro, para escalar,
transladar e rodar, para ver se um ponto está no interior do
triângulo, para calcular os centros, etc.
3. Defina uma classe Rectangle, para representar rectângulos
com base horizontal, com dois membros de dados, um o canto
inferior esquerdo e outro para canto superior direito. Preveja
funções para a área, perímetro, para escalar, transladar, para
calcular a intersecção com outro rectângulo, etc.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 40
Evitando inclusões repetidas
Se um ficheiro A incluir os ficheiros B e C e se o ficheiro B
incluir o ficheiro C, então quando processar o ficheiro A o
compilador inclui o ficheiro C duas vezes. Isto pode causar
problemas. Evitamo-los com a directiva #ifndef e com as
constantes simbólicas. O ficheiro Point.h fica assim:
#ifndef
#ifndef_H_Point
_H_Point Isto significa: se a constante simbólica _H_Point ainda não
#define _H_Point
#define _H_Point estiver definida, então fica definida e o resto do ficheiro é
processado pelo compilador; se já estiver definida, então o
class
classPoint
Point{{ compilador ignora tudo até à correspondente directiva
private:
private: #endif, lá em baixo.
double
doublex;
x;
double y;
double y; Regra: todas as declarações de classes
////...
... ficarão dentro de uma zona #ifndef-
};
}; #define-#endif. O nome da constante
simbólica é o nome da classe prefixado por
#endif
#endif
_H_, convencionalmente.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 41
class
classPoint
Point{{ #include
#include"Point.h"
"Point.h" #include
#include"Point.h"
"Point.h"
private:
private:
double
doublex;x; Point::Point():
Point::Point(): void
voidTestPoints();
TestPoints();
double y;
double y; x(0),
x(0), void TestDoubleOutput();
void TestDoubleOutput();
////... y(0) Só incluímos o que faz
... y(0) falta. Por exemplo, no
};
}; {{ Point.cpp faz falta o int
intmain()
main()
}} <cmath>, mas no {{
#endif
#endif M_Point.cpp não faz. //TestPoints();
//TestPoints();
////...
... TestDoubleOutput();
TestDoubleOutput();
Point.h return
Point.cpp return0;
0;
}}
std::cout
std::cout<<<<"Coordinates
"Coordinates(x
(xand
andy),
y),please:
please:";";
//...
//...
std::cin
std::cin>>
>>xx>>>>y;y; void TestDoubleOutput()
void TestDoubleOutput()
Point
Pointp(x,
p(x,y);
y);
{
{
double x;
double x;
double y;
double y;
cout << "Coordinates (x and y), please: ";
double
doubledd==p.DistanceTo(Point());
p.DistanceTo(Point()); cout << "Coordinates (x and y), please: ";
cin >> x >> y;
cin >> x >> y;
Point p(x, y);
Point p(x, y);
std::cout
std::cout<<
<<"General
"Generalformat:
format:""<<<<std::endl;
std::endl; double d = p.DistanceTo(Point());
double d = p.DistanceTo(Point());
std::cout
std::cout << "Distance to origin:""<<
<< "Distance to origin: <<dd<<<<std::endl;
std::endl; cout << "General format: " << endl;
cout << "General format: " << endl;
cout << "Distance to origin: " << d << endl;
cout << "Distance to origin: " << d << endl;
std::cout
std::cout<<
<<"Fixed
"Fixedformat,
format,two
twodecimal
decimaldigits:
digits:""<<
<<std::endl;
std::endl; cout << "Fixed format, two decimal digits: " << endl;
std::cout << std::fixed << std::setprecision(2);
std::cout << std::fixed << std::setprecision(2);
cout << "Fixed format, two decimal digits: " << endl;
cout << fixed << setprecision(2);
cout << fixed << setprecision(2);
cout << "Distance to origin: " << d << endl;
std::cout
std::cout<<
<<"Distance
"Distancetotoorigin:
origin:""<<
<<dd<<
<<std::endl;
std::endl; cout << "Distance to origin: " << d << endl;
//...
//...
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 43
Limites numéricos
void
voidTestNumericLimits()
Às vezes {{
TestNumericLimits()
precisamos int
intintMax
intMax==std::numeric_limits<int>::max();
std::numeric_limits<int>::max();
de conhecer int
int intMin==std::numeric_limits<int>::min();
intMin std::numeric_limits<int>::min();
double
doubledoubleInfinity
doubleInfinity==std::numeric_limits<double>::infinity();
os limites double
std::numeric_limits<double>::infinity();
double doubleMax==std::numeric_limits<double>::max();
doubleMax
dos tipos double
doubledoubleMin
std::numeric_limits<double>::max();
doubleMin==std::numeric_limits<double>::min();
std::numeric_limits<double>::min();
numéricos double
double epsilon==std::numeric_limits<double>::epsilon();
epsilon std::numeric_limits<double>::epsilon();
nos nossos std::cout
programas. std::cout<<
<<"Maior
"Maiornúmero
númerointeiro:
inteiro:""<<
<<intMax
intMax<<
<<std::endl;
std::endl;
std::cout
std::cout << "Menor número inteiro: " << intMin <<std::endl;
<< "Menor número inteiro: " << intMin << std::endl;
Eis um
programa std::cout
std::cout<<
<<"Mais
"Maisinfinito
infinito(double):
(double):""<<<<doubleInfinity
doubleInfinity<<
<<std::endl;
std::endl;
std::cout
std::cout << "Maior número positivo double: " << doubleMax<<
<< "Maior número positivo double: " << doubleMax <<std::endl;
que os std::cout
std::endl;
std::cout << "Menor número positivo double: " << doubleMin <<std::endl;
<< "Menor número positivo double: " << doubleMin << std::endl;
revela: std::cout
std::cout<<
<<"epsilon:
"epsilon:""<<<<epsilon
epsilon<<<<std::endl;
std::endl;
}}
Erros de principiante
• Chamar uma função sem objecto (por exemplo, Write(p) em vez de p.Write()).
• Esquecer o nome da classe ao definir uma função.
• Tentar usar os membros privados fora da classe.
• Tentar mudar o valor do objecto num selector (função const).
• Esquecer alguns dos #include.
• Usar parêntesis ao declarar um objecto com o construtor por defeito (por exemplo,
Point p(); em vez de Point p;.
• Usar listas de inicializadores em funções que não são construtores.
• Esquecer os parêntesis ao chamar uma função sem argumentos.
• Esquecer alguns consts,
• Usar cout, cin e endl sem o qualificador std::.
• Passar por valor argumentos de um tipo classe.
• Esquecer o especificador virtual nas declarações das funções ou incluí-lo nas
definições.
• Escrever um ponto e vírgula no final do cabeçalho numa definição de função.
• Esquecer o ponto e vírgula no final da classe.
• Esquecer o espaço de nomes.
• Usar a divisão inteira quando se queria a divisão exacta.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 50
Vectores
O C++ fornece directamente suporte para vectores, através
dos arrays: //... //...
int
inta[32];
a[32];
char
charm[80];
m[80];
double
doublex[100];
x[100];
double y[100];
double y[100];
mas::Point
mas::Pointp[8];
p[8];
//...
//...
A instrução for
Sintaxe (caso tradicional):
O operador ++
O operador ++ serve para incrementar de uma unidade o valor
de uma variável numérica.
Assim, em vez de
xx==xx++1;
1; ou xx+=
+=1;
1;
x++;
preferimos O ++ no nome C++ vem
deste operador...
Números aleatórios
A função ::rand() retorna um número pseudoaleatório entre 0
e uma constante RAND_MAX (que normalmente vale 32767).
A função ::srand(x) faz de x a semente do gerador.
Para obter a mesma sequência pseudoaleatória repetidamente,
basta dar a mesma semente.
Para obter sempre sequências diferentes, dá-se um semente
sempre diferente. Que semente? A hora actual em milésimos de
segundo, tal como medida instantaneamente pelo relógio do
computador: O valor ::time(0) representa a hora
::srand(static_cast<unsigned>(::time(0))); actual medida em milésimos de
::srand(static_cast<unsigned>(::time(0)));
//... segundos desde um certo dia no anos
//...
for 70. Para usar a função ::time é preciso
for(int
(inti i==0;
0;i i<<static_cast<int>(a.capacity());
static_cast<int>(a.capacity());i++)
i++)
a.push_back(::rand() % 100); fazer #include <ctime>.
a.push_back(::rand() % 100);
Expressão condicional
Vimos há pouco uma expressão condicional:
for
for(unsigned
(unsignedi i==0;
0;i i<<v.size()
v.size();;i++)
i++)
output
output << (i != 0 ? separator : "")
<< (i != 0 ? separator : "") <<
<<v[i];
v[i];
Sintaxe:
expressão1 ? expressão2 : expressão3
Semântica:
Primeiro avalia-se a expressão1. Se der true, avalia-se a
expressão2 e o resultado da expressão condicional é o
resultado da avaliação da expressão2. Se não, isto é, se a
expressão1 valer false, avalia-se a expressão3 e o
resultado da expressão condicional é o resultado da avaliação
da expressão3. Só uma das expressões 2 e 3 é avaliada.
A instrução if-else
Sintaxe:
Repare que a expressão tem
de estar entre parêntesis.
if (expressão)
instrução1
A instrução if é uma instrução: executa-se.
else A expressão condicional é uma expressão:
instrução2 avalia-se.
void
voidreserve(size_type
reserve(size_typen);
n); A função reserve aumenta a capacidade do vector
conforme indicado no argumento. Note bem:
aumenta a capacidade mas não o tamanho.
ifif(std::find(v.begin(),
(std::find(v.begin(),v.end(),
v.end(),x)!=
x)!=v.end())
v.end())
Existe algum elemento igual a x?
cout
cout<<
<<"Existe.
"Existe.""<<
<<endl;
endl;
else
else
cout
cout<<
<<"Não
"Nãoexiste.
existe.""<<
<<endl;
endl;
class
classPolynomial
Polynomial{{
O grau do polinómio vem no membro de dados
private:
private: degree.
int
intdegree;
degree;
std::vector<double>
std::vector<double>a;
a;
public: Os coeficientes vêm num vector de números double
public:
//...
a, tal que a[k] é o coeficiente de grau k. O número
//...
de coeficientes é maior ou igual a degree + 1.
};
};
O coeficiente de índice de grau degree nunca é
}}
zero, excepto se se tratar do polinómio nulo.
Construtores
Um construtor constrói um polinómio de grau zero, conhecido
o coeficiente de grau zero. O outro constrói um polinómio a
partir de um vector de coeficientes:
class Regra: quando um construtor tem um só argumento,
classPolynomial
Polynomial{{
////... qualificamo-lo explicit, para evitar que seja invocado para
...
public: realizar conversões automáticas.
public:
explicit
explicitPolynomial(double
Polynomial(doublea0
a0==0.0);
0.0);
Polynomial(const
Polynomial(const std::vector<double>&coefs);
std::vector<double>& coefs);
////...
...
};
};
Polynomial::Polynomial(double
Polynomial::Polynomial(doublea0):
a0):
degree(0),
degree(0), Polynomial::Polynomial(const
Polynomial::Polynomial(conststd::vector<double>&
std::vector<double>&coefs):
coefs):
a(1)
a(1) degree(static_cast<int>(coefs.size()) - 1),
degree(static_cast<int>(coefs.size()) - 1),
{{
a(static_cast<int>(coefs.size()))
a(static_cast<int>(coefs.size()))
a[0]
a[0]==a0;
a0; {{
}}
for
for(int
(inti i==0;
0;i i<=
<=degree;
degree;i++)
i++)
a[i]
a[i]==coefs[degree
coefs[degree--i];
i];
}}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 70
Valor
Para calcular o valor de um polinómio num ponto, usamos ou a
função Value ou o operador ():
class
classPolynomial
Polynomial{{
////...
... Este operador é um bocado
virtual
virtualdouble
doubleValue(double
Value(doublex)
x)const;
const; estranho. Veremos já a seguir
virtual
virtual double operator ()(doublex)
double operator ()(double x)const;
const; como se usa.
////...
...
};
};
Operador ()
O operador () é equivalente à função Value:
double
doublePolynomial::operator
Polynomial::operator()(double
()(doublex)
x)const
const
{{
return
returnValue(x);
Value(x);
}}
Implementações:
int
intPolynomial::Degree()
Polynomial::Degree()const
const
{{
return
returndegree;
degree;
}}
bool
boolPolynomial::Null()
Polynomial::Null()const
const void
voidPolynomial::Nullify()
Polynomial::Nullify()
{{ {{
return
returndegree
degree==
==00&&&&a[0]
a[0]==
==0.0;
0.0; for
for(int
(inti i==0;
0;i i<=
<=degree;
degree;i++)
i++)
}} a[i]
a[i]==0.0;
0.0;
degree
degree =0;
= 0;
}}
Somando polinómios
Para somar
polinómios usamos virtual
virtualvoid
voidAdd(const
Add(constPolynomial&
Polynomial&other);
other);
o modificador Add, virtual
virtual Polynomial& operator += (constPolynomial&
Polynomial& operator += (const Polynomial&other);
other);
virtual
virtualPolynomial
Polynomialoperator
operator++(const
(constPolynomial&
Polynomial&other);
o operador += e o other);
Sobrecarregando Add, += e +
Às vezes, só queremos somar um número:
virtual
virtualvoid
voidAdd(double
Add(doubley);
y);
virtual
virtual Polynomial& operator+=
Polynomial& operator +=(double
(doubley);
y);
virtual
virtualPolynomial
Polynomialoperator
operator++(double
(doubley);
y);
virtual
virtualvoid
voidSubtract(double
Subtract(doubley);
y);
virtual
virtual Polynomial& operator -=(double
Polynomial& operator -= (doubley);
y);
virtual
virtualPolynomial
Polynomialoperator
operator--(double
(doubley);
y);
Multiplicando polinómios
Temos três grupos de funções sobrecarregadas Multiply,
*= e *: para polinómios, para binómios representados por
um par de números e para número double:
virtual
virtualvoid
voidMultiply(const
Multiply(constPolynomial&
Polynomial&other);
other); Multiplicando por
virtual
virtual Polynomial& operator *= (constPolynomial&
Polynomial& operator *= (const Polynomial&other);
other); outro polinómio.
virtual
virtualPolynomial
Polynomialoperator
operator**(const
(constPolynomial&
Polynomial&other);
other);
virtual
virtualvoid
voidMultiply(const
Multiply(conststd::pair<double,
std::pair<double,double>&
double>&m);
m); Multiplicando por
virtual
virtualPolynomial&
Polynomial&operator
operator*=*=(const
(conststd::pair<double,
std::pair<double,double>& m); um binómio.
double>&m);
virtual
virtualPolynomial
Polynomialoperator
operator**(const
(conststd::pair<double,
std::pair<double,double>&
double>&m);
m);
virtual
virtualvoid
voidMultiply(double
Multiply(doubley);
y); Multiplicando por
virtual
virtual Polynomial& operator*=
Polynomial& operator *=(double
(doubley);
y); uma constante.
virtual
virtualPolynomial
Polynomialoperator
operator**(double
(doubley);
y);
Derivando e primitivando
A derivada de um polinómio é um polinómio. A primitiva
também:
virtual
virtualvoid
voidDifferentiate();
Differentiate();
virtual
virtual void Integrate(doublea0
void Integrate(double a0==0.0);
0.0);
void void
voidPolynomial::Integrate(double
Polynomial::Integrate(doublea0) a0)
voidPolynomial::Differentiate()
Polynomial::Differentiate() {{
{{ ifif(Null())
(Null())
for a[0]
a[0]==a0;
for(int
(inti i==1;
1;i i<=
<=degree;
degree;i++)
i++) a0;
else
else
a[i-1]
a[i-1]==i i**a[i];
a[i]; {{
ifif(degree Grow(degree
Grow(degree++1);
(degree>>0)
0) 1);
for
for (int i = degree;i i>=
(int i = degree; >=0;
0;i--)
i--)
degree--;
degree--; a[i+1] = a[i] / (i+1);
a[i+1] = a[i] / (i+1);
else
else a[0]
a[0]==a0;a0;
degree++;
degree++;
a[0]
a[0]==0.0;
0.0; }}
}} }}
double
doublePolynomial::Integral(double
Polynomial::Integral(doublex0,
x0,double
doublex1)
x1)const
const
{{
Polynomial
Polynomialtemp(*this);
temp(*this);
temp.Integrate();
temp.Integrate();
return
returntemp(x1)
temp(x1)--temp(x0);
temp(x0);
}}
Lendo e escrevendo
A classe inclui funções para ler e escrever um polinómio, num
formato simples:
virtual
virtualvoid
voidWrite(std::ostream&
Write(std::ostream&output
output==std::cout)
std::cout)const;
const;
virtual
virtual void WriteLine(std::ostream& output==std::cout)
void WriteLine(std::ostream& output std::cout)const;
const;
virtual
virtualvoid
voidRead(std::istream&
Read(std::istream&input
input==std::cin);
std::cin);
void
voidPolynomial::Write(std::ostream&
Polynomial::Write(std::ostream&output) output)const
const
{{
int
intdegree
degree==Degree();
Degree();
for void Polynomial::WriteLine(std::ostream& output) const
for (inti i==0;
(int 0;i i<=
<=degree;
degree;i++) i++) {void Polynomial::WriteLine(std::ostream& output) const
output
output<< <<""""<<<<a[degree
a[degree--i]; i]; {Write(output);
}} Write(output);
output
output<<<<std::endl;
std::endl;
}}
void
voidPolynomial::Read(std::istream&
Polynomial::Read(std::istream&input) input)
Ao escrever,
{{
escrevem-se só input
Ao ler, primeiro lê-se o grau e
input>> >>degree;
degree;
os coeficientes. Grow(degree
Grow(degree++1); 1);
depois os coeficientes. As
for (int i = 0; i <= degree;
for (int i = 0; i <= degree; i++)i++) demais posições são anuladas.
input
input>> >>a[degree
a[degree--i];
i];
for
for(int
(inti i==degree
degree++1;1;i i<<static_cast<int>(a.size());
static_cast<int>(a.size());i++)
i++)
a[i] = 0.0;
a[i] = 0.0;
}}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 86
Desenhando o gráfico
A função Plot escreve num ficheiro uma tabela de valores para
posterior envio para um programa capaz de desenhar gráficos:
virtual
virtualvoid
voidPlot(std::ostream&
Plot(std::ostream&output,
output,double
doublex0,
x0,double
doublex1,
x1,double
doubleh)
h)const;
const;
Testando o gráfico
Eis uma função de teste para a função Plot:
void
voidTestPlot()
TestPlot()
Exemplo de execução:
{{
std::ofstream
std::ofstreamoutput("plot.xls");
output("plot.xls");
mas::Polynomial
mas::Polynomialp; p;
std::cout
std::cout<<<<"grau
"graueecoeficientes:
coeficientes:";";
p.Read();
p.Read();
std::cout
std::cout<<<<p.Degree()
p.Degree()<< <<"-"-";
";
p.WriteLine();
p.WriteLine(); O ficheiro abre directamente no
double
doublea;
double
a;
b; Excel e produz o seguinte gráfico:
double b;
std::cout
std::cout<<<<"Intervalo:
"Intervalo:";"; 40
std::cin
std::cin >> a >>b;
>> a >> b; 30
double delta;
double delta; 20
std::cout
std::cout<<<<"Delta:
"Delta:";"; 10
std::cin >> delta;
std::cin >> delta; 0
p.Plot(a,
p.Plot(a,b,
b,delta,
delta,output);
output);
-4 -2
-10
0 2 4 6
}} -20
-30
-40
Não é obrigatório ter uma zona para funções estáticas, mas fica bem.
Triângulo de Pascal
Um exercício clássico de programação é escrever o
triângulo de Pascal:
1
11
121
1331
14641
1 5 10 10 5 1
....
Programe uma função estática na classe Polynomial para
fazer isto, até à ordem n.
Baseie-se numa função estática que calcula os números da
n-ésima linha:
static
staticPolynomial
PolynomialPascal(int
Pascal(intn);
n);////computes
computes(x
(x++1)^n;
1)^n;
Cuidado com o caso dos pontos da forma (x, 0). Para estes, o
polinómio Q(x), que se anula nas abcissas dos outros pontos e
na deste também, é o polinómio nulo. A solução do exemplo é o
polinómio x2-2x-3.
Classe Date
A classe Date reside no espaço de nomes mas:
namespace
namespacemas
mas{{ Três membros de dados,
para o ano, para o mês e
class
classDate
Date{{ para o dia.
private:
private:
int
intyear;
year;
int Um tipo enumerado para
intmonth;
month; os dias da semana.
int
intday;
day;
public:
public:
enum
enumWeekdayType
WeekdayType{MONDAY,
{MONDAY,TUESDAY,
TUESDAY,WEDNESDAY,
WEDNESDAY,THURSDAY,
THURSDAY,
FRIDAY, SATURDAY, SUNDAY};
FRIDAY, SATURDAY, SUNDAY};
Construtor por defeito, construtor
Date();
Date(); de cópia, construtor elementar.
Date(const
Date(constDate&
Date&other);
other);
Date(int
Date(intyear,
year,int
intmonth,
month,intintday);
day); ////pre
preValid(year,
Valid(year,month,
month,day);
day);
virtual
virtual~Date();
~Date(); Esta precondição usa uma função
//...
//... estática Valid, ainda por declarar.
}; Destrutor.
};
}}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 94
Mais classe Date
class
classDate
Date{{
//...
//... Selectores para o ano, para o mês,
para o dia, e para o número de dias
virtual desde o início. Início de quê? Início do
virtualint
intYear()
Year()const;
const; nosso calendário.
virtual
virtual int Month()const;
int Month() const;
virtual int Day() const;
virtual int Day() const;
virtual
virtualint
intCount()
Count()const;
const; Mudar o valor do objecto, com novos
valores para o ano, mês e dia.
virtual
virtualvoid
voidSet(int
Set(intyear,
year,int
intmonth,
month,int intday);
day); ////pre
preValid(year,
Valid(year,month,
month,day);
day);
virtual void Forth();
virtual void Forth(); Avançar 1 dia. Recuar 1 dia.
virtual
virtualvoid
voidBack();
Back(); //
//pre
preoperator
operator>>(First());
(First());
virtual void Add(int x); // pre x >=
virtual void Add(int x); // pre x >= 0; 0; Avançar x dias. Recuar x dias.
virtual
virtualvoid
voidSubtract(int
Subtract(intx);
x); //
//pre
prexx>=>=00&&&&DaysSince(First())
DaysSince(First())>= >=x;x;
virtual const Date& First() const;
virtual const Date& First() const; Data do primeiro dia do nosso calendário.
virtual int MonthSize() const;
virtual int MonthSize() const; Quantos dias tem o mês do objecto?
virtual
virtualint
intDaysTo(const
DaysTo(constDate&
Date&other)
other)const;
const; //
//pre
preoperator
operator<=
<=(other);
(other);
virtual int DaysSince(const Date& other) const; // pre operator >= (other);
virtual int DaysSince(const Date& other) const; // pre operator >= (other);
//...
//... Dias que faltam para other.
};
}; Dias que passaram desde other.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 95
Construtores de datas
O construtor por defeito, que dá a data de “hoje”, é o mais
sofisticado: A variável time0 recebe o tempo corrente,
Date::Date() calculado pela função de biblioteca ::time, que
Date::Date()
{{ dá o número de segundos desde as zero horas
time_t de 1 de Janeiro de 1970, UTC. Depois, a
time_ttime0
time0==::time(0);
::time(0);
struct função ::localtime converte esse número para
struct ::tm *now==::localtime(&time0);
::tm *now ::localtime(&time0);
year uma variável de tipo ::tm que tem membros
year==now->tm_year
now->tm_year++1900;1900;
month para o ano, mês, dia (e ainda outros que não
month==now->tm_mon
now->tm_mon++1; 1;
day = now->tm_mday; usamos aqui), tendo em conta a zona horária.
day = now->tm_mday;
}} Essa variável é apontada pela variável local
now. Para usar estas funções é preciso fazer
#include <ctime>. (Para mais informação,
Os outros construtores são consulte a documentação.)
rotineiros:
Date::Date(int
Date::Date(intyear,
year,int
intmonth,
month,int
intday):
day): Date::Date(const
Date::Date(constDate&
Date&other):
other):
year(year),
year(year), year(other.year),
year(other.year),
month(month),
month(month), month(other.month),
month(other.month),
day(day)
day(day) day(other.day)
day(other.day)
{{ {{
}} }}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 100
Destrutor de datas
O destrutor das datas não faz nada. No entanto, devemos
programá-lo:
Date::~Date()
Date::~Date()
{{
}}
Selectores de datas
Os selectores para o ano, O selector para o número de dias
para o mês, e para o dia desde o início do calendário
são muito simples: recorre à função DaysSince:
int
intDate::Year()
Date::Year()const
const int
intDate::Count()
Date::Count()const
const
{{ {{
return
returnyear;
year; return
returnDaysSince(first);
DaysSince(first);
}} }}
int
intDate::Month()
Date::Month()const
const first é o membro estático que dá
{{ a data do primeiro dia do
return
returnmonth;
month;
}} calendário. É inicializado assim:
const
constint
intDate::firstYear
Date::firstYear==1901;
1901;
int
intDate::Day()
Date::Day()const
const const
const Date Date::first(Date::firstYear,1,
Date Date::first(Date::firstYear, 1,1);
1);
{{
return
returnday;
day;
}}
const
constDate&
Date&Date::First()
Date::First()const
const A função First
{{
return
devolve a data do
returnfirst;
first;
}} primeiro dia.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 102
Modificadores de datas, avançando
Avança-se um dia com a Avança-se x dias com a
função Forth: função Add:
void
voidDate::Forth()
Date::Forth() void
voidDate::Add(int
Date::Add(intx) x)
{{ {{
ifif(day
(day<<MonthSize())
MonthSize()) for
for(int
(inti i==0;
0;i i<<x;
x;i++)
i++)
day++;
day++; Forth();
Forth();
else
else A função MonthSize }} Isto não é lá muito eficiente,
{{ diz quantos dias tem o se x for grande /. Por outro
day
day==1; 1; mês. Qual mês? O mês lado, se x for negativo, nada
ifif(month
(month<<12)12) acontece.
a que pertence o
month++;
month++; objecto.
else
else
{{ Avança-se x semanas
month
month==1; 1; somando 7*x dias:
year++;
year++;
}} void
voidDate::AddWeek(int
Date::AddWeek(intx)
x)
}} Se não estivermos no fim do mês, {{
}} incrementamos o dia. Se não, o dia é 1 e Add(7
Add(7**x);
x);
se o mês não for Dezembro, }}
incrementamos o mês. Se não, o mês é
Janeiro e incrementamos o ano.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 103
Anos bissextos
No calendário gregoriano, são bissextos os anos múltiplos de
4, excepto os múltiplos de 100, excepto (de entre destes) os
múltiplos de 400:
bool Na verdade, o que está
boolDate::LeapYear(int
Date::LeapYear(inty)
y)
{{ programado é: um ano é
return
returnyy%%400
400==
==00||||yy%%44==
==00 &&
&&yy%
%100
100!=
!=0;
0; bissexto se for múltiplo de
}} 400 ou se for múltiplo de
4 mas não de 100.
Esta função também é estática: só trabalha sobre o seu
argumento.
A instrução while
Sintaxe:
A instrução while já tinha surgido antes.
while (expressão) Fica aqui registada para referência.
instrução
Semântica:
1. Avalia a expressão. Se der false, termina; se não:
2. Executa a instrução;
3. Volta ao passo 1. O que se faz com while pode fazer-se com for e vice-
versa. A instrução while é mais expressiva quando
queremos colocar a ênfase na expressão de continuação.
A instrução for é mais expressiva quando queremos
realçar o papel da variável de controlo.
Operadores de comparação
Uma data é igual a outra se os seus membros de dados
tiverem os mesmos valores do que os da outra:
bool
boolDate::operator
Date::operator==
==(const
(constDate&
Date&other)
other)const
const
{{
return Usamos o this aqui apenas por uma questão de
returnthis->day
this->day==
==other.day
other.day estilo, para contrabalançar o other, mas
&&
&&this->month
this->month====other.month
other.month
&& poderíamos ter simplificado:
&&this->year
this->year==
==other.year;
other.year;
}} return day == other.day && ...;
Aqui podíamos ter escrito
Ser diferente é não ser igual:
return !(*this == other);
bool
boolDate::operator
Date::operator!=
!=(const
(constDate&
Date&other)
other)const
const mas não
{{
return *this != other;
return
return!!operator
operator==
==(other);
(other);
}} pois seria uma definição recursiva,
errada neste caso.
O argumento não é usado. Serve apenas para assinalar convencionalmente que estes são os
operadores pós-fixos. Sem este argumento fictício, estaríamos a definir os operadores prefixos.
Date&
Date&Date::operator
Date::operator+=(int
+=(intx)
x) Date&
Date&Date::operator
Date::operator-=(int
-=(intx)
x)
{{ {{
Add(x);
Add(x); Subtract(x);
Subtract(x);
return
return*this;
*this; return
return*this;
*this;
}} }}
Escrevendo datas
Temos as funções Write, WriteLine e o operador << na
combinação que se tornará habitual:
void
voidDate::Write(std::ostream&
Date::Write(std::ostream&output)
output)const
const
{{
output
output<<
<<year
year<<
<<""""<<
<<month
month<<
<<""""<<
<<day;
day;
}} Estas duas são
funções virtuais.
void Pertencem à classe
voidDate::WriteLine(std::ostream&
Date::WriteLine(std::ostream&output)
output)const
const Date.
{{
Write(output);
Write(output);
output
output<<
<<std::endl;
std::endl;
}}
std::ostream&
std::ostream&operator
operator<<
<<(std::ostream&
(std::ostream&output,
output,const
constDate&
Date&d)
d)
{{
d.Write(output); Esta é uma função
d.Write(output); amiga. Está fora da
return
returnoutput;
output;
}} classe.
Datas válidas
Todas as datas são válidas, isto é, todos os objectos da classe
Date representam datas válidas no calendário gregoriano, se
as funções forem usadas de acordo com as precondições.
No construtor elementar, por exemplo, a precondição
determina que os três argumentos devem poder formar uma
data válida. A função Valid exprime essa “validade”:
bool
boolDate::Valid(int
Date::Valid(inty,
y,int
intm,
m,int
intd)
d)
{{
return
returnValid(y,
Valid(y,m)
m)&&&&11<= <=dd&&&&dd<=
<=DaysInMonth(y,
DaysInMonth(y,m);
m);
}}
funções estáticas.
Estas três funções são
Sexta-feira 13
Nós, engenheiros, não somos supersticiosos. Mas é preciso
cuidado com as sextas-feiras 13. Destas, as piores são a 13ª
sexta-feira 13 de cada século, a 26ª, a 39ª, etc. Escrevamos
um programa para mostrar na consola as piores sextas-feiras
13 do século XXI (e marquemo-las já nas nossas agendas):
void
voidTestWorstFridays13OfThisCentury()
TestWorstFridays13OfThisCentury() Eis o resultado na consola.
{{ Infelizmente, o programa leva
mas::Date
mas::Dated(2001,
d(2001,1, 1,1);
1); muito tempo a calcular isto.
int
intcount
count==0;0; Porquê?
while
while((d.Year()
((d.Year()--1)
1)//100
100==
==20)
20)
{{
ifif(d.Day()
(d.Day()====1313&&&&d.Weekday()
d.Weekday()==
==mas::Date::FRIDAY)
mas::Date::FRIDAY)
{{
count++;
count++;
ifif(count
(count%%1313==
==0) 0)
d.WriteLine();
d.WriteLine();
}} Quantas são as piores
d.Forth();
d.Forth(); sextas-feiras 13 do
}} século XXI?
}}
Datas portuguesas
Quando usamos datas em português, queremos, além das
operações gerais sobre datas, funções para nomes dos dias
da semana e dos meses e funções para ver se um dia é
feriado.
Será a classe DatePortuguese.
A classe DatePortuguese herda da classe Date e acrescenta
funções estáticas para os nomes dos dias semana e para os
nomes dos meses e funções booleanas para os feriados.
namespace Repare na maneira de especificar
namespacemas
mas{{
que a classe DatePortuguese
class
classDatePortuguese:
DatePortuguese:public
publicDate
Date{{ herda da classe Date.
//...
//...
virtual
virtualconst
conststd::string&
std::string&MonthName()
MonthName()const;
const; Estas são as funções que dão o
virtual
virtual const std::string& WeekdayName()const;
const std::string& WeekdayName() const; nome do mês e o nome do dia da
//...
//... semana do objecto para o qual são
};
}; chamadas.
}}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 120
Feriados
Há uma função booleana para cada feriado:
class
classDatePortuguese:
DatePortuguese:public
publicDate
Date{{
//...
//... bool
virtual boolDatePortuguese::Holiday()
DatePortuguese::Holiday()const const
virtualbool
boolHoliday()
Holiday()const;
const; {{
return
returnNewYear()
NewYear()|| ||Liberty()
Liberty()||
||Workers()
Workers()
virtual
virtualbool
boolNewYear()
NewYear()const;
const; || Nation() || Assumption() || Republic()
virtual || Nation() || Assumption() || Republic()
virtualbool
boolLiberty()
Liberty()const;
const; ||
||AllSaints()
AllSaints()||
||Independence()
Independence()|| ||OurLady()
OurLady()
virtual
virtualbool
boolWorkers()
Workers()const;
const; || Christmas() || Carnival() || GoodFriday()
virtual || Christmas() || Carnival() || GoodFriday()
virtualbool
boolNation()
Nation()const;
const; ||
||Easter()
Easter()||||CorpusChristi();
CorpusChristi();
virtual
virtual bool Assumption()const;
bool Assumption() const; }}
virtual
virtualbool
boolRepublic()
Republic()const;
const;
virtual
virtual bool AllSaints()const;
bool AllSaints() const;
virtual
virtual bool Independence()const;
bool Independence() const;
virtual
virtualbool
boolOurLady()
OurLady()const;
const;
virtual
virtualbool
boolChristmas()
Christmas()const;
const;
virtual bool Carnival() const;
virtual bool Carnival() const; Os últimos quatro são os feriados
virtual
virtualbool
boolGoodFriday()
GoodFriday()const;
const; móveis: movem-se com a Páscoa.
virtual
virtualbool
boolEaster()
Easter()const;
const;
virtual
virtualbool
boolCorpusChristi()
CorpusChristi()const;
const;
//...
//...
};
};
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 121
Formatos de escrita
A função Write escreve de acordo com o formato, que é
especificado estaticamente (isto é, que é o mesmo para todos
os objectos da classe) pela função SetFormat:
class
classDatePortuguese:
DatePortuguese:public
publicDate
Date{{
public:
public:
enum
enumFormatType
FormatType{DEFAULT,
{DEFAULT,STANDARD,
STANDARD,STANDARD_FULL,
STANDARD_FULL,
TEXT,
TEXT, TEXT_WITH_WEEKDAY,ARMY,
TEXT_WITH_WEEKDAY, ARMY,NUMERIC};
NUMERIC};
private:
private: ////non
nonconst
conststatic
staticdata
datamembers
members
static FormatType format;
static FormatType format; A nova função Write redefine a
public:
public: função Write herdada.
//...
//...
virtual
virtualvoid
voidWrite(std::ostream&
Write(std::ostream&output
output==std::cout)
std::cout)const;
const;
//...
//...
public:
public:////static
static
static
static voidSetFormat(FormatType
void SetFormat(FormatTypenewFormat);
newFormat);
};
};
Por exemplo, eis o aspecto da data de início da segunda guerra do golfo nos
vários formatos, respectivamente: 2003 3 20; 03/03/20; 2003/03/20; 20 de
Março de 2003; quinta-feira, 20 de Março de 2003; 20MAR03; 20030320.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 122
Escrevendo a data portuguesa
A função Write agulha segundo o formato, usando uma
instrução switch:
void
voidDatePortuguese::Write(std::ostream&
DatePortuguese::Write(std::ostream&output) output)const
const
{{
switch
switch(format){
(format){ Note bem: se o formato for DEFAULT, usa-
case DEFAULT:
case DEFAULT: se a função Write da classe Date.
Date::Write(output);
Date::Write(output);
break;
break;
case Repare nos manipuladores: setfill
caseSTANDARD:
STANDARD: determina o carácter de
output
output<< <<std::setfill('0')
std::setfill('0')
<<
<<std::setw(2)
std::setw(2)<< <<Year()
Year()%%100100<< separator preenchimento; setw determina a
<<separator
<< std::setw(2) << Month() <<
<< std::setw(2) << Month() << separatorseparator largura do campo de escrita. É
<< std::setw(2) <<
<< std::setw(2) << Day() Day() preciso #include <iomanip>.
<<
<<std::setfill('
std::setfill('');
');
break;
break; Cada alternativa case termina por uma
case
caseSTANDARD_FULL:
STANDARD_FULL:
// instrução break. Sem isso, o controlo
//...
... passava à alternativa case seguinte.
break;
break;
case TEXT:
case TEXT:
output
output<< <<Day()
Day()<< <<""de de""<<
<<MonthName()
MonthName()<< <<""de de"" <<
<<Year();
Year();
break;
break;
//...
//... Restantes alternativas.
}}
}}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 123
A instrução switch
Sintaxe: Semântica:
switch (expression) { Primeiro avalia-se a expression. Depois, se
case expr_const1: houver uma etiqueta expr_const cujo valor é
statement1_1; igual ao da expression, o controlo passa para
... a instrução que vem a seguir a essa etiqueta,
break; e a execução prossegue sequencialmente até
case expr_const2: à próxima instrução break, a qual terminará a
statement2_1; instrução switch. Se nenhuma das etiquetas
tiver o valor igual ao da expression, usa-se a
...
etiqueta default, se houver. Se não houver, a
break;
instrução switch não tem efeito (para além do
... efeito eventualmente provocado pela
default: avaliação da expression.)
statement0_1;
... Note que se não houvesse aquelas instruções
É a mais break, o controlo seguiria sequencialmente
break; complicada de
até à chaveta } final.
} todas as instruções
do C++.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 124
O fim do prazo
Em muitas situações burocráticas temos um prazo de x dias
úteis para entregar certos papéis. Eis uma função de teste que
pede a data de início do prazo, o número de dias úteis e
escreve por extenso a data do fim do prazo.
void
voidTestDeadline()
TestDeadline()
{{
mas::DatePortuguese
mas::DatePortuguesed; d;
d.Accept("Início
d.Accept("Iníciodo doprazo
prazo(ano,
(ano,mês,
mês,dia)?
dia)?",","Data
"Datainválida.");
inválida.");
std::cout << "Prazo de quantos dias úteis?
std::cout << "Prazo de quantos dias úteis? "; ";
int
intx; x;
std::cin O primeiro ciclo conta os dias. O segundo
std::cin>> >>x; x;
while (x >
while (x > 0)0) avança até ao próximo dia útil.
{{
ifif(!d.Weekend()
(!d.Weekend()&& &&!d.Holiday())
!d.Holiday()) Aqui chamamos uma função estática através
x--;
x--;
d.Forth(); de um objecto da classe. Poderíamos ter
d.Forth(); feito de outra maneira, assim:
}}
while (d.Weekend() || d.Holiday())
while (d.Weekend() || d.Holiday()) mas::DatePortuguese::SetFormat(...)
d.Forth();
d.Forth();
d.SetFormat(mas::DatePortuguese::TEXT_WITH_WEEKDAY);
d.SetFormat(mas::DatePortuguese::TEXT_WITH_WEEKDAY);
d.WriteLine();
d.WriteLine();
}}
break ;
continue ; expression-statement
return expressionopt ; simple-declaration
block-declaration simple-declaration
...
Exemplo: Secret
Exemplifiquemos a classe std::string com um problema de cifra.
As mensagens em claro são formadas pelas 26 letras maiúsculas, sem
espaços, por exemplo ATACAMOSAOAMANHECER.
A cifra faz-se usando uma chave secreta, por exemplo FCTUNL, e um número
N secreto, por exemplo 4. As letras são cifradas, uma a uma, da esquerda
para a direita.
Uma letra que não pertença à chave é cifrada pela letra N posições à frente no
alfabeto, circularmente.
Uma letra que pertença à chave é cifrada por uma sequência de três letras: a
m-ésima letra da chave, seguida da letra N posições à frente no alfabeto (tal
como antes), seguida da (m+1)-ésima letra da chave. A variável m vale
inicialmente 1 e é incrementada de uma unidade de cada vez que esta regra é
utilizada.
O número N é escolhido de maneira a que a distância entre duas letras da
chave não seja N. Assim, conseguiremos sempre decifrar univocamente.
A mensagem do exemplo dá EFXCECGTEQSWESEQETRULIUGNIV.
Cifrando
std::string
std::stringSecret::Ciphered(const
Secret::Ciphered(conststd::string&
std::string&s) s)const
const
{{
O resultado tem no máximo o triplo
std::string
std::stringresult;
result; dos caracteres da mensagem.
result.reserve(3*s.size());
result.reserve(3*s.size()); Reservamos espaço com fartura,
int
intm m==0; 0; para a cadeia não ter de crescer a
for
for(int
(inti i==0;
0;i i<<static_cast<int>(s.size());
static_cast<int>(s.size());i++) i++) meio do processamento.
{{
std::string::size_type
std::string::size_typekk==key.find(s[i]);
key.find(s[i]);
ifif(k
(k==
==std::string::npos)
std::string::npos)
result.push_back(Shifted(s[i],
result.push_back(Shifted(s[i],number));
number)); Se a letra não pertence à chave.
else
else
{{
Se a letra pertence à chave.
result.push_back(key[m
result.push_back(key[m% %key.size()]);
key.size()]);
result.push_back(Shifted(s[i],
result.push_back(Shifted(s[i],number));
number));
result.push_back(key[(m+1)
result.push_back(key[(m+1)% %key.size()]);
key.size()]);
m++;
m++;
}} Atenção: repare na declaração Esta função ilustra o construtor por
}} da variável k e na utilização da defeito, as funções reserve, size,
return result;
return result; constante npos. Faça sempre find e push_back e o operador [].
}} assim.
Decifrando
std::string
std::stringSecret::Deciphered(const
Secret::Deciphered(conststd::string&
std::string&s)
s)const
const
{{
std::string
std::stringresult;
result;
// Recorde que a restrição sobre o valor de N
// left as anexercise...
left as an exercise...
garante que a decifração é unívoca.
return
returnresult;
result;
}}
template
template<class
<classT> T>
std::ostream&
std::ostream&operator
operator<<
<<(std::ostream&
(std::ostream&output,
output,const
conststd::list<T>&
std::list<T>&v)v)
{{
Write(v,
Write(v,""",",output);
output); Também há funções para escrever listas de trás para a
return
returnoutput;
output; frente: WriteBackwards e WriteBackwardsLine.
}}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 137
class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{ Construtor de cópia
private:
private:
//...
//... Construtores de conversão.
public:
public:
StringBasic();
StringBasic(); Construtor de concatenação.
StringBasic(const
StringBasic(constStringBasic&
StringBasic&other);
other);
StringBasic(const
StringBasic(conststd::string&
std::string&s);
s); Construtor de cópia parcial.
StringBasic(const
StringBasic(const char*s);
char *s);
StringBasic(const
StringBasic(constStringBasic&
StringBasic&other1,
other1,const
constStringBasic&
StringBasic&other2);
other2);
StringBasic(const
StringBasic(const StringBasic& other, int startPos,int
StringBasic& other, int startPos, intendPos);
endPos);
//
//pre
preother.ValidRange(startPos,
other.ValidRange(startPos,endPos);
endPos);
explicit StringBasic(char c);
explicit StringBasic(char c);
StringBasic(char Construtor de intervalo de caracteres.
StringBasic(charlowerBound,
lowerBound,char
charupperBound);
upperBound);
explicit
explicitStringBasic(int
StringBasic(intcapacity);
capacity); //
//pre
precapacity
capacity>=>=1; 1;
StringBasic(const StringBasic& other, int capacity);
StringBasic(const StringBasic& other, int capacity);
//
//pre
pre!other.Empty()
!other.Empty()&& &&capacity
capacity>=
>=1; 1;
// post Full();
// post Full(); Construtor de reserva (de
virtual capacidade).
virtual~StringBasic();
~StringBasic();
//...
//... Construtor de preenchimento
}} StringBasic herda de Clonable. repetitivo.
};
};
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 141
Destrutor
Os caracteres alocados dinamicamente com o operador new
no construtor têm de ser desalocados com o operador
delete[] no destrutor.
StringBasic::~StringBasic()
StringBasic::~StringBasic()
{{
delete
delete []
[] p;
p;
}}
virtual
virtualvoid
voidSet(const
Set(constStringBasic&
StringBasic&other);
other);
virtual
virtual void Set(const StringBasic& other,int
void Set(const StringBasic& other, intstartPos,
startPos,int
intendPos);
endPos);
//
// pre other.ValidRange(startPos,endPos);
pre other.ValidRange(startPos, endPos);
};
};
A técnica do SwapOut
A função SwapOut troca a representação interna da objecto
com a do argumento. O argumento é passado por referência
não constante:
void
voidStringBasic::SwapOut(StringBasic&
StringBasic::SwapOut(StringBasic&other)
other)
{{ A função de biblioteca
std::swap(capacity,
std::swap(capacity,other.capacity);
other.capacity); std::swap é uma função
std::swap(p,
std::swap(p,other.p);
other.p); genérica global que troca os
}} valores dos seus argumentos
int
intStringBasic::Capacity()
StringBasic::Capacity()const
const bool
boolStringBasic::Full()
StringBasic::Full()const
const
{{ {{
return
returncapacity;
capacity; return
returnCount()
Count()==
==capacity
capacity--1;
1;
}} }}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 150
Validação de índice, posição, intervalo
class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{
//... Estas funções são muito usadas
//... em precondições. Veja as
virtual
virtualbool
boolValidIndex(int
ValidIndex(intx)x)const;
const; páginas seguintes.
virtual
virtualbool
boolValidPosition(int
ValidPosition(intx)
x)const;
const;
virtual
virtualbool
boolValidRange(int
ValidRange(intx,x,int
inty)
y)const;
const;
};
};
bool
boolStringBasic::ValidIndex(int
StringBasic::ValidIndex(intx) x)const
const
Um índice é válido se lá existir {{
um carácter: return
return00<=<=xx&&&&xx<<Count();
Count();
}}
Acrescentando caracteres
Para acrescentar um carácter class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{
no final de uma cadeia que não virtual //...
//...
void
voidPut(char
Put(charc);
c); ////pre
pre!Full();
está cheia, usamos Put. Se não virtual void virtual
Extend(char c);
!Full();
virtual void Extend(char c);
pudermos garantir que a cadeia virtual virtualvoid
voidPrecede(char
Precede(charc);c);
não está cheia, usamos virtual
virtual void Insert(charc,
void Insert(char c,int
intx);
x);
Extend. Para acrescentar no // pre ValidPosition(x);
// pre ValidPosition(x);
início, usamos Precede. Para };
};
inserir no meio, usamos Insert:
void
voidStringBasic::Put(char
{{
StringBasic::Put(charc)c) Na função Put, sabemos que há
int
intxx==Count();
Count(); espaço para o novo carácter e
p[x] = c;
p[x] = c; acrescentamos à vontade.
De preferência, usamos
p[x+1] = 0;
p[x+1] = 0; Put.
}}
Inserindo caracteres
Na verdade, Precede insere na posição zero:
void
voidStringBasic::Precede(char
StringBasic::Precede(charc)
c)
{{
Insert(c,
Insert(c,0);
0);
}}
Este padrão, dois operadores [] e duas funções At é muito frequente. Também existe na STL
(mas aqui a função chama-se at, com minúscula).
Head e Tail
A função Head é um modificador que serve para guardar só os
n primeiros caracteres do objecto (os outros são eliminados).
Também há uma função Tail, que guarda os n últimos
caracteres:
class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{
//...
//...
virtual
virtualvoid
voidHead(int
Head(intn);
n); ////pre
prenn>=
>=0;
0;
virtual void Tail(int n);
virtual void Tail(int n); //
// pre n >=0;
pre n >= 0;
};
};
Esvaziando
Clear não limpa, esvazia!
Esvaziar uma cadeia é simples: basta colocar o terminador na
primeira posição:
void
void StringBasic::Clear()
StringBasic::Clear()
{{
*p
*p =
= 0;
0; Também se podia ter escrito p[0] = 0. Era
}} a mesma coisa, mas assim é mais “castiço”.
bool
boolStringBasic::operator
StringBasic::operator>=
>=(const
(constStringBasic&
StringBasic&other)
other)const
const
{{
return
return!!operator
operator<<(other);
(other);
}}
bool
boolStringBasic::operator
StringBasic::operator>>(const
(constStringBasic&
StringBasic&other)
other)const
const
{{
return
return!!operator
operator<=
<=(other);
(other);
}}
Aritmética de apontadores
A função Position exibe dois exemplos de aritmética de
apontadores:
int Se p é um apontador
intStringBasic::Position(char
StringBasic::Position(charc, c,int
intstart)
start)const
const
{{ char* e n é um inteiro int,
const a expressão p+n é um
constchar*
char*qq==::strchr(p+start,
::strchr(p+start,c); c);
return apontador char* que
returnstatic_cast<int>(q
static_cast<int>(q!=!=00??qq--pp::-1);-1);
}} Começamos a procurar start
aponta n posições à direita
posições à direita de p, isto é, de p.
começamos a procurar na posição
start da cadeia.
int
intStringBasic::Position(char
StringBasic::Position(charc, c,int
intstart)
start)const
const
Se p e q são apontadores
{{ char*, a expressão q-p é
const
constchar*
char*qq==::strchr(p+start,
::strchr(p+start,c);c);
um número inteiro que dá
return o número de posições
return static_cast<int>(q != 0 ? q--pp::-1);
static_cast<int>(q != 0 ? q -1);
}} entre p e q.
A diferença q – p dá precisamente o índice
onde está o carácter c, quando q não é zero. Logo:
(Começámos a procurar na posição start.) q – p == n se e só se p + n == q.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 170
Contando caracteres
Podemos contar os caracteres que sim, com CountIf e os que
não, com CountIfNot. (Que sim, que pertencem ao argumento;
que não, que não pertencem ao argumento):
class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{ Por exemplo, a expressão
//...
//... s.CountIf("AEIOUaeiou")
virtual
virtualint
intCountIf(const
CountIf(constStringBasic&
StringBasic&other)
other)const;
const;
virtual representa o número de
virtual int CountIfNot(const StringBasic&other)
int CountIfNot(const StringBasic& other)const;
const; vogais na cadeia s.
};
};
int
intStringBasic::CountIf(const
StringBasic::CountIf(constStringBasic&
StringBasic&other)
other)const
const
{{
int
intresult
result==0;
0; Esta função é pouco eficiente, pois compara cada
for (int i=0; p[i] != 0; i++)
for (int i=0; p[i] != 0; i++) carácter do objecto com cada carácter do
result += other.Has(p[i]);
result += other.Has(p[i]); argumento. É para usar casualmente em situações
return result;
return result; não muito exigentes.
}}
int
intStringBasic::CountIfNot(const
StringBasic::CountIfNot(constStringBasic&
StringBasic&other)
other)const
const
{{
return
returnCount()
Count()--CountIf(other);
CountIf(other);
}}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 171
Posições condicionais
Há uma série de funções PositionIf... que calculam a posição
de caracteres condicionalmente:
class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{
//...
//...
virtual
virtualint
intPositionIf(const
PositionIf(constStringBasic&
StringBasic&other)
other)const;
const;
virtual
virtual int PositionIfNot(const StringBasic& other)const;
int PositionIfNot(const StringBasic& other) const;
virtual
virtualint
intLastPositionIf(const
LastPositionIf(constStringBasic&
StringBasic&other)
other)const;
const;
virtual
virtualint
intLastPositionIfNot(const
LastPositionIfNot(constStringBasic&
StringBasic&other)
other)const;
const;
virtual
virtual int PositionIf(const StringBasic& other, int start)const;
int PositionIf(const StringBasic& other, int start) const; //
// pre
prestart
start>=
>=0;0;
virtual
virtual int PositionIfNot(const StringBasic& other, int start) const; // pre start >=0;
int PositionIfNot(const StringBasic& other, int start) const; // pre start >= 0;
virtual
virtualint
intLastPositionIf(const
LastPositionIf(constStringBasic&
StringBasic&other,
other,intintstart)
start)const;
const;
//
// pre
prestart
start<<Count();
Count();
virtual int LastPositionIfNot(const StringBasic& other, int
virtual int LastPositionIfNot(const StringBasic& other, int start) const; start) const;
//
// pre
prestart
start<<Count();
Count();
};
}; int
intStringBasic::PositionIf(const
StringBasic::PositionIf(constStringBasic&
StringBasic&other,other,int
intstart)
start)const
const
{{
Eis a defini- for
for(int
(inti i==start;
start;p[i]
p[i]!=
!=0;0;i++)
i++)
ção de duas ifif(other.Has(p[i]))
(other.Has(p[i]))
delas, sobre- return
returni;i; int
return -1; intStringBasic::PositionIf(const
StringBasic::PositionIf(constStringBasic&
StringBasic&other)
other)const
const
carregadas: }} return -1; {{
return
returnPositionIf(other,
PositionIf(other,0);
0);
}}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 172
Podando e aparando
As funções Prune... eliminam caracteres, por valor. As funções
Trim... eliminam espaços no início e no fim:
class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{ Eis as três Trim:
//...
//...
virtual void
voidStringBasic::TrimLeft()
virtualvoid
voidPrune(char
Prune(charc);
c); {{
StringBasic::TrimLeft()
virtual
virtual void PruneLast(charc);
void PruneLast(char c); int
intxx==PositionIfNot("
PositionIfNot("");
");
virtual
virtualvoid
voidPruneAll(char
PruneAll(charc);
c); ifif(x
(x ==-1)
== -1)
Clear();
Clear();
virtual
virtualvoid
voidPruneIf(const
PruneIf(constStringBasic&
StringBasic&other);
other); else
else
virtual
virtual void PruneIfNot(constStringBasic&
void PruneIfNot(const StringBasic&other);
other); Erase(0,
Erase(0,x-1);
x-1);
}}
virtual
virtualvoid
voidTrim();
Trim(); void
voidStringBasic::TrimRight()
virtual
virtual voidTrimLeft();
void TrimLeft();
StringBasic::TrimRight()
{{
virtual
virtualvoid
voidTrimRight();
TrimRight(); Head(LastPositionIfNot("
}; Head(LastPositionIfNot("")")++1);
1);
}; void
}}
voidStringBasic::Prune(char
StringBasic::Prune(charc)
c)
Eis a {{ void
voidStringBasic::Trim()
StringBasic::Trim()
definição da int
intxx==Position(c);
Position(c); {{
ifif(x
(x!=
!= -1)
-1) TrimRight();
TrimRight();
função Prune: RemoveAt(x); TrimLeft();
TrimLeft();
RemoveAt(x); }}
}}
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 173
Funções várias
Isto nunca mais acaba: SendToBack sem argumentos
“manda” o primeiro carácter para
class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{ o fim. Com argumentos, manda
//...
//... os n primeiros caracteres para o
virtual
virtualbool
boolStartsBy(const
StartsBy(constStringBasic&
StringBasic&other)
other)const;
const; fim. Analogamente para
virtual
virtualbool
boolEndsBy(const
EndsBy(constStringBasic&
StringBasic&other)
other)const;
const; BringToFront. As outras são
intuitivas.
virtual
virtualvoid
voidReverse();
Reverse();
virtual
virtualvoid
voidSwap(int
Swap(intx,x,int
inty);
y); //
//pre
preValidIndex(x)
ValidIndex(x)&&
&&ValidIndex(y);
ValidIndex(y);
virtual void SendToBack();
virtual void SendToBack();
virtual
virtualvoid
voidSendToBack(int
SendToBack(intn); n);//
//pre
prenn>=
>=0;0; A função Reverse use um
virtual void BringToFront();
virtual void BringToFront(); ciclo for com duas variáveis
virtual void BringToFront(int n); // pre n >=
virtual void BringToFront(int n); // pre n >= 0; 0; de controlo:
void
voidStringBasic::Reverse()
StringBasic::Reverse()
virtual
virtualvoid
voidToLowercase();
ToLowercase(); {{
virtual
virtual voidToUppercase();
void ToUppercase(); for
}; for(int
(inti=0,
i=0,j=Count()
j=Count()--1;
1;i i<<j;j;i++,
i++,j--)
j--)
}; Swap(i, j);
Swap(i, j);
}}
Excepções
Observe a função AsInt:
int
intStringBasic::AsInt(int
StringBasic::AsInt(intbase)
base)const
const
{{ Quem realmente faz o
int
int result;
result; trabalho de conversão é
char *endp;
char *endp; a função ::strtol. (Veja
errno =
errno = 0;0; a documentação.)
result = ::strtol(p, &endp, base);
result = ::strtol(p, &endp, base);
ifif(!*p
(!*p||||errno
errno||||*endp)
*endp)
{{
errno
errno==0; 0;
StringBasic
StringBasicmessage("\"",
message("\"",*this
*this++"\"
"\"isisnot
notaalegal
legalint
intvalue.");
value.");
throw message;
throw message;
}} Aqui a excepção é uma StringBasic que contém
return result;
return result; uma mensagem informativa construída à medida.
}}
Quando uma função “lança” uma excepção não faz mais nada. O
controlo passa à função que a chamou. Aí, das duas uma: ou há
um tratador de excepções catch que “apanha” a excepção e a
trata ou a excepção é propagada à função que chamou essa.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 180
Tratando excepções
A função IsInt (não confundir com AsInt) contém um tratador de
excepções. A ideia é simples: para saber se a cadeia é uma
cadeia inteira, converte-se com AsInt. Se der excepção, afinal
não era:
bool Repare no try-block seguido
boolStringBasic::IsInt(int
StringBasic::IsInt(intbase)
base)const
const
{{ do tratador de excepções.
bool
boolresult
result==true;
true; Quando há uma excepção no
try {
try { try-block para a qual existe um
AsInt(base);
AsInt(base);
}}catch
tratador, o tratador intervém,
catch(const
(constmas::StringBasic&)
mas::StringBasic&){{
result
result==false;
false;
substituindo o resto do bloco.
}} Se não houver tratador, a
return result;
return result; excepção propaga-se para o
}}
chamador.
usa: int
break;
break;
intnn==static_cast<int>(x);
static_cast<int>(x);
mas::StringBasic::Numeral(n).WriteLine();
mas::StringBasic::Numeral(n).WriteLine();
mas::StringBasic::Numeral(n,
mas::StringBasic::Numeral(n,2).WriteLine();
2).WriteLine();
mas::StringBasic::Numeral(n,
mas::StringBasic::Numeral(n,5).WriteLine();
5).WriteLine();
Observe que o resultado mas::StringBasic::Fixed(x).WriteLine();
das funções é um objecto mas::StringBasic::Fixed(x).WriteLine();
mas::StringBasic::Fixed(x,
mas::StringBasic::Fixed(x,2).WriteLine();
2).WriteLine();
de tipo StringBasic. mas::StringBasic::Scientific(x).WriteLine();
mas::StringBasic::Scientific(x).WriteLine();
mas::StringBasic::Scientific(x,
mas::StringBasic::Scientific(x,4).WriteLine();
4).WriteLine();
}}
2003-07-19 }}Programação com C++ © Pedro Guerreiro 2003
Curso de 184
Lendo e escrevendo
Para escrever StringBasics, temos Write, WriteLine e <<.
Para ler, temos Read e >>. Para ler interactivamente temos
Accept:
class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{
//...
//...
virtual
virtualvoid
voidWrite(std::ostream&
Write(std::ostream&output
output==std::cout)
std::cout)const;
const;
virtual
virtualvoid
voidWriteLine(std::ostream&
WriteLine(std::ostream&output=
output=std::cout)
std::cout)const;
const;
friend
friendstd::ostream&
std::ostream&operator
operator<<
<<(std::ostream&
(std::ostream&output,
output,const
constStringBasic&
StringBasic&s);
s);
virtual
virtualvoid
voidRead(std::istream&
Read(std::istream&input
input==std::cin);
std::cin);
friend
friend std::istream& operator >>(std::istream&
std::istream& operator >> (std::istream&input,
input,StringBasic&
StringBasic&s);
s);
virtual void Accept(const StringBasic& prompt);
virtual void Accept(const StringBasic& prompt); Note bem: Read e >> lêem até
};
}; ao fim da linha.
Constantes públicas
Eis cinco constantes prontas a usar:
class
classStringBasic:
StringBasic:public
publicClonable
Clonable{{
//...
//...
public:
public:// //static
staticconstants
constants
static
static const StringBasicuppercaseLetters;
const StringBasic uppercaseLetters;
static const StringBasic lowercaseLetters;
static const StringBasic lowercaseLetters;
static
staticconst
constStringBasic
StringBasicletters;
letters;
static const StringBasic digits;
static const StringBasic digits; Temos aqui membros de dados
static públicos, o que só é perdoável
staticconst
constStringBasic
StringBasicnull;
null; porque se trata de constantes.
};
};
Estratégia
Associamos a cada gene um número único, imaginando no gene
as letras substituídas pelos algarismos 1, 2, 3 e 4. Assim, cada
gene ficaria um numeral na base 5. Como não há mais do que 8
bases em cada gene, o maior número é 58-1, isto é, 390624.
Assim, os genes indexarão directamente o vector de contadores.
Uma vez terminada a contagem, criamos um vector de pares
<índice, frequência> e ordenamos por frequência. Finalmente,
escolhemos os índices de frequência máxima, são os primeiros
do vector.
Usaremos duas classes novas: Gene, para representar um gene,
e Genes, para representar o contador de genes.
Classe Genes
Temos um vector de inteiros e um vector de pares de inteiros. A
função Read lê e conta. A função Count diz quantos há de um
certo gene. A função Top dá a lista dos n genes mais frequentes.
A função ThoseMoreFrequentThan dá a lista dos genes cuja
frequência é maior ou igual a x:
class
classGenes
Genes{{
private:
private:
std::vector<int>
std::vector<int>count;
count;
std::vector<std::pair<int,
std::vector<std::pair<int,int>
int>>>frequencies;
frequencies;
public:
public:
virtual
virtual~Genes();
~Genes();
virtual
virtual voidRead(std::istream&
void Read(std::istream&input);
input);
virtual
virtual int Count(const Gene& g)const;
int Count(const Gene& g) const;
virtual
virtual std::list<Gene> Top(int n)const;
std::list<Gene> Top(int n) const;
virtual
virtual std::list<Gene> ThoseMoreFrequentThan(intx)
std::list<Gene> ThoseMoreFrequentThan(int x)const;
const;
};
};
Os mais frequentes
Os n mais frequentes:
std::list<Gene>
std::list<Gene>Genes::Top(int
Genes::Top(intn)n)const
const
{{
std::list<Gene>
std::list<Gene>result;
result;
for
for(int
(inti i==0,
0,jj==static_cast<int>(frequencies.size())
static_cast<int>(frequencies.size())--1;
1;i i<<n;
n;i++,
i++,j--)
j--)
result.push_back(Gene::FromInt(frequencies[j].second));
result.push_back(Gene::FromInt(frequencies[j].second));
return
returnresult;
result;
}}