Mandar um cafézinho para o programador:


Me ajude a transformar café em código!
Mostrando postagens com marcador return. Mostrar todas as postagens
Mostrando postagens com marcador return. Mostrar todas as postagens

Structs e Funções em C++

 Agora que já aprendemos como declarar e usar structs no tutorial passado, vamos ver neste como usar as estruturas de dados com as funções.

Structs em Parâmetros e Argumentos de uma Função

Quando explicamos que as structs e suas instâncias funcionam como variáveis (mas variáveis especiais, que você mesmo criou), isso vale também para funções.

Por exemplo, uma função que vai receber e retornar um inteiro, tem o seguinte cabeçalho:

  • int myFunction(int num);

Ou seja, especificamos o tipo de dado que a função vai receber. Até o momento trabalhamos com int, double, float, char, ponteiros...agora vamos usar o tipo de dado...que nós mesmos vamos criar!

Por exemplo, uma função que vai receber a struct Student, do tutorial anterior, e exibir seus elementos, tem o seguinte cabeçalho:

  • void showStudent(Student alumn);

Por exemplo, o programa abaixo exibe os dados de uma struct de nome 'alumn', do tipo 'Student':

#include <iostream>
using namespace std;

struct Student
{
   char name[50];
   float math,
         physics,
         science;
};

void showStudent(Student alumn)
{
    cout<<"\nDados:"<<endl;
    cout<<"Nome      : "<<alumn.name<<endl;
    cout<<"Matemática: "<<alumn.math<<endl;
    cout<<"Física    : "<<alumn.physics<<endl;
    cout<<"Ciência   : "<<alumn.science<<endl;
}

int main()
{
    Student Alex;

    cout<<"Nome: ";
    cin.get(Alex.name,50);

    cout<<"Nota em Matemática: ";
    cin >> Alex.math;

    cout<<"Nota em Física: ";
    cin >> Alex.physics;

    cout<<"Nota em Ciências: ";
    cin >> Alex.science;

    showStudent(Alex);

    return 0;
}

Só para dizer que não é exatamente igual como fizemos com outras variáveis, note que nossa função usar a struct Student...ou seja, ela já deve EXISTIR antes de declararmos a função, por isso colocamos a declaração da struct ANTES da função que vai usar ela, entendido?

Retornando uma Struct de uma Função

Assim como as funções servem para receber informações e performar tarefas específicas, elas também servem para retornar informações. Até o momento, fizemos retorno com int, double, char, void...mas as funções podem retornar qualquer tipo de dado, desde que a sua struct tenha sido declarada antes.

A função getStudent() não recebe nenhum parâmetro/argumento, mas retorna uma variável do tipo Student, e é usada para retornar uma struct preenchida, veja como funciona:

#include <iostream>
using namespace std;

struct Student
{
   char name[50];
   float math,
         physics,
         science;
};

void showStudent(Student alumn)
{
    cout<<"\nDados:"<<endl;
    cout<<"Nome      : "<<alumn.name<<endl;
    cout<<"Matemática: "<<alumn.math<<endl;
    cout<<"Física    : "<<alumn.physics<<endl;
    cout<<"Ciência   : "<<alumn.science<<endl;
}

Student getStudent()
{
    Student alumn;

    cout<<"Nome: ";
    cin.get(alumn.name,50);

    cout<<"Nota em Matemática: ";
    cin >> alumn.math;

    cout<<"Nota em Física: ";
    cin >> alumn.physics;

    cout<<"Nota em Ciências: ";
    cin >> alumn.science;

    return alumn;
}

int main()
{
    Student Geddy = getStudent();
    showStudent(Geddy);

    return 0;
}

Veja como nossa main() ficou enxuta, apenas declaramos um aluno, fazemos ele receber os dados da função getStudent(), e catapimbas, exibimos o seu conteúdo, tá feito.

Passagem por Valor e por Referência de Structs

Vamos criar uma função, chamada fillStudent() que tem como argumento uma instancia da struct Student, nessa função, vamos solicitar ao usuário os dados do aluno.

Nosso código fica:

#include <iostream>
using namespace std;

struct Student
{
   char name[50];
   float math,
         physics,
         science;
};

