• Tornar-se apto a programar funções e procedimentos • Familiarizar-se com o conceito de passagem de parâmetros • Reconhecer quando usar parâmetros por valor e por referência • Apreciar a importância de comentários em funções
• Tornar-se apto a determinar o escopo de variáveis • Minimizar o uso de efeitos colaterais e variáveis globais
• Desenvolver estratégias para decompor tarefas complexas em mais simples • Documentar as responsabilidades de funções e seus invocadores com pré-condições Funções são um bloco fundamental de construção de programas C++. Uma função en- capsula uma computação em uma forma que possa ser facilmente entendida e reutilizada. Neste capítulo, você vai aprender como projetar e implementar suas próprias funções e co- mo dividir tarefas complexas em conjuntos de funções cooperantes.
5.1 Funções como caixas pretas 158 5.2 Escrevendo funções 159
Sintaxe 5.1: Definição de função 160
Dica de produtividade 5.1: Escreva funções pensando na reutilização 162
5.3 Comentários em funções 162
Dica de produtividade 5.2: Pesquisa e substituição globais 164
Dica de produtividade 5.3: Expressões regulares 165 5.4 Valores de retorno 165
Sintaxe 5.2: Comando return 167
Erro freqüente 5.1: Esquecer o valor de retorno 168
5.5 Parâmetros 168
Dica de qualidade 5.1: Use nomes significativos para parâmetros 169
Erro freqüente 5.2: Incompatibilidade de tipos 170
Tópico avançado 5.1: Declaração de funções 170
Sintaxe 5.3: Declaração (ou protótipo) de função 170 5.6 Efeitos colaterais 171 5.7 Procedimentos 172
5.1
Funções como caixas pretas
Você tem usado diversas funções que foram fornecidas com a biblioteca do sistema C++. Exemplos são
sqrt Calcula a raiz quadrada de um número em ponto flutuante getline Lê uma linha de um stream
Você provavelmente não sabe como estas funções realizam o seu trabalho. Por exemplo, como a sqrtcalcula raízes quadradas? Olhando valores em uma tabela? Por repetidas tentativas de adi- vinhar a resposta? Você vai realmente aprender no Capítulo 6 como calcular raízes quadradas usan- do nada mais do que aritmética básica, mas você não precisa saber os detalhes da computação pa- ra usar a função raiz quadrada. Você pode pensar na sqrtcomo uma caixa preta, como mostrado na Figura 1.
Quando você usa sqrt(x)dentro da main, o valor de entrada ou valor do parâmetro xé transferido ou passado para a funçãosqrt. A execução da função mainé temporariamente sus- pensa. A função sqrtse torna ativa e calcula a saída ou valor de retorno — a raiz quadrada do valor de entrada — usando algum método que (nós confiamos) irá fornecer o resultado correto. Es- te valor de retorno é transferido de volta para a main, que retoma a computação usando o valor de retorno. O valor de entrada de uma função não precisa ser uma simples variável; ele pode ser qual- quer expressão, como em sqrt(b * b – 4 * a * c). A Figura 2 mostra o fluxo de execu- ção quando uma função é chamada.
5.8 Parâmetros por referência 174
Sintaxe 5.4: Parâmetro por referência 174
Tópico avançado 5.2:
Referências constantes 176
Sintaxe 5.5: Parâmetro por referência constante 176
5.9 Escopo de variáveis e variáveis globais 177
Dica de qualidade 5.2: Minimize variáveis globais 178
5.10 Refinamentos sucessivos 178
Dica de qualidade 5.3: Mantenha as funções curtas 180
5.11 Do pseudocódigo ao código 180 5.12 Inspeções 186
Dica de produtividade 5.4:
Transformando uma seção de código em comentário 188
Dica de produtividade 5.5: Esqueletos vazios 190 5.13 Pré-condições 191
Sintaxe 5.6: Asserção 191
Fato histórico 5.1: O crescimento explosivo dos computadores pessoais 193
Figura 1
A função sqrtcomo uma caixa preta. x
x
sqrt
Parâmetro
Algumas funções possuem mais de uma entrada. Por exemplo, a função powpossui dois parâ- metros: pow(x, y)calcula xy. Funções podem ter várias entradas, mas apenas uma saída.
Cada função aceita entradas de tipos particulares. Por exemplo, sqrtrecebe somente núme- ros como parâmetros, enquanto que getlineespera um stream e um string. É um erro chamar sqrtcom uma entrada string.
Cada função retorna um valor de um tipo particular: sqrtretorna um número em ponto flu- tuante, substrretorna um string e mainretorna um inteiro.
5.2
Escrevendo funções
Vamos calcular o valor de uma conta de poupança com um saldo inicial de $1.000 após 10 anos. Se a taxa de juros é p%, então o saldo após 10 anos é
b = 1000 × (1 + p/100)10
Por exemplo, se a taxa de juros é 5% ao ano, então o investimento inicial de $1.000 terá cresci- do para $1.628,94 após 10 anos.
Vamos colocar esta computação dentro de uma função chamada future_value. Aqui está um exemplo de como usar a função:
Figura 2
Fluxo de execução durante uma chamada de função. main
Calcula parâmetro
Passa o parâmetro para b * b – 4 * a * c sqrt Calcula Passa resultado para o invocador sqrt Aguarda Usa resultado
int main() {
cout << "Por favor forneça a taxa percentual de juros: "; double rate;
cin >> rate;
double balance = future_value(rate);
cout << "Após 10 anos, o saldo é " << balance << "\n";
return 0; }
Agora escreva a função. A função recebe uma entrada em ponto flutuante e retorna um valor em ponto flutuante. Você deve dar um nome para o valor de entrada de modo que possa usá-lo em seus cálculos. Aqui ele é denominado p.
double future_value(double p) {
... }
Isso declara uma função future_valueque retorna um valor do tipo double e que recebe um parâmetro do tipo double. Durante a vida da função, o parâmetro é armazenado em uma variá-
vel parâmetro p. Assim como a main, o corpo da função é delimitado por chaves; veja a Sintaxe 5.1.
A seguir você precisa calcular um resultado da função: double future_value(double p)
{
double b = 1000 * pow(1 + p / 100, 10); ...
}
Finalmente, você precisa retornar o resultado ao invocador da função: double future_value(double p)
{
double b = 1000 * pow(1 + p / 100, 10); return b;
}
Sintaxe 5.1: Definição de função
return_type function_name(parameter1, parameter2,..., parametern)
{ statements } Exemplo: double abs(double x) { if (x >= 0) return x; else return -x; } Finalidade:
Isto completa a definição da função future_value. A Figura 3 mostra o fluxo de dados de entrada e saída da função.
Agora o programa está composto por duas funções: future_value e main. Ambas as de- finições de função devem ser colocadas em um arquivo de programa. Visto que mainchama fu- ture_value, a função future_valuedeve ser conhecida antes da função main. O modo mais fácil de obter isto é colocar no arquivo fonte primeiro a future_valuee por último a main(veja uma alternativa no Tópico Avançado 5.1).
Arquivo futval.cpp 1 #include <iostream> 2 #include <cmath> 3 4 using namespace std; 5 6 double future_value(double p) 7 { 8 double b = 1000 * pow(1 + p / 100, 10); 9 return b; 10 } 11 int main() 12 {
13 cout << "Por favor forneça a taxa percentual de juros: ";
14 double rate;
15 cin >> rate;
16
17 double balance = future_value(rate);
18 cout << "Após 10 anos, o saldo é "
19 << balance << "\n";
20
21 return 0;
22 }
A função future_valuetem um defeito importante: a quantidade inicial do investimento ($1,000) e o número de anos (10) são fixados (hard-wired) no código da função. Não é possível usar a função para calcular o saldo após 20 anos. Naturalmente, você pode escrever outra função future_value20, mas isso seria uma solução muito inadequada. Em vez disso, torne o saldo inicial e o número de anos parâmetros adicionais:
Figura 3
Uma função recebendo um parâmetro por valor e retornando um resultado. rate = balance = future_value main p = b = Parâmetro é inicializado quando a função é chamada Resultado é retornado quando a função é encerrada
double future_value(double initial_balance, double p, int n) {
double b = initial_balance * pow(1 + p / 100, n); return b;
}
Agora precisamos fornecer estes valores na chamada da função: double b = future_value(1000, rate, 10);
Agora nossa função é muito mais valiosa, porque é reutilizável. Por exemplo, podemos facil- mente modificar a mainpara imprimir o saldo após 10 e 20 anos.
double b = future_value(1000, rate, 10);
cout << "Após 10 anos, o saldo é " << b << "\n";
b = future_value(1000, rate, 20);
cout << " Após 20 anos, o saldo é " << b << "\n";
Mas, antes de mais nada, por que estamos usando uma função? Poderíamos ter feito os cálcu- los diretamente, sem uma chamada de função.
double b = 1000 * pow(1 + p / 100, 10);
cout << "Após 10 anos, o saldo é " << b << "\n";
b = 1000 * pow(1 + p / 100, 20);
cout << "Após 20 anos, o saldo é " << b << "\n";
Se você olhar e comparar estas duas soluções, deve ser bem aparente por quê funções são valio- sas. Uma função permite a você abstrair uma idéia — mais exatamente, o cálculo do juro compos- to. Uma vez compreendida a idéia, fica claro o que a alteração de 10 para 20 anos significa nas duas chamadas da função. Agora compare as duas expressões que calculam o saldo diretamente. Para en- tendê-las, você deve olhar detidamente as expressões para ver que elas somente diferem no último número, e então você deve relembrar o significado deste número.
Quando você se flagrar codificando a mesma computação mais de uma vez ou codificando uma computação que pode ser útil em outros programas, você deve transformá-la em uma função.