Graduação em TI (ADS/SI/CS) > Estrutura de Dados

Aula 7 - Tipos Abstratos de Dados

(1/1)

Jefferson:
A primeira coisa a fazer é tentar entender o programa exemplo dado pelo professor. O programa mostrado nos slides tem alguns erros que eu espero ter corrigido aqui. Esse é o nosso primeiro exercício que requer mais de um arquivo:

ponto.h


--- Código: C++ ---12345678910111213/* TAD: Ponto (x,y) *//* Tipo exportado */typedef struct ponto Ponto;/* Funções exportadas *//* Função cria - Aloca e retorna um ponto com coordenadas (x,y) */Ponto* pto_cria (float x, float y);/* Função libera - Libera a memória de um ponto previamente criado */void pto_libera (Ponto* p);/* Função acessa - Retorna os valores das coordenadas de um ponto*/void pto_acessa (Ponto* p, float* x, float* y);/* Função atribui - Atribui novos valores às coordenadas de um ponto */void pto_atribui (Ponto* p, float x, float y);/* Função distancia - Retorna a distância entre dois pontos */float pto_distancia (Ponto* p1, Ponto* p2);
ponto.cpp


--- Código: C++ ---12345678910111213141516171819202122232425262728293031323334353637383940#include <iostream>#include <math.h>#include "ponto.h" using std::cout;using std::endl; struct ponto {    float x;    float y;};//Implementa as funções criadas no "ponto.h"Ponto* pto_cria (float x, float y) {    Ponto* p = new Ponto;    if (p == NULL) {        cout << "Memória insuficiente!\n" << endl;        exit(1);    }    p->x = x;    p->y = y;    return p;}void pto_libera (Ponto* p) {    delete p; } void pto_acessa (Ponto* p, float* x, float* y) {    *x = p->x;    *y = p->y;}void pto_atribui (Ponto* p, float x, float y) {    p->x = x;    p->y = y;}float pto_distancia (Ponto* p1, Ponto* p2) {    float dx = p2->x - p1->x;    float dy = p2->y - p1->y;    return sqrt(dx*dx + dy*dy);}
main.cpp


--- Código: C++ ---12345678910111213141516#include <iostream>#include "ponto.h" using std::cout;using std::endl; int main (void) {    float x, y;    Ponto* p = pto_cria(2.0,1.0);    Ponto* q = pto_cria(3.4,2.1);    float d = pto_distancia(p,q);    cout <<"Distancia entre pontos: "<< d << endl;    pto_libera(q);    pto_libera(p);    return 0;}
Rodar esse programa é um pouco mais complicado que o que estávamos acostumados. Até agora todos os exercícios podiam ser executados no DevC++ em um único arquivo e sem a criação de um projeto. Mas agora você precisa criar um projeto e adicionar esses três arquivos, do contrário você vai se deparar com esses erros:

