Difference between revisions of "Introdução aos Objectos/Objectos em C: Tipos de Dados Abstractos"

From Wiki**3

< Introdução aos Objectos
(Programa exemplo)
(Implementation do conceito "Cat")
Line 178: Line 178:
 
Notar a repetição de muitas definições (relativamente a Animal).
 
Notar a repetição de muitas definições (relativamente a Animal).
  
Ficheiro '''Cat.c''':
+
{{CollapsedCode|Ficheiro '''Cat.c'''|
 
 
 
<c>
 
<c>
 
#include <string.h>
 
#include <string.h>
Line 238: Line 237:
 
}
 
}
 
</c>
 
</c>
 +
}}
  
 
=== Programa exemplo ===
 
=== Programa exemplo ===

Revision as of 23:44, 31 August 2015

Os exercícios seguintes ilustram a definição de tipos de dados abstractos e de suas instâncias. Estas instâncias são semelhantes aos objectos suportados por linguagens como o C++ ou o Java, mas, como estão implementados em C, algumas das operações têm de ser definidas explicitamente pelo programador.

Os exemplos tocam também no aspecto da reutilização de código: em linguagens OO, é algo que surge "naturalmente". Nestes exemplos, é necessário mais trabalho. Os exemplos são implementados em C++ no final (apenas para uma comparação mais directa com C: deixa-se como exercício a implementação em Java ou em outras linguagens de programação).

Animal simples

A tarefa é a modelação e implementação em C (ficheiros .h e .c) de uma estrutura de dados que represente uma versão simples do conceito “animal”.

Um animal tem como características o nome (_name), a idade (_age) e o peso (_weight).

Devem ser ainda implementadas as funções newAnimal e destroyAnimal. A função newAnimal reserva memória suficiente para representar um animal e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assume-se que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyAnimal liberta os recursos associados ao animal.

Outras funções a implementar:

  • função de comparação – equalsAnimal –, por forma a considerar que dois animais são iguais se as suas características forem iguais
  • funções de acesso às variáveis de um animal: getAnimalName, getAnimalAge, getAnimalWeight (dado um animal, retornam um dos seus campos)
  • função printAnimal que, quando aplicada a um animal, apresenta os seus dados (usa-se printf para apresentar cada campo do animal)
  • programa – main – que ilustra a utilização das funções anteriores

Interface do conceito "Animal"

Ficheiro Animal.h

<c>

  1. ifndef __ANIMAL_H__
  2. define __ANIMAL_H__

// this typedef is used to hide the concept's implementation details typedef struct animal *Animal;

Animal newAnimal(const char *name, int age, double weight); void destroyAnimal(Animal animal);

int equalsAnimal(Animal animal1, Animal animal2); const char *getAnimalName(Animal animal); int getAnimalAge(Animal animal); double getAnimalWeight(Animal animal); void printAnimal(Animal animal);

  1. endif

</c>

Implementação do conceito "Animal"

Ficheiro Animal.c

Programa exemplo

Ficheiro main.c
{{{2}}}

Como compilar e executar?

O programa é constituído por dois módulos independentes, que têm de ser compilados (conversão de C para código binário) e "linkados" (ligação de todos os módulos binários e bibliotecas adicionais), para produção do executável.

gcc -c Animal.c -o Animal.o
gcc -c main.c   -o main.o

A opção -o foi apresentada como o valor por omissão, apenas para explicitar o que acontece como resultado do comando.

gcc -o main main.o Animal.o

Note-se que o comando "gcc" não é simplesmente o compilador de C: é um programa capaz de chamar o compilador em si (quando se usa com a opção -c) ou capaz de invocar o "linker", se não for dito nada em contrário (último comando acima).

A execução é simples (o comando seguinte executa o programa "main" que está na directoria actual, i.e., "."):

 ./main

Gato simples

A tarefa é a modelação e implementação em C (ficheiros .h e .c) de uma estrutura de dados que represente uma versão simples do conceito “gato”.

Um gato tem como características o nome (_name), a idade (_age), o peso (_weight), o volume do ronronar (_purrLevel) e o grau de suavidade do pêlo (_fluffiness).

Devem ser ainda implementadas as funções newCat e destroyCat. A função newCat reserva memória suficiente para representar um gato e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assuma que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyCat liberta os recursos associados ao gato.

