Aula 5


Resumo da matéria

1.  Declaração de funções

Antes de se poder invocar uma função, é necessário que esta seja declarada, i.e., que o compilador saiba que ela existe e qual a sua interface.  Declarar uma função consiste pois em dizer qual o seu nome, qual o tipo do valor devolvido, quantos parâmetros tem e de que tipo são esses parâmetros.  Por exemplo:
void imprimeValorLógico(bool b);
ou simplesmente
void imprimeValorLógico(bool);
são possíveis declarações da função imprimeValorLógico() que se define abaixo:
// Esta função imprime "verdadeiro" ou "falso" consoante o valor
// lógico do argumento:
void imprimeValorLógico(bool b)
{
    if(b)
        cout << "verdadeiro";
    else
        cout << "falso";
}
Repare que na declaração não é necessário indicar os nomes dos parâmetros (se bem que na maioria dos casos seja conveniente fazê-lo para mostrar claramente ao leitor o que faz a função).

A sintaxe das declarações em sentido estrito é simples: o cabeçalho da função (como na definição) mas seguido de ;.   O facto de uma função estar declara não a dispensa de ter de ser definida mais cedo ou mais tarde: todas as funções têm de estar definidas em algum lado.
Uma definição, contendo também o cabeçalho da função, serve de declaração, pelo que após uma definição as declarações são redundantes.

2.  Sobrecarga de nomes de funções

Em certos casos é importante ter funções que fazem conceptualmente a mesma operação ou o mesmo cálculo, mas que operam com tipos diferentes de dados.  Seria pois de todo o interesse que o C++ permitisse a definição de funções de nomes idênticos variando apenas no tipo (ou no número) dos parâmetros.  É o que se passa na realidade.  Por exemplo, suponha que estão definidas as funções:
int soma(int, int);
float soma(float, float);
double soma(double, double);
Ao executar o seguinte código:
int i1, i2;
float f1, f2;
double d1, d2;
i2 = soma(i1, 4);         // invoca int soma(int, int).
f2 = soma(5.6f, f1);      // invoca float soma(float, float).
d2 = soma(d1, 10.0);      // invoca double soma(double, double).
são chamadas as funções apropriadas para cada tipo de argumentos usados.

3.  Funções recursivas

Uma função recursiva é aquela que contém no seu corpo uma chamada a si própria, por exemplo:
int factorial(int n)
{
    if(n == 0 || n == 1)
        return 1;
    else
        return n * factorial(n - 1);
}
Este tipo de funções pode ser muito útil na resolução de alguns problemas, mas deve ser usado com cautela.  A chamada de uma função recursivamente implica que as variáveis locais (parâmetros incluídos) são criadas (colocadas na memória) tantas vezes quantas a função é chamada, e só são destruídas (retiradas de memóra) quando as correspondentes chamadas retornam.  Caso ocorram muitas chamadas recursivas, não só pode ser necessária muita memória para para as várias versões das variáveis locais (uma versão por cada chamada), como também a execução pode tornar-se bastante lenta, pois a chamada de funções implica alguma perda de tempo nas tarefas de "arrumação da casa" do processador.  Para uma explicação mais detalhada do funcionamento deste tipo de funções, e das chamadas a funções em geral, aconselha-se a leitura do texto referente à aula teórica 4 (Capítulo 3).

4.  Operadores de atribuição abreviados

Em C++ existe um conjunto de abreviações possíveis para diversas expressões simples envolvendo o operador de atribuição.  Por exemplo:
i = i + 1;  i++;  ou ++i;
i = i - 1;  i--;  ou --i;
i = i + n;  i += n;
i = i - n;  i -= n;
i = i * n;  i *= n;
i = i / n;  i /= n;
i = i % n;  i %= n;
No caso das primeiras duas expressões, colocar o operador (++ ou --) antes (prefixo) ou depois (sufixo) da variável tem, em certos casos, consequências diferentes.  Mais adiante discutiremos a diferença entre estas duas maneiras de incrementar ou decrementar o valor de uma variável.

5.  Expressões booleanas

Uma expressão booleana (ou lógica) é qualquer expressão cujo resultado seja um valor booleano, i.e., true ou false.  Por exemplo:
a > 5           // verdadeira se a maior que 5.
a < 5 && b <= 7 // verdadeira se a menor que 5 e b menor ou igual a 7.
a < 5 || b <= 7 // verdadeira se a menor que 5 ou b menor ou igual a 7.
Os operadores usados neste tipo de expressões são os seguintes (relacionais, de igualdade, e lógicos): Importante:  Os operadores && e || avaliam os operandos da esquerda para a direita e terminam logo que o resultado seja conhecido.  Por exemplo, se na expressão booleana (condição1 && condição2 && condição3) a condição1 for falsa, nenhuma das outras condições é avaliada, dado que qualquer que seja o seu valor o resultado desta expressão é sempre false (propriedade absorvente).  Do mesmo modo, se na expressão (condição1 || condição2 || condição3), condição1 for verdadeira, o resultado é verdeiro e não se avalia nenhuma das outras condições.

6.  Constantes

Nos programas em C++ também se pode definir constantes, i.e. "variáveis" que não mudam de valor durante todo o programa.  Isso é feito usando a palavra-chave const.  Estas "variáveis" têm obrigatóriamente de ser inicializadas no acto da definição.  Por exemplo:
const int primeiro_primo = 1;
const char primeira_letra_do_alfabeto_latino = 'a';


Exercícios

1.a)  Crie uma função que calcule a média de três números de vírgula flutuante passados como argumentos.

1.b)  Crie um procedimento que divida três números de vírgula flutuante pela sua média.  Use a função criada acima para calcular a média.

2.  Crie três funções de soma (com o mesmo nome) para três tipos de dados diferentes (int, float e double).  Verifique, fazendo o traçado do programa, que o computador executa a função correcta para o tipo de argumentos usado quando é invocada a função.

3.  Faça um programa que teste os operadores relacionais, de igualdade, lógicos e de atribuição abreviados indicados acima.  Verifique o resultado de cada operação.  O resultado da execução do seu programa deve ser aproximadamente:

Insira dois numeros inteiros: 3 5
Atribuí a m o valor 3
Atribuí a n o valor 5
O resultado de m-- é: 2
O resultado de n++ é: 6
O resultado de n += m é: 8
...
O resultado de n > m é: false
O resultado de n <= m é: true
Nota:  Para que os booleanos sejam escritos por extenso, faça: cout << boolalpha; no início do programa (experimente primeiro sem esta instrução e interprete o resultado).

4.  Escreva uma função que calcule recursivamente uma potência inteira de um número decimal.  Lembre-se que xn = x xn-1.


Exercícios propostos

1.  Se já tem alguns conhecimentos sobre ciclos, escreva uma função não recursiva para calcular factorial de n.