undefined reference to `'pto_cria(float, float)'
undefined reference to `'pto_cria(float, float)'
undefined reference to `'pto_distancia(ponto*, ponto*)'
undefined reference to 'pto_libera(ponto*)'
undefined reference to 'pto_libera(ponto*)'

Isso acontece porque o arquivo main.cpp tem uma referência a ponto.h, mas em ponto.h não h´á referência a ponto.cpp. Eu esperava que por terem o mesmo nome essa localização fosse automática, mas não é. "undefined reference" significa que o linker não consegue encontrar o arquivo compilado que tem a implementação das funções.

Jefferson:
Exercício 1


--- Citar ---Programe uma aplicação que leia o valor de dois
pares de pontos, representando cada par um
uma reta, e diga se as retas são ou não paralelas
(duas retas são paralelas se possuem o mesmo
coeficiente angular).
--- Fim de citação ---

Não está no enunciado que devemos usar ponteiros, TAD, etc. Mas é óbvio pelo contexto que é isso que o professor deseja.

reta.h


--- Código: C++ ---123456 typedef struct Treta Reta; bool reta_paralelas (Reta* r1, Reta* r2);float reta_calcularCoeficienteAngular(Reta* r);Reta * reta_cria( float x1, float y1, float x2, float y2);void reta_libera (Reta* r);
reta.cpp


--- Código: C++ ---12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364#include "reta.h"#include <iostream> using std::cout;using std::endl; struct Tponto {    float x;    float y;}; struct Treta{  Tponto ponto1;  Tponto ponto2; };  float reta_calcularCoeficienteAngular(Reta *r){     float deltay=r->ponto2.y-r->ponto1.y;    float deltax=r->ponto2.x-r->ponto1.x;    float m= deltay/deltax;        //TODO: debug. Remover após testes    cout <<"\np1x: " << r->ponto1.x << '\n';    cout <<"p1y: " << r->ponto1.y << '\n';    cout <<"p2x: " << r->ponto2.x << '\n';    cout <<"p2y: " << r->ponto2.y << '\n';    cout <<"deltax: " << deltax << '\n';    cout <<"deltay: " << deltay << '\n';    cout <<"coeficiente: " << m << "\n\n";  return m;} Reta *reta_cria( float x1, float y1, float x2, float y2){ Reta *r = new Reta;     if (r == NULL) {        cout << "Memória insuficiente!\n" << endl;        exit(1);    }    r->ponto1.x=x1;    r->ponto1.y=y1;    r->ponto2.x=x2;    r->ponto2.y=y2; return r;} bool reta_paralelas (Reta *r1, Reta *r2){     float m1=reta_calcularCoeficienteAngular(r1);    float m2=reta_calcularCoeficienteAngular(r2);     if (m1==m2) return true;        else        return false; } void reta_libera (Reta *r){        delete r;}
main.cpp


--- Código: C++ ---12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758#include <iostream>#include <sstream>#include "reta.h" using std::cin;using std::cout;using std::string;using std::stringstream; int receberValor(string texto); int main(){      cout << "Reta 1 - Ponto 1\n";    float x1 = receberValor("X: ");    float y1= receberValor("Y: ");     cout << "Reta 1 - Ponto 2\n";    float x2= receberValor("X: ");    float y2= receberValor("Y: ");     Reta * reta1= reta_cria(x1,y1,x2,y2);        cout << "Reta 2 - Ponto 1\n";    x1 = receberValor("X: ");    y1= receberValor("Y: ");     cout << "Reta 2 - Ponto 2\n";    x2= receberValor("X: ");    y2= receberValor("Y: ");     Reta * reta2= reta_cria(x1,y1,x2,y2);        if (reta_paralelas(reta1, reta2)) cout << "As retas são paralelas";    else    cout << "As retas não são paralelas";        reta_libera(reta1);    reta_libera(reta2); }  int receberValor(string texto){    string temp="";    int num=0;    while (true){        cout << texto;        getline(cin, temp);        stringstream myStream(temp);        if (myStream >>num) break; //sucesso na conversão            else            cout << "\nNúmero inválido. Tente de novo." << '\n';    };   return num;} 

Jefferson:
Exercício 2


--- Citar ---Especifique um novo TAD para representar o resultado da avaliação de um aluno. O TAD deve ser composto de uma estrutura que irá representar alunos em uma faculdade que contenha o nome, curso, período, disciplinas atuais e notas de uma disciplina que o aluno está pagando atualmente.
Devem ser especificadas e implementadas subrotinas para - criar, acessar, liberar e atribuir valores - a um Aluno, calcular sua média (Estácio) e verificar se o aluno está ou não aprovado. Por fim, crie um menu para o usuário permitindo que ele adicione quantos alunos ele desejar dinamicamente.
--- Fim de citação ---

Meu programa ainda tem uma deficiência: eu não implementei a adição dinâmica de disciplinas. Embora isso não tenha sido explicitamente pedido pelo professor é razoável esperar que um programa desse tipo não crie essa limitação. Eu limitei o número de disciplinas a três para facilitar os testes.

main.cpp


--- Código: C++ ---1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192#include <iostream>#include <strings.h>#include <sstream>#include <locale.h>#include "aluno.h" using std::cout;using std::cin;using std::string;using std::stringstream; int receberValor(string texto);int main() {     setlocale(LC_ALL,"");        Taluno * ptrAlunos= aluno_criar();    int numAlunos=0;     string resposta;    cout <<"Você deseja ler os dados do (a)rquivo ex4.txt ou (d)igitá-los?\n";    getline(cin, resposta);    if (resposta=="a")  {        freopen("ex4.txt", "r", stdin);         string temp="";        //cout << "Quantos alunos? ";        getline(cin, temp);        int numAlunosArq=stoi(temp);         for (int i=0;i<numAlunosArq;i++){            cout <<"Dados do aluno "<< i+1 <<": \n";            aluno_adicionar(ptrAlunos,&numAlunos);        }         aluno_imprimirTodos(ptrAlunos, numAlunos);    }    if (resposta!="a"){ //Só exibo o menu se não estiver lendo de um arquivo    string opcao="";     while (opcao!="0"){          cout <<"Escolha uma opção e em seguida tecle [ENTER]:\n\n";        cout <<"0 - Sair do programa.\n";        cout <<"1 - Adicionar um aluno.\n";        if (numAlunos>0) cout <<"2 - Listar todos os alunos.\n";        if (numAlunos>0) cout <<"3 - Deletar aluno.\n";         getline(cin, opcao);        if (opcao=="1"){            aluno_adicionar(ptrAlunos,&numAlunos);            cout << "Aluno adicionado.";            cout << "Tecle ENTER para voltar ao menu.";        };         if (opcao=="2"){            aluno_imprimirTodos(ptrAlunos, numAlunos);            cout << "tecle ENTER para voltar ao menu.";            getline(cin, opcao);        };         if (opcao=="3"){             aluno_deletar(ptrAlunos, &numAlunos, receberValor("Digite o número do aluno a deletar"));            cout << "Aluno deletado.";            cout << "Tecle ENTER para voltar ao menu.";        };      if (system("CLS")) system("clear"); //apago a tela. Este método é simples mas tem problemas de segurança    }}         aluno_liberar(ptrAlunos);return 0;} int receberValor(string texto){    string temp="";    int num=0;    while (true){        cout <<  texto;        cin >> temp;        stringstream myStream(temp);        if (myStream >>num) break; //sucesso na conversão            else            cout << "\nNúmero inválido. Tente de novo." << '\n';    };   return num;}

aluno.h


--- Código: C++ ---123456789101112131415/*A linha typedef seguinte foi colocada apenas para evitar o erro[Error] 'Taluno' was not declared in this scopeO alias "Aluno" poderia ser qualquer outra coisa*/typedef struct Taluno Aluno; float aluno_calcularMedia(Taluno *a);void aluno_cadastrar (Taluno *a);void aluno_imprimirTodos (Taluno *a, int total);void aluno_imprimir (Taluno *a, int numRec);void aluno_adicionar(Taluno *&a, int *numRegistros);void aluno_deletar(Taluno *&a, int *numRegistros, int numRec);Taluno *aluno_criar();void aluno_liberar (Taluno *a);
aluno.cpp


--- Código: C++ ---123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132#include <iostream>#include "aluno.h" using std::cout;using std::cin;using std::string; struct Taluno{    string nome;    string periodo;    string curso;    string disciplinas[3];    float notas[3]; }; Taluno *aluno_criar(){ Taluno *a = new Taluno;     if (a == NULL) {        cout << "Memória insuficiente!\n" << '\n';        exit(1);    } return a;} void aluno_liberar (Taluno *a){     delete []a;} void aluno_cadastrar (Taluno *a){    //Esta é a rotina que eu considero que se encaixa no requerimento "atribuir valores"    cout <<"Nome: ";    getline(cin, a->nome);    cout <<"Período: ";    getline(cin, a->periodo);    cout <<"Curso: ";    getline(cin, a->curso);    cout <<"Disciplina 1: ";    getline(cin, a->disciplinas[0]);    cout <<"Disciplina 2: ";    getline(cin, a->disciplinas[1]);    cout <<"Disciplina 3: ";    getline(cin, a->disciplinas[2]);     string temp="";    for (int j=0; j<3;j++){        cout <<"Nota " << j+1 << ": ";        getline(cin, temp);        a->notas[j]=stof(temp);     }     cout <<'\n';} float aluno_calcularMedia(Taluno *a){    //O cálculo é feito seguindo as regras da Estácio     float menorNota=10;    unsigned int indiceMenorNota=0;    float notas[3];     for (int j=0; j<3; j++){         if (a->notas[j]<4) notas[j]=0; else notas[j]=a->notas[j]; //se alguma nota for menor que 4 é o mesmo que ser zero        if (notas[j]<menorNota) {            menorNota=notas[j];            indiceMenorNota=j;        }    }       //Ignoro o menor valor, somo os restantes e divido por dois    float k=0;    if (indiceMenorNota==0) {k=((notas[1]+notas[2])/2);};    if (indiceMenorNota==1) {k=((notas[0]+notas[2])/2);};    if (indiceMenorNota==2) {k=((notas[0]+notas[1])/2);};    return k;} void aluno_imprimirTodos (Taluno *a, int total){    cout <<"Exibindo agora os dados gravados\n\n";    for (int i=0;i<total;i++){      aluno_imprimir(a, i);    } } void aluno_imprimir (Taluno *a, int numRec){    //Esta é a rotina que eu considero que se encaixa no requerimento "acessar valores"    cout <<"Nome: " << a[numRec].nome <<'\n';    cout <<"Período: " << a[numRec].periodo <<'\n';    cout <<"Curso: " << a[numRec].curso <<'\n';    for (int j=0; j<3;j++){        cout <<"Disciplina " << j+1 <<": " << a[numRec].disciplinas[j] <<'\n';    }    for (int j=0; j<3;j++){        cout <<"Nota " << j+1 <<": " << a[numRec].notas[j] <<'\n';    }    float media = aluno_calcularMedia(&a[numRec]);     if (media>=6) cout <<"Aluno APROVADO\n"; else cout <<"Aluno REPROVADO\n";     cout <<'\n'; } void aluno_adicionar(Taluno *&a, int *numRegistros){    //Aumenta o tamanho do vetor em um elemento e preenche esse elemento     Taluno *temp = new Taluno[*numRegistros+1];    std::copy(a,a+*numRegistros,temp);     delete a;    a=temp;    aluno_cadastrar(&a[*numRegistros]); //Agora que o vetor tem um elemento extra, numRec aponta para ele    *numRegistros=*numRegistros+1; } void aluno_deletar(Taluno *&a, int *numRegistros, int numRec){    //crio um novo vetor com um elemento a menos e copio do vetor antigo    //todos os elemmentos anteriores e posteriores ao registro a ser apagado    Taluno *temp = new Taluno[*numRegistros-1];    std::copy(a,a+numRec-1,temp);    std::copy(a+numRec,a+*numRegistros,temp+numRec-1);     delete a;    a=temp;    *numRegistros=*numRegistros-1; }

Jefferson:
Até onde pude entender, o objetivo do exercício é isolar funções e tipos relativos a um determinado dado em arquivos distintos e separados do principal.  Isso parece simples, mas sem ter uma razoável compreensão de como a coisa toda funciona tentar fazer isso pode ser extremamente frustrante por causa de novas mensagens de erro que não são nada intuitivas.

Por exemplo, após mover a struct para outro arquivo a seguinte declaração não funciona mais em main.cpp:

Taluno * ptrAlunos= new Taluno;

Acusa os erros:
[Error] invalid use of incomplete type 'struct Taluno'
[Error] forward declaration of 'struct Taluno'

Já esta aqui funciona (desde que a função  aluno_criar() já exista, claro):

Taluno * ptrAlunos= aluno_criar();

Depois que você entende de onde vem o erro percebe que apesar de "Taluno" ser aceito pelo compilador, "new Taluno" só pode ocorrer no mesmo arquivo onde a struct Taluno foi declarada. O compilador sabe o que é Taluno (aparentemente por causa da declaração typedef) mas não sabe do que ele é feito, daí o "incomplete". Eu confesso que ainda não entendi realmente como a coisa funciona. Só consegui consertar atrav´és de tentativa e erro, comparando com o exemplo dado pelo professor.

Para quem entende inglês, este texto parece ser bem esclarecedor.

Navegação

[0] Índice de mensagens

Responder

Ir para versão completa