Outras funções a implementar:

  • função de comparação – equalsCat –, por forma a considerar que dois gatos são iguais se as suas características forem iguais
  • funções de acesso às variáveis de um gato: getCatName, getCatAge, getCatWeight, getCatPurrLevel, getCatFluffiness (dado um gato, retornam um dos seus campos)
  • função printCat que, quando aplicada a um gato, apresenta os seus dados (usa-se printf para apresentar cada campo do gato)
  • programa – main – que ilustra a utilização das funções anteriores

Interface do conceito "Cat"

Ficheiro Cat.h

<c>

  1. ifndef __CAT_H__
  2. define __CAT_H__

typedef struct cat *Cat;

Cat newCat(const char *name, int age, double weight, int purrLevel,

          double fluffiness);

void destroyCat(Cat cat);

int equalsCat(Cat cat1, Cat cat2); const char *getCatName(Cat cat); int getCatAge(Cat cat); double getCatWeight(Cat cat); int getCatPurrLevel(Cat cat); double getCatFluffiness(Cat cat); void printCat(Cat cat);

  1. endif

</c>

Implementation do conceito "Cat"

Notar a repetição de muitas definições (relativamente a Animal).

Ficheiro Cat.c

Programa exemplo

Ficheiro main.c
{{{2}}}

Como compilar e executar?

Tal como primeiro exemplo, o programa é constituído por dois módulos independentes, que têm de ser compilados (conversão de C para código binário) e "linkados" (ligação de todos os módulos binários e bibliotecas adicionais), para produção do executável. Neste caso, o módulo Animal.o não é utilizado e não aparece nos comandos:

gcc -c Cat.c  -o Cat.o
gcc -c main.c -o main.o

A opção -o foi apresentada como o valor por omissão, apenas para explicitar o que acontece como resultado do comando.

gcc -o main main.o Cat.o

Note-se que o comando "gcc" não é simplesmente o compilador de C: é um programa capaz de chamar o compilador em si (quando se usa com a opção -c) ou capaz de invocar o "linker", se não for dito nada em contrário (último comando acima).

A execução é simples (o comando seguinte executa o programa "main" que está na directoria actual, i.e., "."):

 ./main

Saída do programa

<text>

Cat

Name: Tareco Age: 12 Weight: 3.4 Purr level: 3 Fluffiness: 3.1

Cat

Name: Pantufa Age: 1 Weight: 12.3 Purr level: 2 Fluffiness: 2.7 c1==c1? yes c1==c2? no </text>

Gato menos simples

Como se pode observar acima, existe alguma repetição na definição de animal e de gato. Como o conceito de gato contém alguma informação do conceito de animal, seria conveniente a sua reutilização.

A tarefa é a modelação e implementação em C (ficheiros .h e .c) de uma estrutura de dados que represente uma versão do conceito “gato”, mas considerando-o como uma animal caracterizado pelo volume do ronronar (_purrLevel) e pelo grau de suavidade do pêlo (_fluffiness).

Tal como anteriormente, devem ser ainda implementadas as funções newCat e destroyCat. A função newCat reserva memória suficiente para representar um gato e permite inicializar os seus campos (através dos argumentos da função). Por simplicidade, assuma que o campo _name tem comprimento fixo máximo de 16 caracteres (incluindo o terminador). A função destroyCat liberta os recursos associados ao gato.

Note-se que o facto de o gato ser um animal não altera a necessidade de implementação das funções associadas ao gato.

Assim, as funções a implementar são:

  • função de comparação – equalsCat –, por forma a considerar que dois gatos são iguais se as suas características forem iguais
  • funções de acesso às variáveis internas de um gato: getCatName, getCatAge, getCatWeight, getCatPurrLevel, getCatFluffiness (dado um gato, retornam um dos seus campos). Note-se que algumas destas características são fornecidas pelo conceito “animal”
  • função printCat que, quando aplicada a um gato, apresenta os seus dados (usa-se printf para apresentar cada campo específico do gato)
  • programa – main – que ilustra a utilização das funções anteriores

Interface do conceito "Cat"

Notar que a interface é necessariamente igual à do exemplo anterior, pois é característica do conceito "gato".

Ficheiro Cat.h: <c>

  1. ifndef __CAT_H__
  2. define __CAT_H__

typedef struct cat *Cat;

Cat newCat(const char *name, int age, double weight, int purrLevel,

          double fluffiness);

void destroyCat(Cat cat);

int equalsCat(Cat cat1, Cat cat2); const char *getCatName(Cat cat); int getCatAge(Cat cat); double getCatWeight(Cat cat); int getCatPurrLevel(Cat cat); double getCatFluffiness(Cat cat); void printCat(Cat cat);

  1. endif

</c>

Implementação do conceito "Cat"

