Servem para definir novos tipos que consistem num conjunto finito e enumerado de diferentes valores.
Os identificadores
enum nome {valor
1, valor
2,
..., valor
n};
valor
1
a valor
n ficam
com os valores 0 a n - 1, excepto se algum deles for explicitamente
inicializado, caso em que a numeração recomeça a partir
do valor que lhe for atribuído.Pode-se definir um tipo enumerado para representar os dias da semana:
enum DiaDaSemana {
segunda_feira,
terça_feira,
quarta_feira,
quinta_feira,
sexta_feira,
sábado,
domingo
};
Este tipo pode ser utilizado como se segue:
DiaDaSemana dia;
dia = terça_feira;
É possível converter de enumerado para int
:
e também de
DiaDaSemana dia = quarta_feira;
int número_do_dia = int(dia);
cout << número_do_dia << endl; //
surge2
no ecrã.
int
para enumerado:
int número_do_dia = 5;
DiaDaSemana dia = DiaDaSemana(número_do_dia); //
fica comsábado
.
Declaração:
Definição:
tipo_devolvido operator operador(lista_de_parâmetros);
O número de argumentos (ou seja, de operandos) de um determinado operador é fixo. Por exemplo, o operador
tipo_devolvido operator operador(lista_de_parâmetros)
{
}
/
tem obrigatoriamente de
ter dois argumentos. No entanto, existem símbolos que correspondem
a mais do que um operador. Por exemplo, o símbolo -
pode corresponder quer ao operador subtracção (operador binário,
com dois operandos) quer ao operador simétrico (operador unário
prefixo, com um único operando).
Definição:
Utilização:
enum DiaDaSemana {
segunda_feira,
...
domingo
};
int const número_de_dias_da_semana = 7;
DiaDaSemana operator+(DiaDaSemana const dia, int const salto)
{
return DiaDaSemana((int(dia) + salto) % número_de_dias_da_semana);
}
DiaDaSemana operator+(int const salto, DiaDaSemana const dia)
{
return dia + salto;
}
Definição do operador de incrementação prefixa:
DiaDaSemana x = terça_feira;
int z = 2;
DiaDaSemana a = x + z; //
inicializada com o valorquinta_feira
.DiaDaSemana b = 3 + x; //
inicializada com o valorsexta_feira
.
Utilização do operador:
DiaDaSemana& operator++(DiaDaSemana& dia)
{
if(dia == domingo)
dia = segunda_feira;
else
dia = DiaDaSemana(int(dia) + 1);
return dia;
}
DiaDaSemana x = terça_feira;
++x; // x
passa a ter o valorquarta_feira
.
Uma classe é constituída por um conjunto de membros ou características. Os membros podem ser atributos, operações, tipos, etc. Os atributos correspondem a instâncias (variáveis ou constantes) membro que cada instância da classe possui. Uma instância de uma classe é uma variável dessa classe. As operações são rotinas membro, que podem ser invocados através de instâncias da classe. Às instâncias de uma classe também se chama objectos e a invocação de uma operação através de um dado objecto é muitas vezes expressa dizendo que se passou uma mensagem ao objecto. À implementação de uma operação chama-se o respectivo método.
class Nome {
//
Caso não seja indicada a categoria de acesso esta é (por omissão) privada.public:
//
Declaração dos membros públicos da classe entre os quais normalmente//
existem os construtores, se necessários.//
Estes membros podem ser acedidos sem limitações.//
Não devem existir quaisquer variáveis membro públicas!
private://
Declaração dos membros privados da classe.//
Estes membros só podem ser acedidos a partir de outros membros da//
própria classe (e em outros casos a ver mais tarde).};
Definição de uma classe denominada Complexo
para representar números complexos:
Esta classe pode-se usar como se segue:
/**
Representa números complexos.@invariant V.
*/
class Complexo {public:
/**
Constrói um novo número complexo com partes real e imaginária dadas.@pre V.
@post
Complexo
.parteReal
() =parte_real
e.
ComplexoparteImaginária
() =parte_imaginária
.*/
Complexo(double const parte_real, double const parte_imaginária);
/**
Devolve a parte real do complexo.@pre V.
@post
parteReal
() = parte real do complexo.*/
double parteReal();
/**
Devolve a parte imaginária do complexo.@pre V.
@post
parteImaginária
() = parte imaginária do complexo.*/
double parteImaginária();
...
//
outros membros públicos (tipicamente operações).
private:
double parte_real;
double parte_imaginária;
};
double Complexo::parteReal()
{
return parte_real;
}
double Complexo::parteImaginária()
{
return parte_imaginária;
}
Complexo c(2.1, 3.0); //
construção de uma nova variável do tipoComplexo
.
cout << "A parte real de c é: " << c.parteReal() << endl;
cout << "A parte imaginária de c é: " << c.parteImaginária() << endl;
//
A instrução seguinte não é possível porqueparte_real
é um membro privado
//
da classeComplexo
.cout << "A parte real de c é : " <<
c.parte_real<< endl;
Normalmente os nomes das classes começam por maiúsculas e correspondem a substantivos.
Depois de se definir uma variável x de uma classe C
a frase "o x (ou a variável x) é um C"
deve estar gramaticalmente correcta. Dadas as instruções
acima, pode-se dizer perfeitamente "o c
é um Complexo
",
pelo que o nome dado à classe é apropriado. Normalmente isso
significa que o nome de uma classe é uma substantivo ou frase substantiva*.
A relação que existe entre uma variável de uma classe e essa classe é a relação "é uma instância de". É (presumivelmente) o mesmo tipo de relação que existe entre o leitor desta frase e a classe dos Humanos.
* No entanto note-se que a palavra "complexo", na acepção usada é um substantivo, embora na sua origem seja um adjectivo.
escreve no ecrã:
Complexo c(2.1, 3.0), d(10.1, 11.2);
d = c;
cout << "A parte real de d é: " << d.parteReal() << endl;
cout << "A parte imaginária de d é : " << d.parteImaginária() << endl;
As instâncias de uma classe definida pelo utilizador podem ser usadas exactamente da mesma forma que as instâncias dos tipos básicos do C++, podendo (em geral) ser passadas como argumentos de rotinas e devolvidas por funções.
A parte real de d é: 2.1
A parte imaginária de d é: 3
* É possível proibir atribuições entre instâncias de uma classe desde que se declare o respectivo operador de atribuição como privado, como se verá.
Um construtor de uma classe é invocado sempre que uma instância dessa classe é construída e é utilizado para inicializar correctamente os atributos da classe.
Podem existir vários construtores sobrecarregados para cada classe, desde que tenham assinaturas diferentes (listas de parâmetros diferentes).
Declaração:
Definição:...
class Complexo {
public:
/**
Constrói um novo número complexo com partes real e imaginária dadas.@pre V.
@post
Complexo
.parteReal
() =parte_real
e.
ComplexoparteImaginária
() =parte_imaginária
.*/
Complexo(double const parte_real, double const parte_imaginária);
...
};
ou, tirando partido da possibilidade de haver parâmetros e atributos com o mesmo nome,
Complexo::Complexo(double const r, double const i)
: parte_real(r), parte_imaginária(i) //
isto é uma lista de inicializadores.{
}
Complexo::Complexo(double const
parte_real, double const
parte_imaginária
)
: parte_real(parte_real), parte_imaginária(parte_imaginária)
{
}
ou ainda,
Sempre que possível prefira listas de inicializadores a atribuições.
Complexo::Complexo(double r, double i)
{
//
Aqui não se inicializa: atribui-se. Mas o efeito prático é o mesmo neste caso.parte_real = r;
parte_imaginária = i;
}
O destrutor é invocado automaticamente sempre que uma instância de uma classe é destruída e serve para executar todo o código necessário para que o sistema possa funcionar coerentemente após a destruição dessa instância: serve para "arrumar a casa". A utilidade do destrutor será mais óbvia quando for introduzido o conceito de instância dinâmica, no início do segundo semestre.
Só pode existir um destrutor em cada classe. Em muitos casos o destrutor pode ser omitido, pois a linguagem fornece um sempre que possível e que, na maioria dos casos de interesse neste momento, age de forma apropriada.
Declaração:
...
class Complexo {
public:
/**
Destrói o número complexo.@pre V.
*/
~ Complexo();
...
};
Definição:
Complexo::~Complexo()
{//
Este destrutor não precisa de fazer nada e poderia ser omitido.}
Declaração:
Definição:
//
Numa invocação desta função o primeiro argumento é obrigatório e os dois últimos
//
são opcionais:int somaDe(int const a, int const b = 1, int const c = 1);
Utilização:
int somaDe(int const a, int const b, int const c)
{
return a + b + c;
}
//
Todas estas invocações da funçãosomaDe()
são válidas:int i;
i = somaDe(1, 2, 3); // a
= 1,b
= 2,c
= 3.i = somaDe(1, 2); // a
= 1,b
= 2,c
= 1 (por omissão).i = somaDe(1); // a
= 1,b
= 1 (por omissão),c
= 1 (por omissão).
class
e struct
class
para definir um
novo tipo de dados, todos os membros para os quais não é
indicada a categoria de acesso (público ou privado) são,
por omissão, privados.
Quando é utilizada a palavra-chave struct
para definir
um novo tipo de dados, todos os membros para os quais não é
indicada a categoria de acesso (público ou privado) são,
por omissão, públicos.
Prefira a definição
com a palavra chave class
. Reserve a palavra chave struct
para os raros casos em que se pretende mesmo que todos os membros sejam
públicos. Nesses casos não se fala em classe, mas sim
em agregado de informação heterogénea (agregados
de informação homogénea são as matrizes).
Nota importante para quem já
aprendeu C: Existem algumas diferenças entre as possibilidades
de utilização da palavra-chave struct
em C e em
C++, nomeadamente:
typedef
para indicar que estamos a criar um novo tipo de dados.struct
podem ter operações, que não existem em C.A
utilidade de const
no final dos cabeçalhos de algumas das
operações e métodos da classe abaixo
será vista em aulas posteriores.
#include <iostream>
#include <cmath>
using namespace std;
/**
Representa números complexos.@invariant V.
*/
class Complexo {public:
/**
Constrói um novo número complexo com partes real e imaginária dadas.@pre V.
@post
Complexo
.parteReal
() =parte_real
e.
ComplexoparteImaginária
() =parte_imaginária
.*/
Complexo(double const parte_real = 0.0,
double const parte_imaginária = 0.0);
/**
Devolve a parte real do complexo.@pre V.
@post
parteReal
() = parte real do complexo.*/
double parteReal() const;
/**
Devolve a parte imaginária do complexo.@pre V.
@post
parteImaginária
() = parte imaginária do complexo.*/
double parteImaginária() const;
/**
Devolve o módulo do complexo.@pre V.
@post
módulo
() = módulo do complexo.*/
double módulo() const;
/**
Devolve o ângulo do complexo.@pre V.
@post
ângulo
() = ângulo do complexo.*/
double ângulo() const;
/**
Mostra o complexo no ecrã.@pre V.
@post ecrã passou a conter representação textual do complexo.
*/
void mostra() const;
private:
double parte_real;
double parte_imaginária;
};
Complexo::Complexo(double const parte_real, double const parte_imaginária)
: parte_real(parte_real), parte_imaginária( parte_imaginária)
{
}
double Complexo::parteReal() const
{
return parte_real;
}
double Complexo::parteImaginaria() const
{
return parte_imaginária;
}
double Complexo::módulo() const
{
return sqrt(parteReal() * parteReal() +
parteImaginária() * parteImaginária());}
double Complexo::ângulo() const
{
return atan2(parteImaginária(), parteReal());
}
void Complexo::mostra() const
{
if(0.0 < parteImaginária())
cout << parteReal() << "+i×" << parteImaginária();
else if(parteImaginária() == 0.0)
cout << parteReal();
else // parteImaginária()
< 0.0cout << parteReal() << "-i×" << -parteImaginária();
}
Complexo const operator+(Complexo const& a, Complexo const& b)
{
Complexo soma(a.parteReal() + b.parteReal(),
a.parteImaginaria() + b.parteImaginaria());
return soma;
//
Ou simplesmente:// return Complexo(a.parteReal() + b.parteReal(),
a.parteImaginaria() + b.parteImaginaria());
}
int main()
{
Complexo x(1.1, 0.5), y(2.2, -4.5), z;
Complexo w = x + y + z;
z.escreve();
cout << endl;
//
Aparece:// 3.3-i×4.0
}