Baixe livro: Programação em C e outras Notas de estudo em PDF para Introdução à Programação de Computadores, somente na Docsity!
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 1
Curso de Programação com C++
http://ctp.di.fct.unl.pt/~pg/cpp
Fevereiro de 2003
por
Pedro Guerreiro
Departamento de Informática
Faculdade de Ciências e Tecnologia
Universidade Nova de Lisboa
2829-516 Caparica, Portugal
Objectivos do C++
Ser um C melhor.
Suportar a programação com tipos abstractos.
Suportar a programação orientada pelos objectos.
Suportar a programação genérica.
A linguagem de programação C++ foi inventada por Bjarne
Stroustrup, nos laboratórios Bell, com o objectivo de:
Logo, a linguagem C++ pode ser usada de várias maneiras,
com várias ênfases.
Uma linguagem tão
ambiciosa, com objectivos
tão vastos, provavelmente é
complicada /
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 3
Bibliografia Geral
The C++ Programming Language (3 rd^ edition), Bjarne
Stroustrup, 1997.
Programação com Classes em C++ , …, 2000.
STL Tutorial and Reference Guide , David Musser, Gillmer
Derge, Atul Saini, 2001.
Bibliografia Complementar
Introduction to Algorithms , Thomas H. Cormen, Charles E.
Leiserson, Ronald L. Rivest, Clifford Stein, 2001.
Object-Oriented Software Construction , Bertrand Meyer,
Elementos de Programação com C , ..., 2001.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 7
Tipos básicos
Números inteiros: tipo int.
Números reais: tipo double.
Booleanos: tipo bool.
Caracteres: tipo char.
E ainda:
Cadeias de caracteres: tipo std::string.
Os tipos numéricos (int e double) têm os
operadores aritméticos usuais: +, - , *, /. No tipo
int, a divisão é a divisão inteira, no tipo double
é a divisão exacta. No tipo int há ainda o
operador % para o resto da divisão inteira.
O tipo bool tem os operadores && (conjunção
lógica), || disjunção lógica) e! (negação).
Na verdade o tipo char também é um tipo
numérico...
Em rigor, este não é um tipo básico. É sim uma
classe da biblioteca STL.
As classes são tipos
A classe Point é usada para declarar objectos de tipo Point.
Um ponto
Point p1;
Point p2;
Point a[10];
Point p1;
Point p2;
Point a[10];
Outro ponto
Um quadro ( array ) com 10 pontos, indexados de 0 a 9.
Com os objectos de tipo ponto usam-se as funções da classe
Point. Observe a sintaxe:
p1.Translate(2.0, -3.25);
double x = p2.DistanceTo(p1);
a[0].Rotate(1.57079632679489661923);
a[3].Scale(-1.0, 1.0);
p1.Translate(2.0, -3.25);
double x = p2.DistanceTo(p1);
a[0].Rotate(1.57079632679489661923);
a[3].Scale(-1.0, 1.0);
Isto é apenas um
exemplo parvo. Não é
parte de nenhum
programa significativo.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 9
As funções têm um objecto
A funções da classe Point actuam sobre um objecto da clase
Point. Em cada caso, esse objecto é o objecto da função.
p1.Translate(2.0, -3.25);
double x = p2.DistanceTo(p1);
a[0].Rotate(1.57079632679489661923);
a[3].Scale(-1.0, 1.0);
p1.Translate(2.0, -3.25);
double x = p2.DistanceTo(p1);
a[0].Rotate(1.57079632679489661923);
a[3].Scale(-1.0, 1.0);
p1 é o objecto, 2.0 e –3-25 são os argumentos. O
valor de p1 muda.
O objecto da função é quem “sofre” o efeito da função.
p2 é o objecto, p1 é o argumento. O resultado da
função é guardado na variável x, aqui declarada.
a[0] é o objecto, 1.57... é o
argumento. O valor de a[0] muda.
a[3] é o objecto, -1.0 e 1.0 são os argumentos. O
valor de a[3] muda.
A classe Point
Há quatro
qualidades de
funções membro:
class Point { private: double x; double y; public: Point(); Point(double x, double y); Point(const Point& other); virtual ~Point();
virtual double X() const; virtual double Y() const;
virtual void Translate(double dx, double dy); virtual void Scale(double fx, double fy); virtual void Rotate(double angle); virtual double DistanceTo(const Point& other) const;
virtual double Angle() const; virtual double Modulus() const;
virtual void Write() const; virtual void WriteLine() const; };
class Point { private: double x; double y; public: Point(); Point(double x, double y); Point(const Point& other); virtual ~Point();
virtual double X() const; virtual double Y() const;
virtual void Translate(double dx, double dy); virtual void Scale(double fx, double fy); virtual void Rotate(double angle); virtual double DistanceTo(const Point& other) const;
virtual double Angle() const; virtual double Modulus() const;
virtual void Write() const; virtual void WriteLine() const; };
• Construtores
• Destrutor
• Selectores
• Modificadores
Construtores
Destrutor
Selectores
Modificadores
Mais selectores
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 13
Selectores
Os selectores são as funções da classe que consultam o
estado do objecto, sem o modificar.
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.
virtual double X() const;
virtual double Y() const;
virtual double Angle() const;
virtual double Modulus() const;
virtual double DistanceTo(const Point& other) const;
virtual void Write() const;
virtual void WriteLine() const;
virtual double X() const;
virtual double Y() const;
virtual double Angle() const;
virtual double Modulus() const;
virtual double DistanceTo(const Point& other) const;
virtual void Write() const;
virtual void WriteLine() const;
Regra: todos os selectores são
declarados virtual e const.
Modificadores
Os modificadores são as funções da classe que modificam o
estado do objecto. Não devolvem qualquer informação.
Regra: todos os modificadores são declarados virtual e
retornam void.
Os nomes das funções sugerem o seu significado.
virtual void Translate(double dx, double dy);
virtual void Scale(double fx, double fy);
virtual void Rotate(double angle);
virtual void Translate(double dx, double dy);
virtual void Scale(double fx, double fy);
virtual void Rotate(double angle);
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.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 15
Programação das classes
Cada classe C++ ocupa dois ficheiros. O ficheiro da
declaração e o ficheiro da implementação.
Além dos ficheiros das classes, cada programa mais um
ficheiro onde reside a função main. Um programa C++
começa pela primeira instrução da função main e termina
quando a função main retorna.
Para uma classe X, o ficheiro da
declaração chama-se X.h e o ficheiro de
implementação chama-se X.cpp.
Isto é uma convenção nossa,
para não nos perdermos. Em
rigor, os nomes dos ficheiros
podem ser quaisquer.
O ficheiro da declaração contém apenas a declaração da
classe, na forma que já observámos. O ficheiro de
implementação contém as definições das funções. Ainda não
vimos isso. Na gíria do inglês, o ficheiro de declaração é o header file e o de
implementação o source file.
Exemplo: ponto médio
Escrever um programa
(uma função main) que
aceite as coordenadas
de dois pontos,
construa os pontos e
calcule o ponto médio
do segmento por eles
formado. No final, para
confirmar que a
geometria não é uma
batata, mostra a
distância do ponto
médio a cada um dos
dois pontos iniciais (!)
int main() { double x; double y; std::cout << "Coordenadas do primeiro ponto: "; std::cin >> x >> y; Point p1(x, y); std::cout << "Coordenadas do segundo ponto: "; std::cin >> x >> y; Point p2(x, y); Point pm((p1.X() + p2.X()) / 2, (p1.Y() + p2.Y()) / 2); std::cout << "Ponto médio: "; pm.WriteLine(); double d1 = p1.DistanceTo(pm); double d2 = p2.DistanceTo(pm); std::cout << "Distâncias: " << d1 << " " << d2 << std::endl; return 0; }
int main() { double x; double y; std::cout << "Coordenadas do primeiro ponto: "; std::cin >> x >> y; Point p1(x, y); std::cout << "Coordenadas do segundo ponto: "; std::cin >> x >> y; Point p2(x, y); Point pm((p1.X() + p2.X()) / 2, (p1.Y() + p2.Y()) / 2); std::cout << "Ponto médio: "; pm.WriteLine(); double d1 = p1.DistanceTo(pm); double d2 = p2.DistanceTo(pm); std::cout << "Distâncias: " << d1 << " " << d2 << std::endl; return 0; }
Infelizmente, as letras
acentuadas e os cês
cedilhados saem mal na
consola /
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 19
Passagem de argumentos
Escolher uma ou outra é uma decisão de desenho , que
habitualmente não interfere com o significado da função.
Em C++, os argumentos podem passar de três maneiras: por
valor, por referência e por referência constante.
class Point { ... virtual double DistanceTo(Point other) const; ... };
class Point { ... virtual double DistanceTo(Point other) const; ... }; class Point { ... virtual double DistanceTo(Point& other) const; ... };
class Point { ... virtual double DistanceTo(Point& other) const; ... }; (^) class Point {
... virtual double DistanceTo(const Point& other) const; ... };
class Point { ... virtual double DistanceTo(const Point& other) const; ... };
Passagem por valor.
Passagem por referência.
Passagem por referência constante.
Por valor, por referência
• Quando um argumento passa por valor, a função trabalha
sobre uma cópia do valor do argumento. Logo, mesmo que o
argumento seja uma variável, nada que a função possa fazer
muda o valor dessa variável.
• Quando um argumento passa por referência, a função
trabalha directamente sobre a variável que constitui o
argumento. Poupa-se o trabalho de copiar o valor da
variável e ganha-se a possibilidade de modificar esse valor.
Mas o argumento tem mesmo de ser uma variável, não pode
ser uma expressão.
• Quando um argumento passa por referência constante, é
como no caso anterior, excepto que renunciamos
explicitamente ao direito de modificar o valor da variável. (Se
inadvertidamente o fizermos, o compilador dá erro.) Além
disso, se o argumento for uma expressão, o compilador gera
automaticamente uma variável com o valor da expressão.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 21
Passagem de argumentos: regra prática
A passagem por referência não constante usa-se em casos particulares,
por exemplo, quando os argumentos representam ficheiros:
Os argumentos de um tipo simples (int, double, char, bool,
apontador) passam por valor:
virtual void Scale(double fx, double fy); virtual void Rotate(double angle);
virtual void Scale(double fx, double fy); virtual void Rotate(double angle);
virtual double DistanceTo(const Point& other) const;virtual double DistanceTo(const Point& other) const;
virtual void Write(std::ostream& output = std::cout) const; virtual void WriteLine(std::ostream& output = std::cout) const; virtual void Read(std::istream& input = std::cin);
virtual void Write(std::ostream& output = std::cout) const; virtual void WriteLine(std::ostream& output = std::cout) const; virtual void Read(std::istream& input = std::cin);
Os argumentos de um tipo classe passam por referência
constante:
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.
Argumentos por defeito
Ocasionalmente, aparecem nas classes funções que têm
argumentos por defeito:
virtual void Write(std::ostream& output = std::cout) const; virtual void WriteLine(std::ostream& output = std::cout) const; virtual void Read(std::istream& input = std::cin);
virtual void Write(std::ostream& output = std::cout) const; virtual void WriteLine(std::ostream& output = std::cout) const; virtual void Read(std::istream& input = std::cin);
Se uma função destas for chamada sem argumento, usa-se o
argumento por defeito. Por exemplo:
Point p; ... p.WriteLine();
Point p; ... p.WriteLine();
Point p; ... p.WriteLine(std::cout);
Point p; ... p.WriteLine(std::cout);
é o mesmo do que
Outro exemplo: se quiséssemos que por defeito as rotações fossem de 90
graus, declararíamos:
virtual void Rotate(double angle = 1.57079632679489661923); virtual void Rotate(double angle = 1.57079632679489661923);
Não abusar! Às vezes
complica mais do que
simplifica.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 25
Instrução de afectação
Avalia a expressão após o que o
valor calculado passa a constituir o
valor da variável.
variável = expressão ;
Sintaxe: Semântica:
void Point::Set(double x, double y) { this->x = x; this->y = y; }
void Point::Rotate(double angle) { double x0 = x; double y0 = y; x = x0 * ::cos(angle) - y0 * ::sin(angle); y = x0 * ::sin(angle) + y0 * ::cos(angle); }
void Point::Set(double x, double y) { this->x = x; this->y = y; }
void Point::Rotate(double angle) { double x0 = x; double y0 = y; x = x0 * ::cos(angle) - y0 * ::sin(angle); y = x0 * ::sin(angle) + y0 * ::cos(angle); }
Atenção: this->x é o membro de
dados, x sozinho é o argumento.
class Point { ... virtual void Set(double x, double y); ... };
class Point { ... virtual void Set(double x, double y); ... };
Evitaríamos o this usando
argumentos com nomes diferentes
dos dos membros de dados.
Mais uma função...
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:
int n; double x; bool b; char c;
x = n; n = x;
n = b; b = n;
n = c; c = n;
int n; double x; bool b; char c;
x = n; n = x;
n = b; b = n;
n = c; c = n;
Tem o efeito esperado. Não há problema.
Inseguro: truncaria a parte decimal. O compilador gera warning :
“'=' : conversion from 'double' to 'int', possible loss of data.”
OK: false dá zero, true dá 1.
Inseguro: zero dá false, não zero dá true. O compilador gera
warning :” 'int' : forcing value to bool 'true' or 'false' (performance
warning)”
OK: n fica com o valor numérico de c.
OK, mas inseguro pois o valor de n pode não ser representável
no tipo char.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 27
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 n; double x; bool b; char c;
x = n; n = static_cast(x);
n = b; b = n != 0;
n = c; c = n;
int n; double x; bool b; char c;
x = n; n = static_cast(x);
n = b; b = n != 0;
n = c; c = n;
operador static_cast, no caso da
conversão para bool, usa-se o operador
!= (operador de desigualdade).
Nota: usa-se static_cast para forçar
conversão para double em expressões:
int n; int sum; ... double average = static_cast(sum) / n;
int n; int sum; ... double average = static_cast(sum) / n;
Afectações não simples
O operador += é um operador de afectação não simples. A
expressão x += y é equivalente a x = x + y (com a
vantagem de que a expressão x só é avaliada uma vez).
void Point::Translate(double dx, double dy)
x += dx;
y += dy;
void Point::Scale(double fx, double fy)
x *= fx;
y *= fy;
void Point::Translate(double dx, double dy)
x += dx;
y += dy;
void Point::Scale(double fx, double fy)
x *= fx;
y *= fy;
Este é o operador de afectação
não simples *=.
Programar assim:
void Point::Translate(...)
x = x + dx;
y = x + dy;
seria mau estilo.
x %= y x = x % y
x /= y x = x / y
x *= y x = x * y
x -= y x = x - y
Eis a tabela dos operadores x += y x = x + y
de afectação não simples
mais usuais:
Use Em vez de
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 31
this
Quando definimos uma função, frequentemente o objecto da
função fica implícito:
Ocasionalmente, queremos explicitar o objecto, por exemplo
quando os argumentos têm o mesmo nome do que os
membros de dados. Usamos então o apontador this:
double Point::Modulus() const
return ::sqrt(xx + yy);
double Point::DistanceTo(const Point& other) const
return ::sqrt(::pow(x - other.x, 2) + ::pow(y - other.y, 2));
double Point::Modulus() const
return ::sqrt(xx + yy);
double Point::DistanceTo(const Point& other) const
return ::sqrt(::pow(x - other.x, 2) + ::pow(y - other.y, 2));
void Point::Set(double x, double y)
this->x = x;
this->y = y;
void Point::Set(double x, double y)
this->x = x;
this->y = y;
Este x e este y são referenciam os membros de
dados do objecto da função, em cada chamada.
Aqui x e y sozinhos referenciam os argumentos e
this->x e this->y referenciam os membros de
dados do objecto.
Escrevendo
Em C++, escreve-se usando o operador de inserção <<:
Para escrever na consola, usa-se a stream std::cout:
void Point::Write(std::ostream& output) const
output << x << " " << y;
void Point::WriteLine(std::ostream& output) const
Write(output);
output << std::endl;
void Point::Write(std::ostream& output) const
output << x << " " << y;
void Point::WriteLine(std::ostream& output) const
Write(output);
output << std::endl;
std::cout << "Coordenadas do primeiro ponto: "; ... Point pm((p1.X() + p2.X()) / 2, (p1.Y() + p2.Y()) / 2); std::cout << "Ponto médio: "; pm.WriteLine(); double d1 = ...; double d2 = ...; std::cout << "Distâncias: " << d1 << " " << d2 << std::endl;
std::cout << "Coordenadas do primeiro ponto: "; ... Point pm((p1.X() + p2.X()) / 2, (p1.Y() + p2.Y()) / 2); std::cout << "Ponto médio: "; pm.WriteLine(); double d1 = ...; double d2 = ...; std::cout << "Distâncias: " << d1 << " " << d2 << std::endl;
Como std::cout é o argumento por defeito da função
WriteLine, escrever pm.WriteLine() é o mesmo do
que escrever pm.WriteLine(std::cout).
Tecnicamente, este std::endl é um manipulador que quando
enviado para uma stream de escrita provoca uma mudança
de linha.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 33
Lendo
Em C++, lê-se usando o operador de extracção >>:
Para ler da consola, usa-se a stream std::cin:
void Point::Read(std::istream& input)
input >> x >> y;
void Point::Read(std::istream& input)
input >> x >> y;
int x; int y; std::cout << "Coordenadas do primeiro ponto: "; std::cin >> x >> y; Point p1(x, y); std::cout << "Coordenadas do segundo ponto: "; std::cin >> x >> y; ...
int x; int y; std::cout << "Coordenadas do primeiro ponto: "; std::cin >> x >> y; Point p1(x, y); std::cout << "Coordenadas do segundo ponto: "; std::cin >> x >> y;
... Não surge no exemplo mas para uma variável^ p^ de tipo
Point escrever p.Read() seria o mesmo do que
escrever p.Read(std::cin).
class Point { ... virtual void Read(std::istream& input = std::cin); ... };
class Point { ... virtual void Read(std::istream& input = std::cin); ... };
Nova função:
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():
x(0),
y(0)
Point::Point(double x, double y):
x(x),
y(y)
Point::Point(const Point& other):
x(other.x),
y(other.y)
Point::Point():
x(0),
y(0)
Point::Point(double x, double y):
x(x),
y(y)
Point::Point(const Point& other):
x(other.x),
y(other.y)
A lista de inicializadores vem entre aquele sinal de dois pontos a seguir ao
cabeçalho e a chaveta a abrir. Só os construtores é que têm lista de
inicializadores.
No corpo dos construtores
programamos o que fizer falta.
Por exemplo, podemos escrever mensagens para debug :
Point::Point(): x(0), y(0) { std::cout << "Default constructor. " << std::endl; }
Point::Point(): x(0), y(0) { std::cout << "Default constructor. " << std::endl; }
Este caso é engraçado: o x
de fora é o membro de dados,
o x de dentro é o argumento.
( Idem para o y.)
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 37
Exemplos de formatação double
Estes são os resultados de
três execuções da função
TestDoubleOuput, da
página anterior.
Funções de teste
Para simplificar a organização do
nosso programa, usamos funções de
teste. A função main limita-se a
chamar uma das funções de teste.
#include // ...
#include "Point.h"
void TestPoints(); void TestDoubleOutput();
int main() { //TestPoints(); TestDoubleOutput(); return 0; }
void TestPoints() { //... }
void TestDoubleOutput() { //... }
#include // ...
#include "Point.h"
void TestPoints(); void TestDoubleOutput();
int main() { //TestPoints(); TestDoubleOutput(); return 0; }
void TestPoints() { //... }
void TestDoubleOutput() { //... }
Cada função de teste tem um protótipo, que constitui a
respectiva declaração. O protótipo é indispensável
porque as função vão ser chamadas na função main,
e as definições só aparecem depois.
A função main termina sempre com return 0;
Tipicamente, a função main chama apenas uma das
funções de teste. As outras ficam em comentário.
2003-07-19 Curso de Programação com C++ © Pedro Guerreiro 2003 39
Funções matemáticas de biblioteca
A biblioteca do C++ traz as funções matemáticas do costume:
double abs(double x);
double floor(double x); double ceil(double x);
double cos(double x); double sin(double x); double tan(double x); double acos(double x); double asin(double x); double atan(double x); double atan2(double y, double x);
double sinh(double x); double cosh(double x); double tanh(double x);
double sqrt(double x); double exp(double x); double log(double x); double log10(double x); double pow(double x, double y);
double abs(double x);
double floor(double x); double ceil(double x);
double cos(double x); double sin(double x); double tan(double x); double acos(double x); double asin(double x); double atan(double x); double atan2(double y, double x);
double sinh(double x); double cosh(double x); double tanh(double x);
double sqrt(double x); double exp(double x); double log(double x); double log10(double x); double pow(double x, double y);
abs: valor absoluto.
floor: maior número inteiro (enquanto número double)
menor ou igual a x: ceil: menor número inteiro (enquanto
número double) maior ou igual a x.
Funções trigonométricas coseno, seno, tangente, arco-
coseno, arcosseno, arcotangente. A função arcotangente
tem duas variantes: atan tem resultado no intervalo –
p /2, p /2; atan2(y, x) calcula o arcotangente de y/x no
intervalo –p , p , usando os sinais de y e x para
determinar o quadrante. Calcula mesmo se x valer zero,
mas não se x e y valerem zero.
Para usar isto é preciso fazer
#include .
Raiz quadrada, exponencial, logaritmo natural, logaritmo
decimal, potência (x elevado a y).
Funções hiperbólicas.
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.