Baixe o app para aproveitar ainda mais
Prévia do material em texto
1 C++ Sub-Rotinas Osmar de Oliveira Braz Junior 2 Objetivos ◼ Assuntos a serem abordados nesta apresentação Sub-Rotinas Tipos de Sub-Rotinas Passagem de Parâmetros por valor e referência Procedimentos Funções Sobrecarga Inline Bibliotecas de sub-rotinas Parâmetros do main Vetores de sub-rotinas Recursividade 3 Sub-Rotinas ◼ Um matemático uma vez disse que um grande problema se resolve dividindo-o em pequenas partes e resolvendo tais partes em separado. ◼ Estes dizeres servem também para a construção de programas. ◼ Os profissionais de informática quando necessitam construir um grande sistema, o fazem, dividindo tal programa em partes, sendo então desenvolvido cada parte em separado, mais tarde, tais partes serão acopladas para formar o sistema. ◼ Estas partes são conhecidas por vários nomes Usaremos Sub-Rotinas Subprograma Operações Métodos 4 Sub-Rotinas ◼ O programa apresentado como objetivo gerar o valor do salário de um funcionário e calcular o novo salário. ◼ Para resolver este problema, utilizou-se o programa principal (main) e uma sub- rotina(representada pelo bloco de instruções entre double calculo(){ }). 5 Sub-Rotinas #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; double calculo(double sal){ double valor; valor = sal * 10.0 / 100.0; return valor; } int main(int argc, char *argv[]){ double salario, aumento, novosalario; cout << "Digite o Salario: "; cin >> salario; aumento = calculo(salario); novosalario = salario + aumento; cout << "O novo Salário é: " << novosalario; return EXIT_SUCCESS; } 6 Sub-Rotinas ◼ Objetivos Dividir e estrutura um programa em partes lógicas e coerentes; Facilidade em testar os trechos em separado; O programador poderá criar sua biblioteca de sub- rotinas, tornando sua programação mais eficiente uma vez que poderá fazer uso de sub-rotinas por ele escritas em vários outros programas com a vantagem de já terem sido testadas; Maior legibilidade de um programa; Evitar que certa sequência de comandos necessária em vários locais de um programa tenha de ser escrita repetidamente nestes locais, diminuindo também o código fonte. 7 Sub-Rotinas ◼ Chamada de uma Sub-rotina Quando uma sub-rotina é chamada, o fluxo de execução é desviado para a sub- rotina, no momento em que ela é chamada no programa principal. Ao terminar a execução dos comandos da sub-rotina, o fluxo de execução retorna ao comando seguinte àquele onde ela foi ativada, exatamente como na figura abaixo. #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int main(int argc, char *argv[]){ double salario, aumento, novosalario; cout << "Digite o Salario: "; cin >> salario; aumento = calculo(salario); novosalario = salario + aumento; cout << "O novo Salário é: " << novosalario; return EXIT_SUCCESS; } double calculo(double sal){ double valor; valor = sal * 10.0 / 100.0; return valor; } 8 Sub-Rotinas ◼ A definição de uma sub-rotina consta de: Um cabeçalho, onde estão definidos o nome e o tipo da sub- rotina, sua assinatura ou protótipo, bem como os seus parâmetros e variáveis locais; Um corpo, onde se encontram as instruções da sub-rotina. Cabeçalho Corpo 9 Sub-Rotinas ◼ O nome de uma sub-rotina é o nome simbólico/assinatura pelo qual ele é chamado por outro programa/sub-rotina. ◼ O corpo da sub-rotina contém as instruções que são executadas cada vez que ele é chamado. ◼ Variáveis locais são aquelas definidas dentro da própria sub-rotina e só podem ser utilizadas pela mesma. ◼ Parâmetros são canais por onde os dados são transferidos pelo programa chamador a uma sub-rotina, e vice-versa. Para que possa iniciar a execução das instruções em seu corpo, uma sub-rotina às vezes precisa receber dados do programa que o chamou e, ao terminar sua tarefa, a sub-rotina deve fornecer ao programa chamador os resultados da mesma. Esta comunicação bidirecional pode ser feita de dois modos que serão estudados à frente: por meio da passagem de variáveis globais ou por meio da passagem de parâmetros. 10 Sub-Rotinas - Tipos ◼ O tipo de uma sub-rotina é definido em função do número de valores que a sub-rotina retorna ao programa que o chamou. Segundo esta classificação, os programas podem ser de dois tipos: Procedimentos, que retornam zero ou mais valores ao programa chamador; Funções, que retornam um, e somente um, valor ao programa chamador. ◼ Na realidade, a tarefa desempenhada por uma sub-rotina do tipo função pode perfeitamente ser realizada por outra do tipo procedimento (o primeiro é um caso particular deste). Esta diferenciação é feita por razões históricas, ou, então, pelo grande número de sub-rotinas que se encaixam na categoria de funções. 11 void <Nome> ([parâmetros]) { [<definições>] <comandos> } Sub-Rotinas – Procedimento ◼ É um tipo em especial de sub-rotina. Este tipo de sub-rotina não possui retorno. Consequentemente não possui no corpo de instruções o comando return. Também não possui tipo especificado para a sub- rotina ela é do tipo void. ◼ Quando o seu nome é colocado em alguma parte do programa esta sub-rotina e ativada. Desta forma, assim que o nome de um procedimento é encontrado durante a execução ocorre um desvio do programa, para que os comandos da sub-rotina sejam executados. Ao término da sub-rotina, a execução retornará ao ponto subsequente à chamada do procedimento. 12 Sub-Rotinas – Procedimento – Exemplo #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int n; int numero; void mostraNumeros(){ for(numero = 1; numero <= n; numero++){ cout << "Numero: " << numero << "\n"; } } int main(int argc, char *argv[]){ cout << "Digite a quantidade de numeros: "; cin >> n; mostraNumeros(); cout << "Fim"; return EXIT_SUCCESS; } Sub-rotina do tipo void sem parâmetros ou procedimento 13 Sub-Rotinas – Variáveis Globais ◼ Damos o Nome de variáveis globais para aquelas variáveis que são definidas logo após o comando os comandos includes e antes de qualquer bloco de programa, sendo desta forma visíveis (isto é, que podem ser usadas) em qualquer parte do programa principal e por todas as sub-rotinas. 14 Sub-Rotinas – Variáveis com mesmo nome #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; char nome[10]; //Variável Global void modificaNome(){ cout << "Digite o nome: "; fflush(stdin); gets(nome); } void leNome(){ char nome[10]; //Variável Local cout << "Digite o nome: "; fflush(stdin); gets(nome); } int main(int argc, char *argv[]){ modificaNome(); cout << "O nome e : " << nome << "\n"; leNome(); cout << "O nome e : " << nome << "\n"; return EXIT_SUCCESS; } Variável declarada global. Visível a todos os blocos de programa deste ponto em diante. 15 Sub-Rotinas – Variáveis Globais ◼ É possível acessar variáveis que não estejam em um mesmo escopo utilizando o operador de escopo (::) 16 Sub-Rotinas – Operador de Escopo #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCESS using namespace std; char nome[10] = "Global"; void mostraNome(){ char nome[10] = "Local"; cout << "Local : " << nome << endl; cout << "Global: " << ::nome << endl; } int main(){ mostraNome(); return EXIT_SUCCESS; } O operador de escopo permite acessar a variável global ao invés da local. O default é sempre local. Sem operador 17 Sub-Rotinas – Passagem de Parâmetros ◼ Parâmetros são canais pelos quais se estabelece uma comunicação bidirecional entre uma sub-rotina e o programa chamador (programa principal ou outra sub-rotina). ◼ Dados são passados pelo programa chamador a sub-rotina, ou retornados por este ao primeiro por meio de parâmetros. ◼ Até agora vimos que para ativar uma Sub-rotina bastaria colocaro seu Nome em alguma parte do programa. Mas isto nem sempre significa que o trabalho de escrever o programa irá diminuir. ◼ Com o que vimos até agora, dependendo da tarefa a ser realizada pela Sub-rotina, o trabalho de um programador pode até ser bem complicado. ◼ Por Exemplo: Como faríamos para ler 5 vetores, todos com tamanhos diferentes? Poderíamos, criar 5 Sub- rotinas, uma para cada vetor a ser lido. Isto sem dúvida resolveria esta situação, mas, e se fossem 100 vetores? ou 1000? Seria realmente uma tarefa muito trabalhosa ter de escrever 100, ou 1000 Sub-rotinas, isto só para ler os vetores, imagine se tivéssemos também que ordená-los, ou realizar outro processo qualquer. ◼ Com toda esta dificuldade, o uso das Sub-rotinas deveria ser considerado. Como já foi dito, as Sub-rotinas foram criadas para serem genéricas o bastante para se adaptarem a qualquer situação, visando justamente a possibilidade de reutilização do código. ◼ Para realizar esta “mágica”, foi criado o conceito de passagem de parâmetros, ou seja, passar informações para serem tratadas dentro da Sub-Rotina. ◼ Sintaxe: ◼ Obs: Variável do mesmo tipo são separadas por vírgulas (,). Variáveis de tipos diferentes, são separadas por ponto e vírgula (;). 18 void <Nome> (<tipo_de_dado> <nome_parametro>){ [<definições>] <comandos> } Sub-Rotinas – Passagem de Parâmetros 19 Sub-Rotinas – Passagem de Parâmetros #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; void escreveNome(int n, char nome[10]){ for(int i=0;i<n;i++){ cout << nome << "\n"; } } int main(int argc, char *argv[]){ char funcionario[10]; int numero; cout << "Digite o Numero: "; cin >> numero; cout << "Digite Funcionario: "; fflush(stdin); gets(funcionario); escreveNome(numero,funcionario); escreveNome(10,"Joao"); return EXIT_SUCCESS; } 20 Sub-Rotinas – Passagem de Parâmetros ◼ Os nomes dados aos parâmetros não necessitam serem iguais as variáveis passadas para sub-rotina. No Exemplo acima, o valor contida em “Número” será passado para o parâmetro “N”, da mesma forma que o valor contido na variável “Funcionário” será passada para o parâmetro “Nome”. Note que os nomes são diferentes. ◼ Parâmetros formais são os nomes simbólicos introduzidos no cabeçalho de sub-rotinas, usados na definição dos parâmetros da mesma. Dentro da sub-rotina trabalha-se com estes nomes da mesma forma como se trabalha com variáveis locais ou globais. No exemplo acima são as variáveis N e Nome no cabeçalho da sub-rotina escreveNome. ◼ Parâmetros reais são aqueles que substituem os parâmetros formais quando da chamada de uma sub-rotina. No exemplo acima o programa principal chama a sub-rotina escreveNome com os parâmetros Numero e Funcionário substituindo os parâmetros formais N e Nome respectivamente. Na outra chamada da sub-rotina são passados os parâmetros reais 10 e “João” para N e Nome. 21 Parâmetro e argumento ◼ Parâmetro é a variável que irá receber um valor em um método enquanto que um argumento é o valor (que pode originar de uma variável ou expressão) que você passa para o método. ◼ Você não passa parâmetros, você passa argumentos na chamada da sub-rotina. ◼ Você recebe argumentos também, mas recebe em parâmetros. 22 Sub-Rotinas – Parâmetros por Valor ◼ É quando a alteração dos conteúdos dos parâmetros passados para a sub-rotina não será refletida no programa principal. 23 Sub-Rotinas – Parâmetros por Valor #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; void porValor(int a){ cout << "A1:" << a << endl; a = 5; cout << "A2:" << a << endl; } int main(int argc, char *argv[]){ int x; x = 10; cout << "X1:" << x << endl; porValor(x); cout << "X2:" << x << endl; return EXIT_SUCCESS; } O parâmetro a possui uma cópia do conteúdo da variável x. a = x; 24 Sub-Rotinas – Parâmetros por Valor ◼ O conteúdo da variável “X” não será alterado após o retorno ao programa principal. O conteúdo da variável X foi copiado (atribuído) para a variável A da sub-rotina porValor. ◼ Portanto X e A são variáveis diferentes de espaços de memórias diferentes. int main(int argc,char *argv[]){ int x = 10; cout << "X1:" << x << endl; porValor(x); cout << "X2:" << x << endl; return EXIT_SUCCESS; } x 10 void porValor(int a){ cout <<"A1:"<<a<< endl; a = 5; cout <<"A2:"<<a << endl; } a 10 a = x; a 5 Saída: X1:10 A1:10 A2:5 X2:10 25 Sub-Rotinas – Parâmetros por Referência ◼ Quando a alteração do conteúdo de um parâmetro, dentro de uma sub-rotina, reflete no programa principal, os parâmetros são ditos passados por referência. ◼ Para que um parâmetro seja passado por referência deverá ter na definição da sub- rotina o especificador de referência “&” na frente do nome dos parâmetros. 26 Sub-Rotinas – Parâmetros por Referência #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; void porReferencia(int &a){ cout << "A1:" << a << endl; a = 5; cout << "A2:" << a << endl; } int main(int argc, char *argv[]){ int x = 10; cout << "X1:" << x << endl; porReferencia(x); cout << "X2:" << x << endl; return EXIT_SUCCESS; } O parâmetro a possui uma referência ao mesmo endereço da variável x. &a = x; 27 Sub-Rotinas – Parâmetros por Referência ◼ No Exemplo, o conteúdo da variável “X” será alterado após o retorno ao programa principal. ◼ A chamada da sub-rotina porReferencia cria um outro nome para o espaço de memória da variável X. Este novo nome é utilizado dentro da sub-rotina porReferência através do parâmetro A. int main(int argc,char *argv[]){ int x = 10; cout << "X1:" << x << endl; porReferencia(x); cout << "X2:" << x << endl; return EXIT_SUCCESS; } x 10 void porReferencia(int &a){ cout << "A1:" <<a<< endl; a = 5; cout << "A2:" << a << endl; } ax 5 &a = x Saída: X1:10 A1:10 A2:5 X2:5 28 Sub-Rotinas – Parâmetros por Referência com Ponteiros ◼ É possível realizar a passagem de parâmetros por referência com o uso de ponteiro. ◼ Para que um parâmetro seja passado ponteiro deverá ter na definição da sub- rotina o especificador de ponteiro “*” colocado na frente do nome dos parâmetros. 29 Sub-Rotinas – Parâmetros por Referência com Ponteiros #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; void porRefPonteiro(int *a){ cout << "A1:" << *a << endl; *a = 5; cout << "A2:" << *a << endl; } int main(int argc, char *argv[]){ int x = 10; cout << "X1:" << x << endl; porRefPonteiro(&x); cout << "X2:" << x << endl; return EXIT_SUCCESS; } O parâmetro a possui uma referência ao mesmo endereço da variável x. *a = &x; 30 Sub-Rotinas – Parâmetros por Referência Ponteiro ◼ No Exemplo, o conteúdo da variável “X” será alterado após o retorno ao programa principal. ◼ A chamada da sub-rotina porRefePonteiro cria um outro nome para o espaço de memória da variável X. Este novo nome é utilizado dentro da sub-rotina porRefPonteiro através do parâmetro a, ponteiro de int. int main(int argc,char *argv[]){ int x = 10; cout << "X1:" << x << endl; porRefPonteiro(&x); cout << "X2:" << x << endl; return EXIT_SUCCESS; } x 10 void porRefPonteiro(int *a){ cout << "A1:" <<*a<< endl; *a = 5; cout << "A2:" << *a << endl; } ax 5 *a = &x Saída: X1:10 A1:10 A2:5 X2:5 ◼ Possui as mesmas características de uma “Procedimento” no que se refere a passagem de parâmetros por valor e por referência, variáveis globais e locais, mas possui uma importante diferença, que é o retorno de um valor ao término de sua execução, ou seja, uma Função sempre deverá retornar um valor ao programa principal. E a função deve ter um tipo diferente de void. ◼ Deverá ser informado qual o tipo do valor retornado, sendo que poderá ser usado, nesta definição, tanto tipos pré-definidos da linguagem, como tiposdefinidos pelo usuário. ◼ Somente um valor pode ser retornado para o tipo especificado. ◼ Para informar qual o valor deve ser retornado deve ser colocado em algum ponto de finalização do código da “Função” uma linha com a seguinte Sintaxe: 31 return <valor_a_ser_retornado>; Sub-Rotinas – Função 32 Sub-Rotinas – Função #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int soma(int num1, int num2){ int total; total = num1 + num2; return total; } int main(int argc, char *argv[]){ int a,b,resultado; cout << "Digite A: "; cin >> a; cout << "Digite B: "; cin >> b; resultado = soma(a,b); cout << "O resultado e : " << resultado; return EXIT_SUCCESS; } 33 Sub-Rotinas – Procedimento por referência #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; void soma(int num1, int num2, int &total){ total = num1 + num2; } int main(int argc, char *argv[]){ int a,b,resultado; cout << "Digite A: "; cin >> a; cout << "Digite B: "; cin >> b; soma(a,b, resultado); cout << "O resultado e : " << resultado; return EXIT_SUCCESS; } Senão desejar usar o return pode utilizar um parâmetro por referência. 34 Sub-rotina que chama sub-rotina ◼ Para uma sub-rotina chamar outra sub-rotina ela precisa ser existir antes de sua chamada. A existência pode ser por declaração ou implementação. ◼ Se declaração da assinatura/protótipo da sub- rotina ocorrer antes da chamada, a implementação pode ocorrer em qualquer bloco do programa principal. 35 #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int soma2(int num1, int num2){ int total; total = num1 + num2; return total; } int soma3(int num1, int num2, int num3){ int total; total = soma2(soma2(num1,num2), num3); return total; } int main(int argc, char *argv[]){ int a, b, c, resultado; cout << "Digite A: "; cin >> a; cout << "Digite B: "; cin >> b; cout << "Digite C: "; cin >> c; resultado = soma3(a,b,c); cout << "O resultado e : " << resultado; return EXIT_SUCCESS; } Sub-Rotinas – Função em C++ Sub-rotina soma3 realizada a chamada da sub-rotina soma2 portanto precisa ser declarado e implementado antes. 36 #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int soma2(int num1, int num2); int soma3(int num1, int num2, int num3); int main(int argc, char *argv[]){ int a, b, c, resultado; cout << "Digite A: "; cin >> a; cout << "Digite B: "; cin >> b; cout << "Digite C: "; cin >> c; resultado = soma3(a,b,c); cout << "O resultado e : " << resultado; return EXIT_SUCCESS; } int soma2(int num1, int num2){ int total; total = num1 + num2; return total; } int soma3(int num1, int num2, int num3){ int total; total = soma2(soma2(num1,num2), num3); return total; } Sub-Rotinas – Função em C++ A implementação das sub-rotinas soma3 e soma2 podem ser em qualquer parte do programa principal pois já foram declaradas no início do arquivo. Declara a assinatura/protótipos das sub-rotinas soma3 e soma2. 37 Sobrecarga de métodos ◼ Uma programa também pode ter vários métodos com o mesmo nome (sobrecarga) distinção é feita pelo tipo e número de parâmetros o tipo de dado de retorno não pode ser usado para distinguir métodos sobrecarregados ◼ Podemos usar o mesmo nome para o mesmo método. 38 Exemplo Sobrecarga de Métodos com parâmetros de diferentes quantidades #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int soma(int num1, int num2){ //Dois parâmetros int int total; total = num1 + num2; return total; } int soma(int num1, int num2, int num3) { //Três parâmetros int int total; total = num1 + num2 + num3; return total; } int main(int argc, char *argv[]){ int a = 10, b = 20, c=30; cout << "1 - Soma int+int : " << soma(a,b) << endl; cout << "2 - Soma int+int+int : " << soma(a,b,c) << endl; return EXIT_SUCCESS; } A quantidade de parâmetros identifica o método a ser chamado 39 Exemplo Sobrecarga de Métodos #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int soma(int num1, int num2){ //Dois parâmetros inteiros int total; total = num1 + num2; return total; } int soma(char* num1, char* num2){ //Dois parâmetros char return soma(stoi(num1),stoi(num2)); } int soma(char* num1, int num2){ //Parâmetro int e char return soma(stoi(num1),num2); } int soma(int num1, char* num2){ //Parâmetro char e int return soma(num1,stoi(num2)); } int main(int argc, char *argv[]){ int a1 = 10, b1 = 20; char a2[] = "10", b2[] = "20"; cout << "1 - Soma int+int : " << soma(a1,b1) << endl; cout << "2 - Soma char+char : " << soma(a2,b2) << endl; cout << "3 - Soma char+int : " << soma(a2,b1) << endl; cout << "4 - Soma int+char : " << soma(a1,b2) << endl; return EXIT_SUCCESS; } O tipo de dado dos parâmetros identifica qual o método a ser chamado. 40 Valores default para os parâmetros ◼ É possível especificar valores default para os parâmetros de uma sub-rotina. Inicializar os valores dos parâmetros ◼ Desta forma é possível criar uma forma de chamar a sub-rotina, passando somente os argumentos que não foram inicializados. 41 Exemplo Valores default para os parâmetros #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int soma(int num1, int num2, int num3=0, int num4=0){ int total; total = num1 + num2 + num3 + num4; return total; } int main(int argc, char *argv[]){ int a = 10, b = 20, c = 30, d = 40; cout << "1 - Soma 2 int : " << soma(a,b) << endl; cout << "2 - Soma 3 int : " << soma(a,b,c) << endl; cout << "3 - Soma 4 int : " << soma(a,b,c,d) << endl; return EXIT_SUCCESS; } Com valores de inicialização para os parâmetros é possível omiti-los na chamada do método. 42 inline ◼ Tornar mais rápida a chamada de sub-rotinas. ◼ Elas são tratadas pelo compilador quase como uma macro: a chamada da sub-rotina é substituída pelo corpo da sub-rotina. Para sub-rotinas pequenas, evita geração de código para a chamada e o retorno da sub-rotina. ◼ Deve ser usado o especificador “inline” antes do tipo da sub-rotina. ◼ A chamada da sub-rotina é feita normalmente. 43 inline #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCESS using namespace std; inline int soma(int num1, int num2){ return num1 + num2; } inline int menos(int num1, int num2){ return num1 - num2; } int main(int argc, char *argv[]){ int a = 10, b = 20; cout << "soma : " << soma(a,b) << endl; cout << "menos: " << menos(b,a) << endl; return EXIT_SUCCESS; } 44 Bibliotecas ◼ Quando as sub-rotinas são bem construídas podem ser isoladas em arquivos vindo a formar bibliotecas. ◼ Para criar bibliotecas deve se criar um arquivo de cabeçalho, com a extensão .h e um arquivo para o corpo da sub-rotina em um arquivo .cpp. Cabeçalho Corpo .h .cpp 45 Bibliotecas #ifndef _BIBLIOTECA_H_ #define _BIBLIOTECA_H_ //Evita a referencia circular #include <iostream> #include <cstdlib> int soma(int num1, int num2); #endif #include "biblioteca.h" using namespace std; int soma(int num1, int num2){ int total; total = num1 + num2; return total; } biblioteca.h biblioteca.cpp Corpo da biblioteca que possui a implementação. Permite que as sub-rotinas possam chamar outras que estejam antes de sua implementação. Cabeçalho da biblioteca, que possui a assinatura/protótipo da sub-rotina. 46 Usando a biblioteca #include "biblioteca.h" using namespace std; int main(int argc, char *argv[]){ int a, b, resultado; cout << "Digite A: "; cin >> a; cout << "Digite B: "; cin >> b; resultado = soma(a,b); cout << "O resultado e : " << resultado; return EXIT_SUCCESS; } main.cpp Inclui a biblioteca parauso no programa principal. 47 Método main ◼ Toda sub-rotina possui entrada, processamento e saída. ◼ Da mesma forma o método main do programa principal em C++. ◼ O método main possui dois parâmetros(argc - quantidade de parâmetros e argv - vetor com os parâmetros #include <iostream> #include <cstdlib> using namespace std; int main(int argc, char *argv[]) { //ou char **argv return EXIT_SUCCESS; } 48 main #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int main(int argc, char *argv[]) { cout << "Qtde parametros" << argc << endl; if (argc != 1) { cout << "Valores dos parametros"< endl; for(int i=0;i<argc;i++) { cout << "argv[" <<i<<"] = " << argv[i] << endl; } } else { cout << "O programa nao tem parametros" << endl; } return EXIT_SUCCESS; } 49 Executando ◼ Abra uma janela de Prompt de MS-DOS ( )Windows + R -> Digite “cmd” ◼ Entre no diretório da aplicação Utilize os comandos do MS-DOS ◼ cd nomediretorio (Entra no diretório) ◼ cd .. (retorna um diretório) ◼ dir (mostra o conteúdo do diretório) ◼ Outros md (cria), rm(apaga) main ◼ No prompt de comando chame o executável do programa e passe parâmetros. 50 51 Vetores de métodos ◼ Uma assinatura de método(função ou procedimento) possui um tipo da mesma forma que uma variável declarada. ◼ O que difere uma variável de um método é o uso do () com ou sem parâmetros logo depois do nome. ◼ Da mesma forma que se cria vetores de tipos de dados é possível criar vetor de funções. 52 #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCESS using namespace std; void funcao0(){cout<<endl<<"*** Estou em funcao0()***"<<endl;} void funcao1(){cout<<endl<<"*** Estou em funcao1()***"<<endl;} void funcao2(){cout<<endl<<"*** Estou em funcao2()***"<<endl;} int main(int argc, char *argv[]){ //Declara um vetor de 3 posições de void void (*ponteiroFuncao[3])(void); //Atribui cada função a uma posição do vetor ponteiroFuncao[0] = funcao0; ponteiroFuncao[1] = funcao1; ponteiroFuncao[2] = funcao2; //Percorre o vetor chamando as funções for(int i=0;i<=2;i++){ (*ponteiroFuncao[i])(); } return EXIT_SUCCESS; } 53 Recursividade ◼ Na programação de computadores, a recursão é uma técnica que define um problema em termos de uma ou mais versões menores deste mesmo problema. ◼ Esta ferramenta pode ser utilizada sempre que for possível expressar a solução de um problema em função do próprio problema. ◼ Sendo assim, um número infinito de cálculos pode ser definido por um programa recursivo finito, ainda que este não contenha repetições explícitas (como enquanto, repita, para faça). 54 Recursividade ◼ A recursão não é um conceito novo, sendo diretamente derivado das relações de recorrência da Matemática. Um exemplo clássico é o somatório (A soma S dos N primeiros números inteiros):recursiva ◼ Supondo N = 5; 55 Recursividade ◼ Para se codificar programas de modo recursivo usa-se uma sub- rotina, que permite dar um nome a um comando, o qual pode chamar a si próprio. ◼ É comum a definição de variáveis locais a uma sub-rotina. Quando a mesma sub-rotina é ativado recursivamente, um novo conjunto de variáveis locais é criado. Ainda que com os mesmos nomes, mas em posições de memória diferentes. ◼ Em se tratando de sub-rotinas recursivas pode-se ocorrer um problema de terminação do programa, como um “looping interminável ou infinito”. Para determinar a terminação das repetições, deve-se: Definir uma função que implica em uma condição de terminação (solução trivial), e Provar que a função decresce a cada passo de repetição, permitindo que, eventualmente, esta solução trivial seja atingida. 56 Recursividade ◼ Retomando o exemplo do somatório, percebemos que existe uma função, a qual é chamada constantemente até que encontre a solução do problema. ◼ Então temos que: S(N) = S(N – 1) + N. ◼ Como a solução para S(N) pode ser encontrada a partir da solução de S(N – 1), que é uma simplificação do problema, podemos continuar a diminuir o valor de N até chegarmos a uma solução trivial, isto é, se N = 1 então S(N) = 1. ◼ Para um bom funcionamento de uma sub-rotina recursiva, ele deve conter pelo menos uma solução trivial. 57 Recursividade #include <iostream> // std::cout, std::endl #include <cstdlib> //EXIT_SUCCESS using namespace std; int soma(int _n){ if (_n == 1) { //cout << "Retornando 1" << endl; return 1; } else { //cout << "Somando " << _n << " e soma(" << _n << " - 1)" << endl; return (_n + soma(_n - 1)); } } int main(int argc, char *argv[]){ int n; cout << "Digite n: "; cin >> n; cout << "A soma e " << soma(n); return EXIT_SUCCESS; } 58 Recursividade Teste de Mesa para N=4 (somatório dos 4 primeiros números inteiros) 59 Recursividade ◼ Teste de Mesa para N=4 (somatório dos 4 primeiros números inteiros) soma(4) 4 + soma(4-1) 3 + soma(3-1) 2 + soma(2-1) soma(1) Retorna 1 Retorna 1 + 2 Retorna 3 + 3 Retorna 6 + 4 Retorna 10 60 Torre de Hanoi ◼ O objetivo é transferir três discos da torre A para a torre C, usando a torre B como auxiliar. Somente o primeiro disco de uma torre(pilha) pode ser deslocado, e um disco maior(número) nunca pode ser colocado sobre o outro menor. A B C 3 2 1 61 Torre de Hanoi A B C 3 2 1 0 A B C 3 2 1 1 A B C 3 2 1 2 A B C 3 2 1 3 A B C 32 1 4 A B C 321 5 A B C 3 2 1 6 A B C 3 2 1 7 62 Torre de Hanoi ◼ Considerando o trabalho necessário para movimentar apenas três discos, matematicamente é possível demonstrar que o tempo necessário para transferir n discos é da ordem de 2n -1 (ou maior) 3 discos precisam de 7 movimentos (23 -1=8-1=7) ◼ Raciocínio: Se n = 1, o disco da torre A é transferido para a torre B, usando a torre auxiliar C; Senão: a) n-1 discos são transferidos da torre A para a torre B, usando a torre auxiliar C; b) o último disco da torre A é transferido para a torre C; c) n-1 discos são transferidos da torre B para a torre C, usando a torre auxiliar A. 63 Torre de Hanoi - Subrotina void hanoi(int numeroDiscos, char origem, char meio, char destino) { //Se existe somente um disco move da origem para o destino if (numeroDiscos == 1) { cout << "Disco 1 de " << origem << " para " << destino << endl; } else { //Move o disco da origem para o meio hanoi(numeroDiscos - 1, origem, destino, meio); //Mostra a acao de mover o disco da origem para o destino cout << "Disco " << numeroDiscos << " de " << origem << " para " << destino << endl; //Move o disco do meio para o destino hanoi(numeroDiscos - 1, meio, origem, destino); } } 64 Torre de Hanoi - Principal #include <iostream> #include <fstream> #include <string> using namespace std; void hanoi(int numeroDiscos, char origem, char meio, char destino); int main(int argc, char** argv) { int numeroDiscos = 3; cout << "Digite a quantidade de disco da primeira torre: "; cin >> numeroDiscos; out << endl << "Sequencia de acoes para " << numeroDiscos << " discos:" << endl; hanoi(numeroDiscos, 'A', 'B', 'C'); return 0; } 65 Torre Hanoi – Execução 3 ◼ hanoi(3,"A","B","C") 66 Torre Hanoi – Execução 4 ◼ hanoi(4,"A","B","C") 67 Recursividade ◼ Quando não se deve empregar recursão Programas recursivos são particularmente apropriados quando o problema for definido em termos recursivos. Isto não significa, no entanto, que tais definições recursivas garantam ser um programa recursivo a melhor maneira de se resolver o problema. Como exemplo podemos citar o mesmo exemplo do somatório, para o qual não é eficiente usar a recursão e sim a iteração (comandos explícitos de repetição, como: para faça, enquanto faça...), sendo a melhor solução para o problema. De fato, a apresentação da técnica através de exemplos inadequados tem sido a principal causa da grande apreensão e antipatia criadas contra o uso da recursão nas atividadesde programação, bem como pela associação direta entre recursão e ineficiência. 68 Recursividade ◼ Vantagens X Desvantagens de um programa recursivo em relação a um programa iterativo Um programa recursivo é mais elegante e menor que a sua versão iterativa, além de exibir com maior clareza o processo utilizado, desde que o problema ou os dados sejam naturalmente definidos através de recorrência. Por outro lado, um programa recursivo exige mais espaço de memória e é, na grande maioria dos casos, mais lento do que a versão iterativa. 69 Conclusão ◼ Como todo programa tem entrada, processamento e saída, toda sub-rotina também. ◼ O reaproveitamento de sub-rotinas depende da correta utilização dos conceitos de parâmetros para isolar a sub-rotina. ◼ Devido ao tamanho dos programas dividir o programa/problema e partes é essencial para facilitar o entendimento e teste dos programas. ◼ É dividir para conquistar. 70 Bibliografia/Referências ◼ Stroustrup, Bjarne. The C++ Programming Language. 2019. Disponível em: “http://www.stroustrup.com/C++.html” 71 Fim
Compartilhar