int númeroDeItensVerdadeirosEm(vector<bool> const& v)
{
int contador = 0;
for(vector<bool>::size_type i = 0; i != v.size(); ++i)
if(v[i])
++contador;
return contador;
}
...
int main()
{
vector<bool> v(1000);
...
cout << númeroDeItensVerdadeirosEm(v) << endl;
}
++
sufixo
e prefixo++
prefixo para uma classe (neste
caso a classe Racional
) pode ser definido do seguinte modo:
o que permite escrever as seguintes instruções:...
class Racional {
public:
...
/**
Adiciona um à instância implícita, devolvendo-a por referência.@pre
*this
= r.@post
*this
= r + 1 eoperator+=
idêntico a*this
.*/
Racional& operator++();
...
};
...
Racional& Racional::operator++()
{assert(cumpreInvariante());
numerador_ += denominador_;
assert(cumpreInvariante());
return *this;
}
A definição do operador
Racional r = 1;
++ ++r;
++
sufixo para a classe
Racional
é feita acrescentando um parâmetro postiço do tipo int
:
O que permite escrever as seguintes instruções:...
class Racional {
public:
...
/**
Adiciona um à instância implícita, devolvendo-a por referência.@pre
*this
= r.@post
*this
= r + 1 eoperator+=
idêntico a*this
.*/
Racional& operator ++ ();
/**
Adiciona um à instância implícita, devolvendo o racional antes de incrementado.@pre
*this
= r.@post
*this
= r + 1 eoperator+=
= r.*/
Racional& operator ++(int);
...
};
...
Racional& Racional::operator++()
{assert(cumpreInvariante());
numerador_ += denominador_;
assert(cumpreInvariante());
return *this;
}
Racional Racional::operator++(int)
{assert(cumpreInvariante());
//
O parâmetro não é usado para nada! É um "truque sujo" do C++...
Racional cópia = *this; //
cria-se uma cópia da instância
//
implícita antes de incrementada!
operator ++(); //
invoca-se explicitamente a incrementação prefixa//
para incrementar a instância implícita.//
Também podia ser:++*this;
assert(cumpreInvariante() and cópia.cumpreInvariante());
return cópia; //
devolve-se a cópia com o valor da instância implícita//
antes de incrementada!}
No caso de o operador ser definido como rotina normal (não membro de uma classe), é necessário usar um parâmetro adicional para representar a variável a incrementar (visto que não há instância implícita numa rotina normal). Por exemplo:
Racional r = 1;
r++; //
utilização do operador sufixo.++ ++r; //
utilização do operador prefixo.
O que permite escrever as seguintes instruções:
enum DiaDaSemana {
segunda_feira,
...
domingo
};
int const número_de_dias_da_semana = 7;
//
Incrementação prefixa:DiaDaSemana& operator++(DiaDaSemana& dia)
{
if(dia == domingo)
return dia = segunda_feira;
else
return dia = DiaDaSemana(int(dia) + 1);
}
//
Incrementação sufixa:DiaDaSemana operator++(DiaDaSemana& dia, int)
{
DiaDaSemana cópia = dia;
++dia;
return cópia;
}
DiaDaSemana d = terça_feira;
++d; //
utilização do operador prefixo.d++; //
utilização do operador sufixo.
cout
. O canal
de entrada de dados pelo teclado é cin
. Para efectuar
uma operação de leitura de dados do teclado usa-se o operador de extracção
>>
. Para efectuar
uma operação de escrita de dados no ecrã usa-se o
operador de inserção <<
.
Por exemplo,
e
int a = 10;
cout << "Vou escrever o valor de 'a' a seguir a esta frase: " << a;
Após uma operação de entrada de dados, a variável toma o valor que foi inserido no teclado pelo utilizador do programa, desde que esse valor possa ser tomado por esse tipo de variável. É possível verificar se o valor introduzido estava correcto, ou seja, verificar se a operação de extracção teve sucesso: basta tratar o canal como um valor booleano. Se o resultado for verdadeiro, a leitura teve sucesso. Se for falso, a leitura falhou. Por exemplo:
int a;
cout << "Introduza um valor: ";
cin >> a;
cout << "Introduza um inteiro: ";
int i;
cin >> i;
if(not cin)
cout << "A leitura falhou! Não introduziu um inteiro..." << endl;
Alternativamente, e de forma mais clara e legível, pode-se usar uma operação para saber se o canal está em "bom estado":
cout << "Introduza um inteiro: ";
int i;
cin >> i;
if(not cin.good())
cout << "A leitura falhou! Não introduziu um inteiro..." << endl;
Ao ser executada uma operação de leitura como acima, o computador interrompe a execução do programa até que seja introduzido algum valor no teclado.
Quando é feita a leitura de um caracter do teclado (usando extracção
do canal cin
) é lido apenas um caractere, mesmo que no
teclado seja inserido um código (ou mais do que um caractere), i.e.,
o resultado das operações
seria
cout << "Insira um caractere: ";
char caractere;
cin >> caractere;
cout << "O caracter inserido é : " << caractere;
caso o utilizador inserisse 48. O dígito '8' foi ignorado visto que se leu apenas um caractere, e não o seu código.
Insira um caractere: 48
O caracter inserido é: 4
Mais genericamente, os canais são entidades que permitem ler ou escrever
informação sequencialmente a partir de algum dispositivo. Depois de ser fazer #include <iostream>
há (pelo menos)
três canais que ficam estabelecidos: cin
, cout
e cerr
. O primeiro é um canal de entrada e está
normalmente ligado ao teclado. O segundo é um canal de saída
e está normalmente associado ao ecrã (ou à janela
da consola de comandos...). O terceiro difere do segundo em pouco,
mas deve ser usado para escrever mensagens de erro.
É possível estabelecer canais de leitura (entrada) e escrita
(saída) ligados a ficheiros no disco do computador. Para isso
é necessário fazer #include <fstream>
, e definir
variáveis das classes ifstream
(para leitura) e ofstream
(para escrita), passando o nome do ficheiro ao seu construtor. O
nome do ficheiro deve ser passado na forma de uma cadeia de caracteres
clássica do C++ (i.e., uma matriz de caracteres com um terminador
especial), e não uma string
.
A abertura de um canal
de escrita para um ficheiro é destrutiva: se existir, o ficheiro
é esvaziado imediatamente. Tal como para os três canais preestabelecidos
já referidos, podem-se realizar operações de extracção e inserção a
partir de canais ligados a ficheiros, usando-se para isso os usuais operadores
de extracção >>
e de inserção <<
.
Um
canal pode ser interpretado como um booleano. Se o resultado for
verdadeiro, então o canal está estabelecido e em bom estado. Caso
contrário o canal está em mau estado, por exemplo porque não está
estabelecido (não liga a lado nenhum) ou porque uma operação de inserção ou
extracção falhou. O mesmo efeito se pode obter recorrendo à operação good()
.
Todas as operações de inserção e extracção
realizadas sobre um canal em mau estado falham, não produzindo qualquer
efeito. Para limpar uma condição de erro pode-se invocar o método clear()
para o canal em causa (e.g., cin.clear()
).
Por exemplo, o próximo programa lê um ficheiro (chamado notas1
)
consistindo numa sequência de linhas contendo um número de
aluno e duas notas e produz um ficheiro consistindo numa sequência
semelhante, mas com a média das notas (arredondada para cima):
#include <fstream>
#include <string>
using namespace std;
int main()
{
//
Guarda nomes dos ficheiros emstring
:string nome_entrada = "notas1";
string nome_saida = "notas2";
//
Estabelece canal de entrada (usa-se a função membrostring::c_str()
para se obter//
o nome do ficheiro na forma de uma cadeia de caracteres clássica):ifstream entrada(nome_entrada.c_str());
if(not entrada.good()) {
cout << "O ficheiro '" << nome_entrada << "' não parece existir!"
<< endl;
return 1;
}
ofstream saida(nome_saida.c_str());
if(not saida.good()) {
cout << "O ficheiro '" << nome_saida
<< "' não pôde ser criado/esvaziado!" << endl;
return 1;
}
int numero, nota1, nota2;
while(entrada >> numero >> nota1 >> nota2)
//
A guarda deste ciclo são três operações de extracção. Cada uma
//
devolve (uma referência para) o canal de entrada. O canal devolvido
//
pela última operação é interpretado como umbool
pelowhile
.
//
Assim, logo que a leitura falhar, presumivelmente por se ter//
atingido o fim do ficheiro de entrada, o ciclo termina.saida << ' ' << numero << ' ' << (nota1 + nota2 + 1) / 2 << endl;
//
Os canais são fechados automaticamente quando a funçãomain()
termina.}
Note-se que as classes ifstream
e ofstream
funcionam como especializações das classes istream
e ostream
,
respectivamente. Isto permite usar um ifstream
onde quer que
o C++ esteja à espera de um istream
, por exemplo. É este
facto que permite fazer com que a sobrecarga dos operadores de inserção e extracção
de canais para tipos definidos pelo utilizador também sirva para canais ligados
a ficheiros, como se verá mais tarde.