Notar as partes onde é utilizado o conceito "animal".

Ficheiro Cat.c: <c>

  1. include <string.h>
  2. include <stdlib.h>
  3. include <stdio.h>
  4. include "Animal.h"
  5. include "Cat.h"

// note that, since a cat is an animal, part of the cat will // be implemented as an animal. struct cat {

 Animal _animal;
 int _purrLevel;
 double _fluffiness;

};

Cat newCat(const char *name, int age, double weight, int purrLevel,

          double fluffiness) {
 Cat cat = (Cat)malloc(sizeof(struct cat));
 if (cat != NULL) {
   // use previously defined animal allocator
   cat->_animal = newAnimal(name, age, weight);
   if (cat->_animal == NULL) {
     free(cat);
     cat = NULL;
   }
   else {
     cat->_purrLevel = purrLevel;
     cat->_fluffiness = fluffiness;
   }
 }
 return cat;

}

// destroying the cat has to undo its construction void destroyCat(Cat cat) {

 if (cat) {
   // first, release the animal part
   destroyAnimal(cat->_animal);
   // then, release the rest of the cat
   free(cat);
 }

}

// these functions are implemented based on the animal versions const char *getCatName (Cat cat) { return getAnimalName (cat->_animal); } int getCatAge (Cat cat) { return getAnimalAge (cat->_animal); } double getCatWeight (Cat cat) { return getAnimalWeight(cat->_animal); }

// these are cat-specific functions int getCatPurrLevel (Cat cat) { return cat->_purrLevel; } double getCatFluffiness(Cat cat) { return cat->_fluffiness; }

/* note that we require cat1 and cat2 to be valid cats: any of

  them being NULL pointers will result in a false comparison. */

int equalsCat(Cat cat1, Cat cat2) {

 if (cat1 == NULL || cat2 == NULL) return 0;
 return equalsAnimal(cat1->_animal, cat2->_animal) &&
     getCatPurrLevel(cat1) == getCatPurrLevel(cat2) &&
     getCatFluffiness(cat1) == getCatFluffiness(cat2);

}

// note that the output here is slightly different, because it uses the // default animal implementation. // we could have used the animal interface to obtain individual fields. void printCat(Cat cat) {

 printf("== Cat ==\n");
 printAnimal(cat->_animal);
 printf("Purr level: %d\n", getCatPurrLevel(cat));
 printf("Fluffiness: %g\n", getCatFluffiness(cat));

}

</c>

Programa exemplo

Notar que este programa é idêntico ao do caso anterior, uma vez que depende exclusivamente da interface do conceito "gato" (que não mudou) e não da sua implementação.

Ficheiro main.c: <c>

  1. include <stdio.h>
  2. include "Cat.h"

int main() {

 Cat c1 = newCat("Tareco", 12, 3.4, 3, 3.1);
 Cat c2 = newCat("Piloto", 1, 12.3, 2, 2.7);
 printCat(c1);
 printCat(c2);
 printf("c1==c1? %s\n", equalsCat(c1, c1) ? "yes" : "no");
 printf("c1==c2? %s\n", equalsCat(c1, c2) ? "yes" : "no");
 destroyCat(c1);
 destroyCat(c2);
 return 0;

} </c>

Como compilar e executar?

Neste caso, o conceito de gato depende do conceito de animal inicialmente definido. Deste modo, o programa é constituído por três módulos a compilar separadamente (conversão de C para código binário) e "linkados" (ligação de todos os módulos binários e bibliotecas adicionais), para produção do executável.

gcc -c Animal.c -o Animal.o
gcc -c Cat.c    -o Cat.o
gcc -c main.c   -o main.o

A opção -o foi apresentada como o valor por omissão, apenas para explicitar o que acontece como resultado do comando.

gcc -o main main.o Cat.o Animal.o

Note-se que o comando "gcc" não é simplesmente o compilador de C: é um programa capaz de chamar o compilador em si (quando se usa com a opção -c) ou capaz de invocar o "linker", se não for dito nada em contrário (último comando acima).

A execução é simples (o comando seguinte executa o programa "main" que está na directoria actual, i.e., "."):

 ./main

Saída do programa

Notar que a apresentação do conceito "gato" expõe a sua implementação como parte "animal".

<text>

Cat

Animal

Name: Tareco Age: 12 Weight: 3.4 Purr level: 3 Fluffiness: 3.1

Cat

Animal

Name: Piloto Age: 1 Weight: 12.3 Purr level: 2 Fluffiness: 2.7 c1==c1? yes c1==c2? no </text>

Discussão