• Nenhum resultado encontrado

Funções Objetivos do capítulo

• 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.