|
|
Line 1: |
Line 1: |
| {{TOCright}}
| | #REDIRECT [[ist:Introdução aos Objectos/Objectos em C: Tipos de Dados Abstractos]] |
| 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" ===
| |
| | |
| {{CollapsedCode|Ficheiro '''Animal.h'''|
| |
| <c>
| |
| #ifndef __ANIMAL_H__ | |
| #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);
| |
| | |
| #endif
| |
| </c>
| |
| }}
| |
| | |
| === Implementação do conceito "Animal" ===
| |
| | |
| {{CollapsedCode|Ficheiro '''Animal.c'''|
| |
| <c>
| |
| #include <string.h>
| |
| #include <stdlib.h>
| |
| #include <stdio.h>
| |
| #include "Animal.h"
| |
| | |
| struct animal {
| |
| char _name[16];
| |
| int _age;
| |
| double _weight;
| |
| };
| |
| | |
| Animal newAnimal(const char *name, int age, double weight) {
| |
| Animal animal = (Animal)malloc(sizeof(struct animal));
| |
| if (animal != NULL) {
| |
| strcpy(animal->_name, name);
| |
| animal->_age = age;
| |
| animal->_weight = weight;
| |
| }
| |
| return animal;
| |
| }
| |
| | |
| void destroyAnimal(Animal animal) {
| |
| if (animal)
| |
| free(animal);
| |
| }
| |
| | |
| const char *getAnimalName (Animal animal) { return animal->_name; }
| |
| int getAnimalAge (Animal animal) { return animal->_age; }
| |
| double getAnimalWeight(Animal animal) { return animal->_weight; }
| |
| | |
| /* note that we require animal1 and animal2 to be valid animals: any of
| |
| them being NULL pointers will result in a false comparison. */
| |
| int equalsAnimal(Animal animal1, Animal animal2) {
| |
| if (animal1 == NULL || animal2 == NULL) return 0;
| |
| return !strcmp(getAnimalName(animal1), getAnimalName(animal2)) &&
| |
| getAnimalAge(animal1) == getAnimalAge(animal2) &&
| |
| getAnimalWeight(animal1) == getAnimalWeight(animal2);
| |
| }
| |
| | |
| void printAnimal(Animal animal) {
| |
| printf("== Animal ==\n");
| |
| printf("Name: %s\n", getAnimalName(animal));
| |
| printf("Age: %d\n", getAnimalAge(animal));
| |
| printf("Weight: %g\n", getAnimalWeight(animal));
| |
| }
| |
| </c>
| |
| }}
| |
| | |
| === Programa exemplo ===
| |
| | |
| {{CollapsedCode|Ficheiro '''main.c'''|
| |
| <c>
| |
| #include <stdio.h>
| |
| #include "Animal.h"
| |
| | |
| int main() {
| |
| Animal a1 = newAnimal("Tareco", 12, 3.4); // could be a cat...
| |
| Animal a2 = newAnimal("Piloto", 1, 12.3); // is it a dog??
| |
| | |
| printAnimal(a1);
| |
| printAnimal(a2);
| |
| | |
| printf("a1==a1? %s\n", equalsAnimal(a1, a1) ? "yes" : "no");
| |
| printf("a1==a2? %s\n", equalsAnimal(a1, a2) ? "yes" : "no");
| |
| | |
| destroyAnimal(a1);
| |
| destroyAnimal(a2);
| |
| | |
| return 0;
| |
| }
| |
| </c>
| |
| }}
| |
| | |
| === 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" ===
| |
| | |
| {{CollapsedCode|Ficheiro '''Cat.h'''|
| |
| <c>
| |
| #ifndef __CAT_H__
| |
| #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);
| |
| | |
| #endif
| |
| </c>
| |
| }}
| |
| | |
| === Implementation do conceito "Cat" ===
| |
| | |
| Notar a repetição de muitas definições (relativamente a Animal).
| |
| | |
| {{CollapsedCode|Ficheiro '''Cat.c'''|
| |
| <c>
| |
| #include <string.h>
| |
| #include <stdlib.h>
| |
| #include <stdio.h>
| |
| #include "Cat.h"
| |
| | |
| struct cat {
| |
| char _name[16];
| |
| int _age;
| |
| double _weight;
| |
| 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) {
| |
| strcpy(cat->_name, name);
| |
| cat->_age = age;
| |
| cat->_weight = weight;
| |
| cat->_purrLevel = purrLevel;
| |
| cat->_fluffiness = fluffiness;
| |
| }
| |
| return cat;
| |
| }
| |
| | |
| void destroyCat(Cat cat) {
| |
| if (cat)
| |
| free(cat);
| |
| }
| |
| | |
| const char *getCatName (Cat cat) { return cat->_name; }
| |
| int getCatAge (Cat cat) { return cat->_age; }
| |
| double getCatWeight (Cat cat) { return cat->_weight; }
| |
| 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 !strcmp(getCatName(cat1), getCatName(cat2)) &&
| |
| getCatAge(cat1) == getCatAge(cat2) &&
| |
| getCatWeight(cat1) == getCatWeight(cat2) &&
| |
| getCatPurrLevel(cat1) == getCatPurrLevel(cat2) &&
| |
| getCatFluffiness(cat1) == getCatFluffiness(cat2);
| |
| }
| |
| | |
| void printCat(Cat cat) {
| |
| printf("== Cat ==\n");
| |
| printf("Name: %s\n", getCatName(cat));
| |
| printf("Age: %d\n", getCatAge(cat));
| |
| printf("Weight: %g\n", getCatWeight(cat));
| |
| printf("Purr level: %d\n", getCatPurrLevel(cat));
| |
| printf("Fluffiness: %g\n", getCatFluffiness(cat));
| |
| }
| |
| </c>
| |
| }}
| |
| | |
| === Programa exemplo ===
| |
| | |
| {{CollapsedCode|Ficheiro '''main.c'''|
| |
| <c>
| |
| #include <stdio.h>
| |
| #include "Cat.h"
| |
| | |
| int main() {
| |
| Cat c1 = newCat("Tareco", 12, 3.4, 3, 3.1);
| |
| Cat c2 = newCat("Pantufa", 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? ===
| |
| | |
| 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".
| |
| | |
| {{CollapsedCode|Ficheiro '''Cat.h'''|
| |
| <c>
| |
| #ifndef __CAT_H__
| |
| #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);
| |
| | |
| #endif
| |
| </c>
| |
| }}
| |
| | |
| === Implementação do conceito "Cat" ===
| |
| | |
| Notar as partes onde é utilizado o conceito "animal".
| |
| | |
| {{CollapsedCode|Ficheiro '''Cat.c'''|
| |
| <c>
| |
| #include <string.h>
| |
| #include <stdlib.h>
| |
| #include <stdio.h>
| |
| #include "Animal.h"
| |
| #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.
| |
| | |
| {{CollapsedCode|Ficheiro '''main.c'''|
| |
| <c>
| |
| #include <stdio.h>
| |
| #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 ==
| |
| | |
| [[category:Ensino]]
| |
| [[category:PO]]
| |
| [[category:PO Exemplos]]
| |
| [[category:C++]]
| |