void showStudent(Student alumn)
{
    cout<<"\nDados:"<<endl;
    cout<<"Nome      : "<<alumn.name<<endl;
    cout<<"Matemática: "<<alumn.math<<endl;
    cout<<"Física    : "<<alumn.physics<<endl;
    cout<<"Ciência   : "<<alumn.science<<endl;
}

void fillStudent(Student alumn)
{
    cout<<"Nome: ";
    cin.get(alumn.name,50);

    cout<<"Nota em Matemática: ";
    cin >> alumn.math;

    cout<<"Nota em Física: ";
    cin >> alumn.physics;

    cout<<"Nota em Ciências: ";
    cin >> alumn.science;
}

int main()
{
    Student Geddy;
    fillStudent(Geddy);
    showStudent(Geddy);

    return 0;
}

Na main(), criamos o estudante 'Geddy' e enviamos para a fillStudent() para seus dados serem preenchidos. Porém, quando invocamos a função showStudent(), ela exibe o seguinte resultado:

Dados:
Nome      : Ћ���
Matemática: 3.0844e-41
Física    : 6.11017e-29
Ciência   : 3.0844e-41

Ou seja, os membros da struct 'Geddy' não foram preenchidos! O motivo disso é que a passagem de structs foi feita por valor!

Quando fazemos fillStudent(Geddy), a função pega essa instância, cria uma cópia internamente na função, e altera os valores nessa cópia, e não na struct original.

E como fazer para essa função alterar, de fato, os membros da struct? Simples, basta fazer uma passagem por referência, da struct:

#include <iostream>
using namespace std;

struct Student
{
   char name[50];
   float math,
         physics,
         science;
};

void showStudent(Student alumn)
{
    cout<<"\nDados:"<<endl;
    cout<<"Nome      : "<<alumn.name<<endl;
    cout<<"Matemática: "<<alumn.math<<endl;
    cout<<"Física    : "<<alumn.physics<<endl;
    cout<<"Ciência   : "<<alumn.science<<endl;
}

void fillStudent(Student &alumn)
{
    cout<<"Nome: ";
    cin.get(alumn.name,50);

    cout<<"Nota em Matemática: ";
    cin >> alumn.math;

    cout<<"Nota em Física: ";
    cin >> alumn.physics;

    cout<<"Nota em Ciências: ";
    cin >> alumn.science;
}

int main()
{
    Student Geddy;
    fillStudent(Geddy);
    showStudent(Geddy);

    return 0;
}

Apenas adicionamos & ao parâmetro 'alumn'. Agora, toda struct que mandarmos para a função fillStudent() será alterada.

Como Receber Informações de uma Função - O comando RETURN em C++

Agora que você já aprendeu o que são e para que servem as funções, vamos ver aprender como nos comunicar com as funções, recebendo dados e informações delas através do comando return.

Trocando dados com funções em C++

No tutorial anterior, de nossa seção de Funções em C++, vimos como criar e invocar uma função.
Bastou chamar pelo nome: hello(), que ela veio rapidinho e executou de imediato.

Poderíamos chamar 1 milhão de vezes, que ela executaria 1 milhão de vezes, sem precisar ficar repetindo código, basta invocar a função sempre que precisarmos fazer aquilo que ela se propõe a fazer.

Mas ali tem um problema...quando invocamos uma função, a execução sai do local onde houve a chamada e vai pra outro local da memória, onde estão as instruções da função, executa tudo, e volta. Nesse caso anterior, não há uma comunicação entre quem chamou e a função.

Vamos ver um código C++ onde invocamos uma função que pede o número e exibe seu dobro:
#include <iostream>
using namespace std;

void doub()
{
    float num, dobro;

    cout<<"Digite um numero: ";
    cin >> num;

    dobro = 2*num;

    cout<<"Dobro: "<<dobro<<endl;

}

int main()
{
    doub();
    return 0;
}
Legal, né? Porém, a gente não tem acesso a essa informação 'dobro', não podemos pegar ela pra usar em outro cálculo, por exemplo, ele é simplesmente exibido dentro da função e ao término dela, esse valor se perde. As variáveis internas das funções são criadas localmente, e depois excluídas.

Se tentar usar 'dobro' fora da função vai ver que dá um erro de compilação.
Precisamos achar outra maneira de fazer a função se comunicar com o exterior, de enviar informações pra fora, concorda que isso é importante?

