Breve revisão

editar

Conceito

editar

Da linguagem "C" também temos o conceito de estrutura, do qual faremos uma pequena revisão agora. Como todos sabemos, nem todos os dados que precisamos usar podem ser agrupados em matrizes. Frequentemente usamos dados de diversos tipos diferentes, com tamanhos diferentes. Para tipos de dados de diferentes tamanhos existem estruturas de armazenamento de dados heterogêneos.

O especificador struct é usado para esta finalidade. Com ele podemos criar tipos que armazenam dados compostos por agrupamentos de outros tipos primitivos da linguagem. Geralmente, os dados são armazenados de forma a facilitar a identificação de cada campo de dados que pretende-se manter, para isso usamos nomes para cada campo dentro da estrutura de dados, de forma a ter um meio de acessá-la depois.

Estruturas são blocos básicos de informação e são manipulados de maneira primitiva. Basicamente o compilador instrui a montagem de um código que manipula-as de forma a copiar, referenciar e obter posição na memória. Todas as outras formas de tratar os dados devem ser providas pelo código do programa.

Implementação

editar

Para criar um tipo de dados composto heterogêneo, basicamente, cria-se uma lista de tipos e nomes de variáveis separadas por ponto e vírgula. Podemos imaginar esta lista como um bloco de código, em linguagem C, onde estão presentes apenas as declarações de variáveis. Para isso temos a seguinte sintaxe:

struct Estrutura
{
  <Tipo A> NomeVariavelA;
  <Tipo A> NomeVariavelA2;
  <Tipo A> NomeVariavelA3;
  <Tipo B> NomeVariavelB;
  <Tipo C> NomeVariavelC;
  <Tipo D> NomeVariavelD;
  ...
  ...
  <Tipo Z> NomeVariavelZ;


} [<NomeVariavelComposta>];

O nome da variável composta pode ser omitido na declaração da estrutura e depois definido onde for mais apropriado, geralmente, dentro de algum bloco de código onde venha a ser usado. Podemos ver logo abaixo, o exemplo de uma declaração de estrutura:

struct Estrutura
{
  int Inteiro;
  double PontoFlutuante;
  char Caracteres[10];
};

int main()
{
  Estrutura MinhaVariavelComposta;
  ...
  ...
  ...
  return 0;
}

Acessando dados internos

editar

O modo de acesso a variáveis internas de uma estrutura é feito através do operador ponto ".", porém, quando usamos ponteiros para guardar o endereço de uma estrutura usamos o operador seta "->". Vejamos um pequeno trecho de código:

   Estrutura st;
   Estrutura *pst;
   
   st.Inteiro = 200;

   pst = &st;
   
   pst->PontoFlutuante = 23.976;

Estruturas em C++

editar

As estruturas em C++ funcionam de modo análogo ao apresentado em linguagem "C". A diferença, a princípio, notável entre elas nas duas linguagens é que em "C++" o especificador struct não precisa ser escrito quando criamos a estrutura:

Em C, para criar uma estrutura de dados chamada st, declaramos:

  struct Estrutura st;

Para fazer o mesmo em C++, declaramos:

  Estrutura st;


Este simples detalhe revela uma característica importante das estruturas em C++: Nesta linguagem as estruturas são tratadas como tipos primitivos de objetos. Elas têm características semelhantes às classes, que veremos nos capítulos subsequentes.

As estruturas em C++ também podem conter funções além de dados. Este fato vem da ideia de modelo de objeto que as estruturas mantém nesta linguagem. Objetos devem ter propriedades (variáveis) e métodos (funções membro), por isso temos a possibilidade de criar funções dentro do corpo das estruturas, com a finalidade de lidar com os dados que elas mantém.

Construtores

editar

Os construtores são funções que são criadas automaticamente sempre que tentamos criar um objeto. Eles funcionam da mesma maneira que construtores de classe. A esses que são criados automaticamente são os chamados de defaut.

Se escrevermos o código:

#include <iostream>
#include <string>
using namespace std;

 struct Person 
 {
   string name; 
   float height;
 };

 int main ()
 {
   Person p1;
   cout << "O nome da pessoa é " << p1.name << " e a altura é " << p1.height << endl;
   cin.get();   
   return 0;
 }

