Mandar um cafézinho para o programador:


Me ajude a transformar café em código!
Mostrando postagens com marcador Passagem por valor. Mostrar todas as postagens
Mostrando postagens com marcador Passagem por valor. 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.

Parâmetros e Variáveis de Referência de Funções em C++

Neste tutorial de nosso curso de C++, vamos aprender a passagem de argumentos por referência, em funções.

Passagem por Valor

Já aprendemos como enviar informações para funções, através dos argumentos e parâmetros.
E vimos que esta passagem é chamada passagem por valor, pois só passamos o valor para a função.

Se enviamos uma variável pra uma função, e dentro dessa função essa variável é alterada, ela não é alterada fora da função. Teste o seguinte código, que eleva um número ao quadrado:
#include <iostream>
using namespace std;

void square(int num);

int main()
{
    int number = 6;

    cout<<"Antes  : num = "<<number<<endl;
    square(number);
    cout<<"Depois : num = "<<number<<endl;

    return 0;
}

void square(int num)
{
    num = num*num;
    cout<<"Durante: num = "<<num<<endl;
}
Quando invocamos a função: square(number), estamos na verdade, passando uma cópia do valor da variável number.

Dentro da função, a variável num vai receber uma cópia do valor de number. Ou seja, ela não vai ter acesso a variável original number, somente ao seu valor! Por isso, passagem por valor.

Parâmetro de Referência: &

Seja a declaração de variável:

  • int num = 6;


O que estamos fazendo aí é alocando, reservando, um espaço na memória do seu computador.
Assim, quando usamos 'num', o C++ entende que devemos ir no endereço para onde essa variável aponta e pegar o número que está dentro daquele local da memória.

Como vimos, quando passamos essa variável normal para uma função que espera uma variável normal, ela pega apenas uma cópia de seu valor, e não mexe no conteúdo original.

Porém, existe outro tipo especial de variável, a variável de referência.
Ela é especial, pois se passarmos uma variável para uma função e a função quiser obter a variável de referência, através de um parâmetro de referência, ela vai ter acesso ao endereço da memória (a referência), onde esta variável está originalmente armazenada.

Ou seja: vamos ter acesso, de verdade, a variável, e não somente a sua cópia.

Para pegarmos a referência de uma variável, basta usarmos o operador & antes do nome da variável, no parâmetro da função! É o parâmetro de referência. Tanto no protótipo quanto na declaração da função.

Assim, o exemplo de código anterior, fica:
#include <iostream>
using namespace std;

void square(int &);

int main()
{
    int number = 6;

    cout<<"Antes  : num = "<<number<<endl;
    square(number);
    cout<<"Depois : num = "<<number<<endl;

    return 0;
}

void square(int &num)
{
    num = num*num;
    cout<<"Durante: num = "<<num<<endl;
}
Veja agora o resultado:
Variável de referência em C++ : &

De fato, a função conseguiu mudar o valor da variável, pois essa passagem de argumento para o parâmetro foi via parâmetro de referência.

O que acontece aqui é que o parâmetro não vai capturar o valor do argumento, mas sim sua referência, para onde ele está apontando na memória. Falando em apontar para um endereço, estudaremos mais sobre esse assunto nas seções de ponteiros e arrays (vetores) em C++, onde estudaremos sobre passagem por referência, usando ponteiros.

Mais sobre parâmetros de referência em C++

Alguns programadores também preferem declarar o protótipo assim:

  • square (int&);

Você também pode usar assim no protótipo:

  • square(int &num);
  • square(int& num);


Também pode, o importante é que o & esteja tanto no protótipo quanto na declaração da função, ok?

Lembrando que se seu parâmetro é de referência, ele só pode trabalhar com variável de referência.
Seria um erro se você usar um argumento que não é uma variável, como um literal, uma expressão ou uma constante, por exemplo:

  • square(10); // tem que usar square(number);
  • square(number+1); // é uma expressão, evite
Quando fazemos: square(int &num)
Leia-se: "num é uma referência para um inteiro", ou seja, ele está referenciando, apontando, indicando um local da memória em que está armazenado um inteiro. Como você tem o local da variável original, você consegue mudar esse valor.

Exercício de passagem de valor e referência

Crie um programa que pede um inteiro ao usuário. Em seguida, ele deve transformar esse valor em seu cubo. Faça isso usando uma função que recebe passagem por valor e outra que usa parâmetro e variável de referência. Deixe bem claro que uma altera o valor somente dentro da função (valor) e a outra altera o valor original da variável (referência). 

Cole nos comentários, sua solução.

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