Trabalho prático de declaração e definição de classes
e utilização de classes de biblioteca
Mais bolas?
1.
2.
3.
4.
5.
6.
7.
8.
Antes de começar este trabalho prático, feche os olhos, e veja se se lembra de todos os
passos necessários para criar uma solução, para criar um projecto (de que tipo?), para
criar os ficheiros .h e .cpp. Lembra-se de para onde é que vão estas coisas? Se não se
lembrar, consulte o primeiro guião. Repare: se o projecto tiver sido criado erradamente,
se os ficheiros estiverem nas directorias erradas, ou forem do tipo errado, ou tiverem o
nome errado, depois é uma confusão.
Neste exercício, você vai declarar de raiz uma classe e programá-la, tal como no
exercício anterior. A principal diferença, é que hoje você vai usar também algumas
classes já programadas, que lhe vão ser fornecidas.
Abra o Visual C++ e crie uma nova solução FiguresSolution e lá dentro um projecto
CirclesProject. Verifique com o Explorer que ficou tudo nas directorias certas. (Não sabe
quais são as directorias certas? Então releia o primeiro guião.)
Copie para a sua directoria Library, os ficheiros das classes Point, Figure e Line,
acessíveis através do link para as classes do livro recomendado. Esse link está disponível
na página da cadeira, em "Bibliografia & Links". A classe Point é semelhante à da
sebenta, mas contém umas “complicações”. As outras duas são “novas”. Mas não se
atrapalhe. Você não vai modificar estes ficheiros, apenas os vai usar, tal como estão.
Incluí-los-á nos seus projectos, para poder tirar partido da classe Point fornecida, mas
sem lhes mexer. Se não respeitar esta regra, isto é, se começar a chafurdar nos ficheiros
de biblioteca, depois não se queixe. Mas se detectar algum erro ou alguma incorrecção
nestas classes, ou no futuro noutras que serão fornecidas pelo mesmo sistema, por favor
avise o seu assistente e siga as suas (do assistente) instruções.
Volte ao Visual C++, que na janela Solution Explorer deve exibir a solução
FiguresSolution e o projecto CirclesProject, com a pasta do projecto seleccionada faça
File>Add Existing Item, seleccione os ficheiros Figures.h, Figures.cpp, Points.h,
Point.cpp, Line.h e Line.cpp, na sua directoria Library, e faça Open. Os seis ficheiros
devem agora estar no projecto. Abra as pastas Source Files e Header Files para
confirmar.
Dê uma vista de olhos aos ficheiros, e compile -os, um a um. Dê outra vista de olhos,
agora com mais atenção, aos ficheiros Point.h e Point.cpp. Repare nos operadores == e
!=. Servem para ver se um ponto é igual a outro, e diferente de outro, respectivamente,
escrevendo simplesmente p1 == p2 ou p1 != p2, para dois pontos p1 e p2, como se
fossem números. Observe as funções Translate, Scale e Rotate. Os nomes são
significativos, mas se não perceber o que fazem, estude as definições. Repare nas
funções Write, WriteLine e Read, que têm parâmetros com valores por defeito. Por
exemplo, se programarmos p.Write(), para um ponto p, as coordenadas de p aparecem na
consola. Se quisermos que apareçam num outro ficheiro, teremos que o indicar
explicitamente no argumento. Repare também na função extra, que é só um “mau”
exemplo de um “selector-modificador”, o tipo de funções que nós nunca queremos usar.
Crie o ficheiro M_Circle.cpp, ou de raiz, como no primeiro guião, ou usando o seguinte
expediente: abra o ficheiro M_Parabola.cpp, guarde com o novo nome M_Circle.cpp,
apague o que estiver a mais e não valer a pena reutilizar, e junte ao projecto movendo-o
da pasta Miscellaneous Files para a pasta Source Files do projecto. Mude os includes.
Agora temos que incluir pelo menos o fic heiro com a declaração da classe Point, além do
iostream, que deve vir antes (porquê?). Será que o compilador “pede” mais includes?
Escreva uma função main vazia (só com return 0;) e compile. Tudo bem? Não deve estar.
Ao compilar, o compilador queixa-se de que não consegue encontrar os ficheiros .h. A
1
9.
10.
11.
12.
13.
14.
15.
16.
razão é simples: os ficheiros .h estão na directoria Library e o ficheiro M_Circle.cpp está
na directoria C++. Se não está, devia estar. O compilador precisa de uma ajudinha nossa
para ir buscar os ficheiros à directoria Library. Faça assim: seleccione o projecto no
Solution Explorer e faça View>Property Pages. Escolha a pasta C/C++ e dentro desta o
item General; no Additional Include Directories escreva o nome completo da sua
directoria Library: “C:\…\…\Library”. (Sugestão: abra o Explorer na directoria Library e
copie o nome completo da caixa Address.) Você terá que fazer isto para cada novo
projecto, não se esqueça! Compile de novo e linque. Agora já deve estar tudo bem. Até
pode correr o programa, mas o programa não faz nada (pudera: a função main só tem um
return 0.)
Agora acrescente uma linha na função main, apenas com a declaração de uma variável de
tipo Point. Compila bem? Se sim, é bom sinal. Se não, veja o que se passa.
Para começar a fazer qualquer coisa, escreva uma função de teste que aceita um ponto, e
depois move-o 2 para a direita 1 para baixo, roda-o de 90 graus e escala -o 0.5. Note que
o argumento da função Rotate vem expresso em radianos, não em graus. Para passar de
graus para radianos multiplica-se por pi e divide-se por 180, não é? Eis o valor de pi com
20 casas decimais: 3.14159265358979323846. Trate de decorar isto J, pois nem sempre
terá este guião à mão, ou então, se for preguiçoso (como convém a um programador),
quando for preciso o valor de pi, use o quádruplo do arcotangente de 1. Para o
arcotangente use a função ::atan, do <cmath>. Experimente.
Escreva agora outra função de teste mais problemática. A função pede um número e
calcula os vértices de um polígono regular com esse número de lados, centrado na
origem, começando no ponto de coordenadas <1, 0>, e inscrito na circunferência de raio
unitário. Como resultado, queremos ver na consola as coordenadas dos vértices, com
duas casas decimais.
Não se preocupe demasiado com a classe Figure. Hoje ela só é necessária porque a classe
Point “deriva” dela (veja a primeira linha da declaração da classe Point), mas nós não
tiramos grande partido disso neste trabalho. Por sua vez, a classe Line é precisa porque a
classe Figure a usa.
Passemos agora à classe de hoje, a classe Circle. A sua tarefa é desenhar e implementar
uma classe Circle para representar círculos no plano cartesiano. Cada objecto de tipo
Circle tem dois membros de dados, o centro, center, de tipo Point, e o raio, radius, de
tipo double. Inclua para começar um construtor por defeito, que inicializa um círculo de
raio 1 na origem, um construtor elementar, com argumentos para o centro e para o raio,
os selectores para o centro (este tem um resultado de tipo Point retornado por referência
constante) e para o raio, as operações Scale, Translate e Rotate, e ainda Write e
WriteLine, análogas às da classe Point. Mas repare: rodar um círculo não é rodá-lo em
torno do seu centro, que deixaria tudo na mesma, mas sim rodá-lo em torno da origem.
Também para escalar um círculo, só damos um factor de escala, e não dois como no caso
da classe Point. De facto, um ponto pode ser escalado diferentemente segundo os dois
eixos, mas um círculo não. Se o fizéssemos deixávamos de ter um círculo, não era?
Finalmente escalar um círculo não é apenas multiplicar o raio por esse factor de escala.
Em geral, escalar uma figura, é aplicar o factor de escala (ou os factores de escala, se
houver um factor de escala para cada direcção) a cada um dos pontos da figura.
Avance pausadamente. Primeiro declare a classe só com os membros de dados, os
construtores e o destrutor, no ficheiro Circle.h. Depois, no ficheiro Circle.cpp, primeiro
meta só o #include “Circle.h”. Compile.
Agora defina os construtores e o destrutor no fiche iro Circle.cpp. Como é que se
inicializa o centro, no construtor por defeito e no construtor elementar? Se ainda não
sabe, procure um exemplo do género, na sebenta ou no livro.
A seguir, declare e defina os selectores para o centro e para o raio. O do raio não tem
novidade: não tem argumentos e devolve um double. O do centro também não tem
argumentos mas devolve um ponto, isto é, um objecto de tipo Point. Ora Point é um tipoclasse. Normalmente quando uma função devolve um objecto de um tipo-classe que
2
17.
18.
19.
20.
21.
22.
existe em memória como “parte” do objecto, devolve-o por referência constante. Se este
assunto é novidade para si, procure mais informação sobre ele na sebenta ou no livro.
Acrescente as declarações das funções Write e WriteLine, por analogia com as da classe
Point. Defina-as. Quando estiver satisfeito com o seu trabalho, e a sua classe compilar
sem erros, passe ao ponto seguinte.
Observe os ficheiros .h fornecidos: a declaração da classe vem sempre entre as directivas
#ifndef, #define (antes) e #endif (depois). Este esquema serve para evitar inclusões
repetidas, isto é, para evitar que um ficheiro .h seja incluído mais que uma vez (o que dá
erro). Apesar de não ser necessário nestes exemplos simples, pois não há inclusões
repetidas (a não ser por distracção) é um bom hábito proteger as declarações das classes
com estas directivas, sistematicamente. Faça isso para a sua classe Circle, usando a
constante simbólica _H_Circle.
Ao ficheiro M_Circle.cpp, que apenas tem os includes e uma função main vazia,
acrescente uma função de teste TestCircle pare experimentar os construtores e os
selectores básicos. Tudo bem?
Declare e defina agora as três funções Scale, Translate e Rotate. Não é muito difícil, pois
não?
Volte ao ficheiro M_Circle.cpp, e acrescente uma função de teste TestCircleFunctions
para experimentar as funções Scale, Translate e Rotate. Por exemplo, faça a sua função
ler o centro do círculo e o raio, e depois mova o círculo algumas unidades para a direita e
para cima, aumente o tamanho para o dobro e rode-o de -45 graus em torno da origem.
Escreva uma terceira função de teste para construir virtualmente o seguinte desenho, para
um dado número de círculos, começando com um círculo área 1, sabendo que as áreas
dos sucessivos círculos vão duplicando.
23. Acrescente à classe Circle os selectores Area e Perimeter, para calcular a área e o
perímetro. Escreva uma função de teste, TestAreaPerimeter, para experimentar.
Modifique a função do passo anterior para que ela acrescente o perímetro e a área de
cada um dos círculos mostrados na consola.
24. Acrescente à classe Circle a função Equal e os operadores == e !=, como na classe Point.
Um círculo é igual a outro se o centro for igual e o raio for igual. Escreve uma função de
teste, TestEquality, para experimentar.
25. Acrescente uma função booleana Contains com um argumento de tipo Point que dê true
se o ponto pertencer ao círculo, isto é, se estiver sobre a circunferência ou no interior da
circunferência. Teste.
26. Acrescente outra função Contains agora com um argumento de tipo Circle. Esta função
dá true se o argumento estiver “coberto” pelo objecto. Teste.
3
27. Acrescente uma função booleana Intersects com um argumento de tipo Circle. A função
dá true se o objecto tiver pelo menos um ponto em comum com o argumento. Teste.
28. Acrescente uma função de RightMost, com um resultado de tipo Point (retornado por
valor). A função calcula e devolve o ponto do círculo mais à direita. Por exemplo, no
círculo centrado na origem e raio 1, o ponto mais à direita é o ponto de coordenadas <1,
0>. Teste. “Já agora”, acrescente três funções análogas, LeftMost, TopMost, e
BottomMost, que dão os pontos mais à esquerda, mais acima e mais abaixo. Teste de
novo. Repare que estas funções têm que devolver o resultado por valor, porque os pontos
calculados não existiam em memória como componentes do objecto da função. Por isso
têm que ser construídos no seio da função chamada e “copiados” para o contexto da
função chamadora. No caso da função Center, já programada, o resultado era um
componente do objecto, pré-existente, e a função não precisa duplicar esse objecto: basta
devolver uma referência para ele.
29. Acrescente uma função PointAt, com um parâmetro de tipo double representando um
ângulo, e um resultado de tipo Point retornado por valor, para calcular o ponto sobre a
circunferência que se encontra à distância angular dada do ponto do círculo mais à
direita, no sentido contrário ao dos ponteiros do relógio.
30. Acrescente uma função Angle com um parâmetro de tipo Point, para calcular o ângulo
(em radianos) formado pela semi-recta horizontal do centro para a direita com a semirecta do centro para o argumento.
31. Programe agora uma função Plot, que escreva num ficheiro de texto n pontos
equidistantes ao longo da circunferência do círculo. Em cada linha vem um valor de x e
um valor de y. A declaração será assim:
void Plot(std::ostream& output,
int countPoints) const;
O primeiro argumento representa o ficheiro e o segundo o número de pontos. Baseie -se
na função PointAt e na função WriteLine da classe Point.
32. Escreva uma função de teste para a função Plot.
33. (Extra) Importe o ficheiro resultante do teste do ponto anterior para o Excel e desenhe o
gráfico da circunferência.
34. Acrescente quatro funções SectorArea, SegmentArea, ArcLength e ChordLength, com
um parâmetro de tipo double representando um ângulo, para calcular a área do sector
circular, a área do segmento circular, o comprimento do arco de circunferência, e o
comprimento da corda, definidos pelo ângulo dado. Teste. (Este exercício exige alguma
trigonometria, mas nada demais. O próximo também.)
35. Acrescente uma função ChordAngle que calcula o ângulo correspondente à corda cujo
comprimento é dado no argumento. Teste.
36. Acrescente uma função IntersectionArea, com um parâmetro de tipo Circle, para calcular
a área da intersecção do objecto com o argumento. Quando tiver esta função pronta,
copia-a para uma mensagem de correio electrónico e envie -a com os seus cumprimentos
para o seu assistente. Ele vai gostar. E você? Gostou?
4
Download

Inunciado