O resultado é:

O nome da pessoa é e a altura é 1.75+e2783

Aqui é criado um construtor defaut no momento em que criamos a instância p1 ex com a linha Person p1;

Como as variáveis membro não foram iniciadas, o valor de name está vazio e o na variável height está um valor qualquer – que é lixo!

Construtor sem argumentos

Podemos ter um construtor sem argumentos que ao contrário do construtor defaut designa valores padrões às variáveis membro.

 struct Person 
 {
   string name; 
   int height;
   Person()		//construtor sem argumentos
   {
      name = "Nenhum nome assinalado";
      height = -1;
   }
 };
  • O nome do construtor é sempre igual ao nome da estrutura, sem exceção.
  • O construtor não retorna qualquer valor, sem exceção

Refazendo o nosso exemplo

 #include <iostream>
 #include <string>
 using namespace std;

 struct Person {
    string name;
    int height;
    Person()
    {
           name = "Nenhum nome assinalado";
           height = -1;
    }
 };
 int main ()
 {
    Person p1; 
    cout << "O nome da pessoa é "<< p1.name << " e a altura é " << p1.height << endl;
    cin.get();
    return 0;
 }

Repare que demos valores padrões ás variáveis. Agora não estamos no caso de ter p1.name=??? Por mais instâncias que criemos eles vão ter sempre valores padrões.

Construtor com argumentos

Termos um construtor sem argumentos é um melhoramento face ao construtor defaut pois agora temos valores padrões para as variáveis membro. Porém seria melhor se conseguíssemos inicializar as variáveis membro com valores dados pelo utilizador enquanto o programa estivesse sendo executado. E realmente podemos fazer se passarmos argumentos.

 #include <iostream>
 #include <string>
 using namespace std;
 
 struct Person 
 {
    string name;
    float height;
    Person()	//construtor sem argumentos
       {
       name = "Nenhum nome assinalado";
       height = -1;
       }
    Person(string s, float h)  //construtor com 2 argumentos
    {
       name = s;
       height = h;
    }
 };  
 
 int main ()
 {
   float metro;
   string strName;
   cout << "Entre com o nome da pessoa: ";
   getline(cin, strName);
   cout << "Entre com a altura em metros: ";
   cin >> metro;
   cin.ignore();   
   Person p1(strName,metro); 
   cout << "O nome da pessoa é " << p1.name << " e a altura é " << p1.height << endl;
   cin.get();
   return 0;
 }


Repare que os argumentos do construtor têm de estar na ordem esperada

Separar o construtor prototype da implementação
 #include <iostream>
 #include <string>
 using namespace std;
 
 struct Person {
   string name;
   float height;
   
   Person();		//construtor sem argumento
   Person(string, float);	//construtor com dois parâmetros, apenas é necessário dizer o tipo dos parâmetros – o nome não é necessário)
 };
 
  Person::Person()
  {
      name = "Nenhum nome assinalado";
      height = -1;
  }
  
  Person::Person(string s, float h)
  {
      name = s;
      height = h;
 }
 
 int main ()
 {
   float metro;
   string strName;
   cout << "Entre com o nome da pessoa: ";
   getline(cin, strName);
   cout << "Entre com a altura da pessoa em metros: ";
   cin >> metro;
   cin.ignore();   
   Person p1(strName, metro); 
   cout << "O nome da pessoa é " << p1.name << " e a altura é " << p1.height << endl;
   cin.get();
   return 0;
}
  • Vamos ver a função main(): declaramos 2 variáveis uma float e outra string. Pedimos para a pessoa escrever o nome e colocamos o valor na variável string, depois pedimos a altura e colocámos na variável float. Depois chamamos o construtor com dois argumentos e passamos as variáveis anteriores como argumentos. Por fim mandamos imprimir os valores das variáveis membro da estrutura.
  • Repare que para definirmos fora o construtor recorremos ao operador scope ::
 Person::Person()
 Person::Person(string s, int h)
  • Repare que no prototype dos construtor apenas tivemos de dizer o tipo dos parâmetros