O comando RETURN em Funções

Sempre que quisermos enviar uma informação de dentro de uma função para o local onde ela foi chamada, usamos o comando return.

Primeiro, no cabeçalho de declaração da função, precisamos informar o tipo de dado que a função vai retornar. No exemplo anterior é void porque ela não retorna nada.

Se o tipo de dado que ela vai retornar é um float, por exemplo, seu cabeçalho deve ser:
float doub() {...}

E dentro do escopo da função devemos fazer: return info;
Onde info deve ser um float.

Pronto, a informação 'info' será retornada, devolvida para o local onde a função foi invocada.
Vamos criar uma função que pede um número, dobra ele e retorna seu dobro:
#include <iostream>
using namespace std;

float doub()
{
    float num, dobro;

    cout<<"Digite um numero: ";
    cin >> num;

    dobro = 2*num;

    return dobro;
}

int main()
{
    float res;
    res = doub();

    cout<<"Dobro: "<<res<<endl;
    return 0;
}
Agora, quando chamamos a função: doub()
Um valor é retornado para quem chamou ela, volta um float, no caso.

Então armazenamos o resultado desse retorno na variável res (obviamente, deve ser float):
res = doub();

E prontinho, essa variável vai ter dobro do valor que o usuário digitar lá dentro da função.
Podemos até deixar o código mais enxuto, veja:
#include <iostream>
using namespace std;

float doub()
{
    float num;

    cout<<"Digite um numero: ";
    cin >> num;

    return (2*num);
}

int main()
{
    cout<<doub()<<endl;
    return 0;
}

Veja que podemos dar return numa expressão, diretamente: return 2*num;

Exemplo de uso de RETURN em Funções

Vamos agora criar uma função de par ou ímpar.
Se for par, ela deve retornar 0, se for ímpar deve retornar 1.

Veja como fica nosso código C++:
#include <iostream>
using namespace std;

int par_impar()
{
    int num;

    cout<<"Digite um numero: ";
    cin >> num;

    if(num%2==0)
        return 0;
    else
        return 1;
}

int main()
{
    if(par_impar()==0)
        cout<<"É par!\n";
    else
        cout<<"É ímpar!\n";
    return 0;
}
Note agora que tem dois comandos return dentro da nossa função par_impar(), isso pode, mas veja que somente um ou outro pode acontecer, nunca um IF e um ELSE é executado, é sempre um deles.

Vamos dar uma enxugada nesse código?
#include <iostream>
using namespace std;

int par_impar()
{
    int num;

    cout<<"Digite um numero: ";
    cin >> num;

    return (num%2);
}

int main()
{
    if(!par_impar())
        cout<<"É par!\n";
    else
        cout<<"É ímpar!\n";
    return 0;
}
Conseguiu entender de boa?

Boas práticas de funções

Primeiro, nomes de funções: não existe um jeito certo nem errado de usar, mas recomendamos ser consistentes com o nome delas.

De preferência crie funções com o nome no seguinte estilo: c_progressivo(), projeto_progressivo(), van_der_graaf_generator() ... - tudo minúsculo, separado por _
Ou: cProgressivo(), projetoProgressivo(), vanDerGraafGenerator()...- primeira letra minúscula, e maiúscula para início das próximas palavras, ok?

Segundo, use muitas funções, e faças elas bem comunicativas, sempre que possível retornando e recebendo (vamos ver no próximo tutorial) dados, de maneira bem lógica e clara.

Assim como cada peça do seu carro ou cada órgão do seu corpo faz sua tarefa específica, eles também se comunicam e trocam informações entre si, isso é essencial para o bom funcionamento do sistema.

As funções de um sistema devem fazer o mesmo! Isso é uma boa prática de programação, não se esqueça! Faça suas funções serem facilmente 'acopladas' com outras funções, isso vai ser essencial para você ser capaz de criar sistemas grandes e robustos, como softwares empresariais, sistemas operacionais, jogos, etc.

Ajude o C++ Progressivo

Que tal apoiar e fazer crescer o ensino da programação no Brasil ?

Ajudar nosso país a crescer e se desenvolver cada vez mais, tecnologicamente?

Clica abaixo pra saber mais!

Apoiar o Projeto Progressivo