Escolar Documentos
Profissional Documentos
Cultura Documentos
parte elementos)
Genealogia
Fortran Algol 60 CPL
por Pedro Guerreiro Curso preparado especialmente para a cadeira de Linguagens de Programao da licenciatura em engenharia informtica da FCT/UNL Maio de 1994 Pascal
BCPL Algol 68
Simula 67
C++
C++
Apontamento histrico
C++ C++ Desenvolvida Desenvolvidanos nosLaboratrios LaboratriosBell Bell(AT&T) (AT&T)por por Bjarne Stroustrup, a partir de 1980, at Bjarne Stroustrup, a partir de 1980, at1986. 1986. Aperfeioada Aperfeioadaprogressivamente. progressivamente.Actualmente, Actualmente, em vias de normalizao em vias de normalizao(ANSI (ANSIe eISO). ISO). Baseia-se no C (que se baseava no BCPL), e no Simula67 (e ainda no Ada, no Clu e no ML). Do C vem tudo: a sintaxe, o estilo, o sabor, Do Simula67 vm as classes. Do Ada vm as templates (tambm do Clu) e as excepes (tambm do Clu e do ML). O OC++ C++comeou comeoupor porser serapenas apenasum um C com classes. C com classes. O nome C++ foi inventado em 1983, e inspira-se no operador ++ do C, pretendendo sugerir a natureza evolucionria da linguagem. Segundo Stroustrup, o C++ no se chama D, porque quer ser um extenso do C, sem tentar resolver dificuldades do C, eliminando os defeitos desta linguagem. 3 C++
Objectivos do C++
ser um C melhor. suportar a abstraco de dados suportar a programao orientada pelos objectos.
Stroustrup: C++ C++was wasdesigned designedprimarily primarilyso sothat thatthe theauthor author and his friends would not have to program and his friends would not have to programin in assembler, assembler,C, C,or orvarious variousmodern modernhigh-level high-level languages languages C++ C++was wasdesigned designedto toenable enablelarger largerprograms programsto to be structured in a rational way so that be structured in a rational way so thatit itwould would not notbe beunreasonable unreasonablefor fora asingle singleperson personto tocope cope with 25000 lines of code with 25000 lines of code The Theuse useof ofC++ C++for forlarger largerprograms programsleads leadsto tothe the use of C++ by groups of programmers. C++ use of C++ by groups of programmers. C++ emphasis emphasison onmodularity, modularity,strongly stronglytyped typedinterinterfaces, and flexibility pays off here. However, faces, and flexibility pays off here. However,as as programs programsget getlarger, larger,the theproblems problemsassociated associated with withtheir theirdevelopment developmentand andmaintenance maintenanceshift shift from being language problems to more global from being language problems to more global problems problemsof oftools toolsand andmanagement. management.
C++
C++ e C
O C++ foi desenvolvido a partir do C. O C , com poucas excepes, um subconjunto do C++. Ateno: quanto melhor se conhece o C, mais difcil parece ser escrever C++ sem ser moda do C O C++ tem os mesmos mecanismos lingusticos bsicos que o C (funes, variveis, expresses, instrues, vectores, apontadores, estruturas, input/output, bibliotecas standard). Ento, o que que o C++ tem que o C no tinha? Tem classes (e conceitos associados: herana, funes virtuais, classes derivadas, etc.) E tambm umas coisitas para melhorar o C: por exemplo, o operador new, converso de tipos, referncias, funes inline, etc.
C++
1. The C++ Programming Language, Bjarne Stroustrup (2 ed.), Addison-Wesley, 1991. 2. The Annotaded C++ Reference Manual, Margaret Ellis e Bjarne Stroustrup, AddisonWesley, 1990. 3. C++ Primer, 2 edio, Stanley Lippman, Addison-Wesley, 1993. 4. Programming in C++, Stephen Dewhurst e Kathy Stark, Prentice Hall, 1989. 5. An Introduction to Object-Oriented Programming and C++, Richard Wiener e Lewis Pinson, Addison-Wesley, 1988. 6. Effective C++, Scott Myers, Addison-Wesley, 1992. 7. C++ Programming Style, Tom Cargill, Addison-Wesley, 1992. 8. Data Abstraction and Object-Oriented Programming in C++, Keith Gorlen, Sanford Orlow, Perry Plexico, John Wiley and Sons, 1990. 9. The C Programming Language (ANSI C), 2. edio, Brian Kernighan e Dennis Ritchie, Prentice Hall (1988). 10. Elementos de Programao com C, ..., Europa-Amrica, 1991. ... 6 C++
Revistas, etc.
1. Journal of Object-Oriented Programming 2. Object 3. C++ Report ... Conferncias cientficas: 1. OOPSLA 2. ECOOP 3. TOOLS ... Conferncias profissionais, exposies, etc.: 1. C++ World 2. Object Expo 3. Object Technology 4. Object Oriented Portugal ...
C++
C++
void void Point::Set(int Point::Set(int newX, newX, int int newY) newY) { { Point:: xx xx = = newX; newX; o yy yy = = newY; newY; qualificador } } void void Point::Move(int Point::Move(int dX, dX, int int dY) dY) { { xx xx += += dX; dX; yy += yy += dY; dY; } } double double Point::DistanceTo(Point Point::DistanceTo(Point p) p) { { return return sqrt(sqr(xx-p.xx)+ sqrt(sqr(xx-p.xx)+ sqr(yy-p.yy)); sqr(yy-p.yy)); } } etc.
Construtor
C++
10
Nveis de acesso
Normalmente, na zona pblica s h funes. Os membros que so valores ficam na zona privada, e s so acedidos atravs das funes pblicas. Por vezes tambm aparecem funes na zona privada, e essas s podem ser usadas dentro da classe. Em esquema:
class class AccessLevels AccessLevels {{ private: private: int int noAccess; noAccess; int int readOnly; readOnly; int int readWrite; readWrite; int int writeOnly; writeOnly; void void PrivateFunction(void); PrivateFunction(void); public: public: int int GetReadOnly(void) GetReadOnly(void) const; const; void SetReadWrite(int); void SetReadWrite(int); int int GetReadWrite(void) GetReadWrite(void) const; const; void SetWriteOnly(int); void SetWriteOnly(int); }; };
C++
C++
12
Construtores
Normalmente todas as classes tm um (ou vrios) construtores. O construtor chamado sempre que um objecto criado. Um objecto pode ser criado em quatro circunstncias: 1 Quando a sua declarao processada (objectos automticos). 2 No incio do programa (objectos estticos). 3 Por meio do operador new (objectos dinmicos, acedidos por meio de apontadores). 4 Quando criado um objecto maior ou um vector, do qual o objecto faz parte (objectos-membro).
Por vezes, as classes tambm tm destrutores, que so chamados automaticamente quando os objectos so destrudos.
C++
} } 13
C++
14
Exerccios 1
1. Enriquecer a classe Point com funes para reflectir o ponto no eixo dos xx, no eixo dos yy, na diagonal principal, para rodar o ponto de um certo ngulo, etc. 2. Declarar uma classe Segment, representando segmentos de recta definidos por dois pontos, com funes para o comprimento do segmento, para calcular o ponto mdio, mover o segmento, esticar o segmento, etc. 3. Programar uma classe Triangle, com funes para calcular a rea, mover, etc.
A rea de um tringulo dada pela seguinte expresso: s * (s - a) * (s - b) * (s - c) onde s o semipermetro e a, b, e c so as medidas dos lados.
}}
16
Modularizao
class Point { ... } #include "point.hpp" Point::Point(void) { ... } void Point::Set(... ... #include "point.hpp" class Triangle { ... } #include "triangle.hpp" Triangle::Triangle(void) { ... } double Triangle::Area(... C++ ...
Ficheiros de headers
Os ficheiros de headers so usados, como no C, para garantir a coerncia do cdigo, de uma unidade de compilao para outra. Exemplo:
class class Simple Simple {{ private: private: int int value; value; public: public: int int Get(void) Get(void) const; const; void void Set(int); Set(int); }; }; extern extern double double CubeRoot(double); CubeRoot(double); inline inline int int Max(int Max(int x, x, int int y) y) {{ return x <= y ? y : x; return x <= y ? y : x; }} extern extern int int dimension; dimension; const const double double pi pi == 3.141592653; 3.141592653; enum enum Bool Bool {false, {false, true}; true}; class class Matrix; Matrix; #include #include <string.h> <string.h> #define forever #define forever for(;;) for(;;) /* /* Isto Isto e e um um exemplo exemplo */ */ C++ Definio de tipo (classe, neste caso) Declarao de funo Definio de funo inline Declarao de varivel Declarao de constante Enumeraes Declarao de nome Directivas #include Macros Comentrios
Ficheiro point.hpp
Ficheiro point.cpp
ficheiros de header
Ficheiro triangle.hpp
ficheiros com definies
Ficheiro triangle.cpp
Nota: as extenses usadas variam de sistema para sistema
17
18
19
C++
20
Problema do contador
O problema do contador: Escrever um programa para simular o comportamento de um contador. Um contador um dispositivo que tem um mostrador onde aparece um nmero, e trs botes, um para incrementar, outro para decrementar, e o terceiro para fazer o contador voltar ao princpio.
Classe Count
Que se faz com um contador? Incrementa-se, decrementa-se, reinicializase
class class Count Count {{ private: private: int int value; value; int int resetValue; resetValue; public: public: Count(void); Count(void); void void Init(int); Init(int); void void Inc(void); Inc(void); void void Dec(void); Dec(void); void void Reset(void); Reset(void); int int Value(void); Value(void); }; };
Que classes h nisto? Contador Contador Mostrador Mostrador(?) (?) Boto Boto(?) (?)
21
C++
22
Contador, verso 1
#include #include <stdio.h> <stdio.h> #include "count.hpp" #include "count.hpp" int main main () () {int { Count count; Count count; int == 0; int over over 0; char line[80]; char line[80]; count.Init(100); count.Init(100); for (;;) for {{ (;;) printf("Value: %d\n", printf("Value: %d\n", count.Value()); count.Value()); printf("1. Increment\n2. Decrement\n" printf("1. Increment\n2. Decrement\n" "3. Reset\n4. Quit\n\n"); "3. Reset\n4. Quit\n\n"); gets(line); gets(line); switch(*line){ switch(*line){ case '1': case '1': count.Inc(); count.Inc(); break; break; case '2': case '2': count.Dec(); count.Dec(); break; break; case '3': case '3': count.Reset(); count.Reset(); break; break; case '4': case '4': over == 1; over 1; break; break; default: default: break; }} break; if (over) if (over) break; break; }} return 0; }} return 0;
OK?
23
C++
24
Menus
Para ter uma interface fcil, vamos implementar os trs botes atravs de um menu. O menu ter um quarto comando para terminar o programa. Para ser simples, ser seleccionado o primeiro comando que comear pelo carcter que for teclado. Se no houver nenhum comando nessas condies, nenhum comando seleccionado. Cada menu ter uma etiqueta, que o identifica, e um pronto, para indicar que est espera do utilizador. Ser que os menus tambm so uma classe?
Classe Menu
Os menus tambm so uma classe, claro. O que se faz com um menu? Definem-se comandos, mostra-se o menu, e deixa-se seleccionar um comando:
const const int int MAXITEMS MAXITEMS == 10; 10; No , com certeza, class class Menu Menu {{ a implementao private: private: mais eficiente char label[32]; char label[32]; char char items[MAXITEMS] items[MAXITEMS] [32]; [32]; int size; int size; char char prompt[32]; prompt[32]; public: public: Menu(void); Menu(void); void void Init(char Init(char *, *, char char *); *); Devolve o nmero do void AddItem(char *); void AddItem(char *); comando seleccionado, void Select(int *); void Select(int *); ou 0, se nenhum for void Display(void); void Display(void); seleccionado. }; };
C++
C++
Contador, verso 2
void DoCommand(int #include DoCommand(int n) n) #include <stdio.h> <stdio.h> {void { switch(n){ #include switch(n){ #include "menu.hpp" "menu.hpp" case 1: #include case 1: #include "count.hpp" "count.hpp" count.Inc(); count.Inc(); break; void DoCommand(int); break; void DoCommand(int); case 2: int case 2: int over over == 0; 0; count.Dec(); Funo auxiliar count.Dec(); break; Menu break; Menu menu; menu; case 3: Count case 3: Count count; count; count.Reset(); count.Reset(); break; int main break; main () () case 5: {int case 5: { int command; over=1; over=1; int command; break; break; default: menu.Init("Contador decimal", default: menu.Init("Contador decimal", break; "Escolha, por break; "Escolha, por favor: favor: "); "); }} menu.AddItem("1. Incrementar"); menu.AddItem("1. Incrementar"); }} menu.AddItem("2. menu.AddItem("2. Decrementar"); Decrementar"); menu.AddItem("3. Reinicializar"); menu.AddItem("3. Reinicializar"); menu.AddItem(""); menu.AddItem(""); menu.AddItem("4. menu.AddItem("4. Terminar"); Terminar"); count.Init(88); count.Init(88); for(;;) {for(;;) { printf("Current value: %d\n", printf("Current value: %d\n", count.Value()); count.Value()); menu.Display(); menu.Display(); menu.Select(&command); menu.Select(&command); DoCommand(command); DoCommand(command); if(over) if(over) break; break; }} return 0; }} return 0;
27
C++
28
Classe DisplayBox
O mostrador onde aparece o valor numrico, tambm merece ser uma classe:
class class DisplayBox DisplayBox {{ private: private: char char message[32]; message[32]; public: public: DisplayBox(void); DisplayBox(void); void void Init(char Init(char *); *); void Display(int); void Display(int); }; }; #include #include "dbox.hpp" "dbox.hpp" #include #include <string.h> <string.h> #include #include <stdio.h> <stdio.h> DisplayBox::DisplayBox(void) DisplayBox::DisplayBox(void) {{ *message *message == 0; 0; }} void void DisplayBox::Init(char DisplayBox::Init(char *s) *s) {{ strcpy(message, strcpy(message, s); s); }} void void DisplayBox::Display(int DisplayBox::Display(int n) n) {{ printf("%s%d\n", message, n); printf("%s%d\n", message, n); }} C++ A mensagem representa a parte fixa do mostrador.
Contador, verso 3
#include #include "menu.hpp" "menu.hpp" #include Toda a interaco #include "count.hpp" "count.hpp" #include "dbox.hpp" gerida pelas classes. #include "dbox.hpp" (No aparece aqui void DoCommand(int); void DoCommand(int); #include <stdio.h>) int int over over == 0; 0; Menu Menu menu; menu; Count Count count; count; DisplayBox DisplayBox displayBox; displayBox; int main () main () {int { int command; int command; displayBox.Init("Valor: displayBox.Init("Valor: "); "); menu.Init("Contador decimal", menu.Init("Contador decimal", "Escolha, por "Escolha, por favor: favor: "); "); menu.AddItem("1. Incrementar"); menu.AddItem("1. Incrementar"); menu.AddItem("2. Decrementar"); menu.AddItem("2. Reinicializar"); Decrementar"); menu.AddItem("3. menu.AddItem("3. Reinicializar"); menu.AddItem(""); menu.AddItem(""); menu.AddItem("4. Terminar"); menu.AddItem("4. Terminar"); count.Init(77); count.Init(77); for(;;) {for(;;) { displayBox.Display(count.Value()); displayBox.Display(count.Value()); menu.Display(); menu.Display(); menu.Select(&command); menu.Select(&command); DoCommand(command); DoCommand(command); if(over) if(over) break; break; }} return return 0; 0;
29
30
Classes derivadas
Em vez de repetirmos os membros todos, herdamo-los, derivando da classe Menu uma nova classe CountMenu:
#include #include "menu.hpp" "menu.hpp" #include #include "count.hpp" "count.hpp" class class CountMenu: CountMenu: Menu Menu {{ private: private: Count Count itsCount; itsCount; int int quit; quit; void void DoCommand(int); DoCommand(int); public: public: CountMenu(void); CountMenu(void); void void Init(void); Init(void); void void DoMenu(void); DoMenu(void); int int Quit(void); Quit(void); }; };
CountMenu uma subclasse, uma especializao, de Menu; Menu a sobreclasse (superclass), a generalizao, de CountMenu; CountMenu a classe derivada, Menu a classe de base. CountMenu -um (is-a) Menu;
C++ Mais sobre isto, mais frente
32
Controlo de acesso
1. Os membros da classe derivada podem usar os nome pblicos da classe de base, como se fossem seus. Exemplos:
void void ColoredPoint::MoveRight(int ColoredPoint::MoveRight(int dx) dx) {{ Move(dx, Move(dx, 0); 0); }} ColoredPoint::Display(void) ColoredPoint::Display(void) {{ Point::Display(); Point::Display(); printf("The printf("The color color is: is: %d", %d", color); color); }}
2. Os membros da classe derivada no tm acesso aos membros privados da classe de base. Isto estaria errado:
void void ColoredPoint::MoveRight(int ColoredPoint::MoveRight(int dx) dx) {{ xx xx += += dx; dx; }}
33
Regra: Regra: a aclasse classederivada derivadausa usas sos osmembros membros pblicos da classe de base. pblicos da classe de base. 34 C++
A resposta NO, porque Point , por defeito, uma classe de base privada:
class class ColoredPoint: ColoredPoint: private private Point Point {{ private: private: ... ... public: public: ... ... }; };
Se quisermos mover pontos coloridos, ento devemos tornar pblica a classe de base:
class class ColoredPoint: ColoredPoint: public public Point Point {{ private: private: ... ... public: public: ... ... }; }; C++
Se fizssemos cp = p; o erro seria No suitable constructor or conversion function exists for conversion from "Point" to "ColoredPoint". Se a derivao fosse private a afectao p = cp; estaria errada e o erro seria "Point" is a private base class of "ColoredPoint".
35
}} C++
36
Classe Color
E se houvesse uma classe Color?
typedef typedef unsigned unsigned short short int int ushort; ushort; class class Color Color {{ private: private: ushort ushort red; red; ushort ushort green; green; ushort ushort blue; blue; public: public: Color(void); Color(void); void void Paint(ushort, Paint(ushort, ushort, ushort, ushort); ushort); ushort Red(void); ushort Red(void); ushort ushort Green(void); Green(void); ushort ushort Blue(void); Blue(void); }; };
Herana mltipla
fcil:
class class ColoredPoint: ColoredPoint: public public Point, Point, public public Color Color {{ private: private: public: public: ColoredPoint(void); ColoredPoint(void); void void Display(); Display(); }; };
Programa-se normalmente:
void void ColoredPoint::Display() ColoredPoint::Display() {{ Point::Display(); Point::Display(); printf("<%d,%d,%d>\n", printf("<%d,%d,%d>\n", Red(), Red(), Green(), Green(), Blue()); Blue()); }}
Ento, um ponto colorido um ponto com cor, ou uma cor com ponto? Se calhar, um ponto colorido um ponto mais uma cor
Utiliza-se normalmente:
int int main() main() {{ ColoredPoint ColoredPoint cp; cp; cp.Set(4, cp.Set(4, 6); 6); cp.Paint(100, cp.Paint(100, 200, 200, 300); 300); cp.Display(); cp.Display(); ... ... }}
C++
37
C++
38
Classe CountDisplayBox
Uma DisplayBox com contador pode ser construda por derivao, acrescentando um membro privado para o contador:
class class CountDisplayBox: CountDisplayBox: public public DisplayBox DisplayBox {{ private: private: Count Count itsCount; itsCount; public: public: CountDisplayBox(void); CountDisplayBox(void); void void Init(void); Init(void); void void Display(void); Display(void); }; };
39
40
Classe CountDisplayBox
O campo itsCount passa a ser um apontador para a classe Count:
class class CountDisplayBox: CountDisplayBox: public public DisplayBox DisplayBox {{ private: private: Apontador, Count Count *itsCount; *itsCount; privado public: public: CountDisplayBox(void); CountDisplayBox(void); void void Init(Count Init(Count *); *); void void Display(void); Display(void); }; };
No construtor, coloca-se o apontador a zero. Na inicializao, copia-se para l um apontador (que no deve ser zero):
Em C++ costuCountDisplayBox::CountDisplayBox(void) CountDisplayBox::CountDisplayBox(void) me usar o 0 {{ itsCount = 0; (zero) para reitsCount = 0; }} presentar o apontador nulo. void void CountDisplayBox::Init(Count CountDisplayBox::Init(Count *x) *x) {{ itsCount Apontadores itsCount == x; x; DisplayBox::Init("Valor: DisplayBox::Init("Valor: "); "); }}
C++
41
void void CountDisplayBox::Display(void) CountDisplayBox::Display(void) {{ DisplayBox::Display(); DisplayBox::Display(); printf("%d\n", printf("%d\n", itsCount->Value()); itsCount->Value()); }} C++
42
Classe CountMenu
Com a classe CountMenu faz-se a mesma coisa:
class class CountMenu: CountMenu: public public Menu Menu {{ private: private: Count Count *itsCount; *itsCount; int int quit; quit; void void DoCommand(int); DoCommand(int); public: public: CountMenu(void); CountMenu(void); void void Init(Count Init(Count *); *); void DoMenu(void); void DoMenu(void); int int Quit(void); Quit(void); }; };
void void CountMenu::Init(Count CountMenu::Init(Count *x) *x) {{ itsCount itsCount == x; x; Menu::Init("Contador Menu::Init("Contador decimal", decimal", "Escolha, "Escolha, por por favor: favor: "); "); AddItem(...); AddItem(...); ... ... }} C++
43
C++
44
O apontador this
No mbito de cada objecto existe o apontador this, que aponta para o prprio objecto. Entre outras coisas, this serve para tornar as coisas mais object-oriented: this->itsCount
Contador, verso 4
Com esta infra-estrutura, a funo main limita- se a declarar os objectos, inicializ-los e lanar o ciclo principal:
int int main main () () {{ CountMenu CountMenu menu; menu; Declaraes Count Count count; count; CountDisplayBox CountDisplayBox displayBox; displayBox; Inicializaes Assim se mete o count.Init(0); count.Init(0); mesmo contador displayBox.Init(&count); displayBox.Init(&count); dentro de dois menu.Init(&count); menu.Init(&count); objectos while(!menu.Quit()) while(!menu.Quit()) {{ displayBox.Display(); displayBox.Display(); menu.DoMenu(); menu.DoMenu(); }} printf("Muito printf("Muito obrigado.\n"); obrigado.\n"); return return 0; 0;
... ... o mesmo que void CountMenu::Init(Count *x) itsCount s. CountMenu::Init(Count *x) {void { this->itsCount = x; this->itsCount = x; decimal", Menu::Init("Contador Menu::Init("Contador decimal", "); "Escolha, por "Escolha, por favor: favor: "); Menu::AddItem("1. Incrementar"); Menu::AddItem("1. Decrementar"); Incrementar"); Menu::AddItem("2. Menu::AddItem("2. Decrementar"); ... }} ... void CountMenu::DoMenu(void) CountMenu::DoMenu(void) Aqui, this->Display(), {void { int command; etc., reala a natureza int command; OO da programao! this->Display(); this->Display(); this->Select(&command); this->Select(&command); this->DoCommand(command); }} this->DoCommand(command); void CountMenu::DoCommand(int CountMenu::DoCommand(int n) n) {void { switch(n){ switch(n){ case 1: case 1: this->itsCount->Inc(); this->itsCount->Inc(); break; break; case 2: this->itsCount->Inc() case 2: ... ... } }} }
Ciclo principal
Finalizao }}
Cuidado, que o programa j tem cinco classes: Count, Menu, CountMenu, DisplayBox, e CountDisplayBox.
C++
45
46
Estrutura do programa
O programa tem uma estrutura rica, que apreciamos melhor com ajuda de um desenho:
DisplayBox Count
Menu
CountDisplayBox
CountMenu
A A
C++
B B
B uma classe derivada de A (B is-a A). B conhece A, ie, tem um membro que um apontador para A (B knows A). 47
A funo Initiate conter o cdigo de inicializao. A seguir inicializao vem um ciclo: em cada passo do ciclo chama-se a funo Operate; o ciclo termina quando a funo Continue devolver zero. No fim, chama-se a funo Terminate. A misso da funo Run, a nica que pblica, encadear as outras todas. 48 C++
Funes virtuais
Se as classes derivadas no fornecerem definies que ultrapassem as das funes virtuais, usam-se essas mesmo:
void void Application::Initiate(void) Application::Initiate(void) {{ }} int int Application::Continue(void) Application::Continue(void) {{ return return 0; 0; }} void void Application::Operate(void) Application::Operate(void) {{ }} void void Application::Terminate(void) Application::Terminate(void) {{ }} void void Application::Run(void) Application::Run(void) {{ this->Initiate(); this->Initiate(); while(this->Continue()) while(this->Continue()) this->Operate(); this->Operate(); this->Terminate(); this->Terminate(); }} C++ Esta j tem significado, mas pode ser ultrapassada nas classes derivadas.
O especificador virtual significa que se espera que classes derivadas desta forneam implementaes alternativas para as funes. A classe Application abstracta, pois normalmente no haver objectos desta classe. Ela s serve para ser herdada por outras, mais especializadas.
C++
49
50
Classe Application
A classe Application fica assim: Ficheiro appl.hpp
class Application { private: virtual void Initiate(void) = 0; virtual int Continue(void) = 0; virtual void Operate(void) = 0; virtual void Terminate(void) = 0; public: Application(void); virtual void Run(void); };
Ficheiro appl.cpp
Application::Application(void) { } void Application::Run(void) { this->Initiate(); while(this->Continue()) this->Operate(); this->Terminate(); } Se, por engano, declararmos um objecto de tipo Application, a mensagem de erro : An object of abstract class "Application" cannot be created.
Uma classe que tenha funes virtuais puras mesmo abstracta, e o compilador impedir que haja objectos dessa classe.
Nota: muitas vezes, ao contrrio do que acontece neste exemplo, as funes virtuais puras so pblicas. C++
51
C++
52
class class CountApplication: CountApplication: public public Application Application {{ private: private: As variveis da Count funo main Count itsCount; itsCount; CountMenu aparecem como CountMenu itsMenu; itsMenu; CountDisplayBox membros da classe. CountDisplayBox itsDisplayBox; itsDisplayBox; void Initiate(void); void Initiate(void); int int Continue(void); Continue(void); void void Operate(void); Operate(void); void void Terminate(void); Terminate(void); public: public: CountApplication(void); CountApplication(void); }; };
A derivao pblica para os objectos desta classe poderem ir buscar a funo Run classe de base.
C++
53
C++
Quando a funo Run for chamada para um objecto da classe CountApplication as funes invocadas (Initiate, Continue, Operate, Terminate) so estas e no as da classe Application (onde se encontra a definio da funo Run).
54
Contador, verso 5
A funo main agora limita-se a declarar um objecto da classe CountApplication e a fazerlhe Run:
#include #include "cappl.hpp" "cappl.hpp" int int main(void) main(void) {{ CountApplication CountApplication app; app; app.Run(); app.Run(); return return 0; 0;
CountDisplayBox
CountMenu
}}
CountApplication O programa tem sete classes (Count, Menu, CountMenu, DisplayBox, CountDisplayBox, Application, CountApplication), cada uma com dois ficheiros (.hpp e .cpp). Comea a ficar difcil ter tudo na cabea.
C++
A
C++
55
Compilao condicional.
As funes declaradas desta maneira so implicitamente em-linha (inline), isto , o compilador substitui o corpo da funo pela chamada no cdigo gerado, assim evitando o overhead devido chamada da funo. Para definir explicitamente uma funo inline, coloca-se inline antes. 57
C++
58
Exerccios 2
1. Modificar o programa do contador de maneira a que o mostrador mostre os nmeros em hexadecimal. Comece por generalizar o mostrador, tornando-o um mostrador alfanumrico, e no decimal. Declare uma classe HexaCount, derivada de Count, com uma funo que devolva o numeral hexadecimal (uma cadeia), etc. 2. Idem, mas para contar com datas. Comece por declarar uma classe Date, com operaes para avanar e recuar um dia, entre outras. 3. Definir uma classe Time para representar as horas do dia, expressas em horas e minutos. Defina uma classe TimeTable cujos objectos so vectores de pares de objectos Time, e que serve para representar, por exemplo, a hora de partida e a hora de chegada dos comboios de Lisboa para o Porto.
Neste caso, em que todas as funes so definidas junto com a classe, no h ficheiro .cpp.
C++
59
C++
60
Classe Complex
Regressemos s coisas simples. O exemplo clssico da classe dos nmeros complexos:
class class Complex Complex {{ Trs construtores, private: private: definidos logo. double double re; re; double double im; im; public: public: Complex(void){re Complex(void){re == im im == 0.0;} 0.0;} Complex(double Complex(double r){re r){re == r;im r;im == 0.0;} 0.0;} Complex(double Complex(double r, r, double double i){re i){re == r; r; im im == i;} i;} void void Print(void); Print(void); void void Print(FILE Print(FILE *); *); double double Real(void){return Real(void){return re;} re;} double Imaginary(void){return double Imaginary(void){return im;} im;} Complex Complex operator operator (void); (void); Operador unrio
Construtores parametrizados
Os construtores podem ter parmetros. Exemplo:
class class Complex Complex {{ private: private: ... ... public: public: ... ... Complex(double Complex(double r, r, double double i){re i){re == r; r; im im == i;} i;} ... ... }; };
Utilizao:
... ... Complex Complex zz (3.0, (3.0, 4.0); 4.0); const Complex const Complex jj (0.0, (0.0, 1.0); 1.0); ... ... z uma varivel complexa inicializada com 3+4i; j uma constante complexa, que vale i.
}; };
friend friend Complex Complex operator operator ++ (Complex, (Complex, Complex); Complex); friend Complex operator (Complex, friend Complex operator (Complex, Complex); Complex); friend friend Complex Complex operator operator ** (Complex, (Complex, Complex); Complex); friend Complex operator / (Complex, friend Complex operator / (Complex, Complex); Complex);
seria equivalente a: 61
Complex Complex ww (0.0, (0.0, 0.0); 0.0); C++
62
Sobrecarga de funes
Pode haver numa classe vrias funes com o mesmo nome. Nesse caso, o tipo dos argumentos deve ser suficiente para as distinguir. o caso dos construtores na classe Complex:
class class Complex Complex {{ private: private: ... ... public: public: Complex(void){re Complex(void){re == im im == 0.0;} 0.0;} Complex(double r){re Complex(double r){re == r;im r;im == 0.0;} 0.0;} Complex(double Complex(double r, r, double double i){re i){re == r; r; im im == i;} i;} ... ... }; };
Utilizao:
... ... Complex Complex zz (3.0, (3.0, 4.0); 4.0); const Complex const Complex jj (0.0, (0.0, 1.0); 1.0); Complex Complex xx (1.0); (1.0); Aqui, h uma Complex Complex w; w; converso implcita de Complex a (12); Complex a (12); int para double, antes ... ... de ser escolhido o segundo construtor. C++ O C++ forte em converses implcitas
Esta facilidade pode ser usada tambm em funes normais. Por exemplo, eis a declarao de uma funo que escreve nmero inteiros em qualquer base, sendo 10 a base por defeito:
void void PrintInt(int PrintInt(int value, value, int int base base == 10); 10); PrintInt(31) PrintInt(31, 10) PrintInt(31, 8) PrintInt(31, 2) C++ daria 31 daria 31, tambm daria 37 daria 11111 Exerccio: programe esta funo
63
64
Eis as definies:
void void Complex::Print(FILE Complex::Print(FILE *f) *f) {{ fprintf(f, fprintf(f, "%4.2f", "%4.2f", re); re); if (im >=0.0) if (im >=0.0) fprintf(f, fprintf(f, "+"); "+"); else else fprintf(f, fprintf(f, "-"); "-"); fprintf(f, fprintf(f, "%4.2fi", "%4.2fi", fabs(im)); fabs(im)); }}
Aqui, idem, depois de uma converso de int para double, e da utilizao do segundo construtor, e copia-se para a. C++
65
Se tudo estiver bem feito, o compilavoid void Complex::Print(void) Complex::Print(void) dor h-de {{ descobrir, this->Print(stdout); this->Print(stdout); em cada }} caso, qual das funes utilizar. Por vezes, isso implica fazer converses implcitas nos argumentos, usando contrutores. 66 C++
67
68
C++
Repare-se que um membro for const ento s pode ser inicializado desta forma.
69
C++
Erro: the const Person(int Person(int id, id, char char *s, *s, Date Date t) t) member {{ "idNumber" must idNumber idNumber == id; id; be initialized in strcpy(name, s); strcpy(name, s); the constructer's born born == t; t; initializer list. }} Erro: Assignment to a 70 constant expression is not allowed.
Operadores
A classe Complex apresenta cinco operadores:
class class Complex Complex {{ ... ... Complex Complex operator operator (void); (void); friend friend Complex Complex operator operator ++ (Complex, (Complex, Complex); Complex); friend Complex operator (Complex, friend Complex operator (Complex, Complex); Complex); friend friend Complex Complex operator operator ** (Complex, (Complex, Complex); Complex); friend Complex operator / (Complex, friend Complex operator / (Complex, Complex); Complex);
}; };
71
72
Funes camaradas
As funes camaradas, especificadas friend, no so funes-membro, mas tm acesso parte private da classe. Na classe Complex, por acaso, todas as funes camaradas so operadores:
Complex Complex operator operator ++ (Complex (Complex x, x, Complex Complex y) y) {{ return return Complex(x.re Complex(x.re ++ y.re, y.re, x.im x.im ++ y.im); y.im); }} Complex Complex operator operator (Complex (Complex x, x, Complex Complex y) y) {{ return return Complex(x.re Complex(x.re y.re, y.re, x.im x.im y.im); y.im); }} Complex Complex operator operator ** (Complex (Complex x, x, Complex Complex y) y) {{ return return Complex(x.re Complex(x.re ** y.re y.re x.im x.im ** y.im, y.im, x.re * y.im + x.im x.re * y.im + x.im ** y.re); y.re); }} ... ...
As funes camaradas no esto associadas a nenhum objecto da classe. Por isso nunca so chamadas na forma z.f(). 73 C++
74
Operadores ++ e
Em certas classes faz sentido definir operadores ++ e . Nas datas, por exemplo, ++ significar amanh e ontem:
const const int int days[] days[] == {31, {31, 28, 28, 31, 31, 30, 30, 31, 31, 30, 30, 31, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 31}; class class Date Date {{ private: private: ... ... int int LeapYear(int LeapYear(int y){return y){return yy %% 44 == == 0;} 0;} public: public: Date(int y = 1901, int m = 1, int d = Date(int y = 1901, int m = 1, int d = 1) 1) {year {year == y, y, month month == m; m; day day == d;} d;} verses friend friend int int operator operator <= <= (Date, (Date, Date); Date); Date prefixas: Date operator operator ++(void); ++(void); Date operator (void); ++d, d Date operator (void); Date Date operator operator ++(int) ++(int) {return {return ++*this;} ++*this;} Date Date operator operator (int) (int) {return {return *this;} *this;} ... ... }; }; verses posfixas: d++, d Date Date::operator ++(void) Date Date::operator ++(void) {{ if(day++ if(day++ == == days[month-1] days[month-1] ++ (month (month == == 11 && && LeapYear(year))) LeapYear(year))) {{ day day == 1; 1; if(month++ if(month++ == == 12) 12) {{ Ateno: Janeiro month o ms zero, e Fevemonth == 1; 1; year++; year++; reiro o ms 1 }} }} return return *this; *this; }} C++
Exerccios 3
1. Definir os mtodos que faltam na classe Date e acrescentar operadores +=, -=, , um construtor Date (long) (ex. Date (940218)), uma funo camarada int IsDate(int, int, int) (para ver se trs inteiros compem uma data), etc. 2. Acrescentar classe Person membros father e mother, e funes para trabalhar com esses membros, etc. 3. Acrescentar classe Student um membro vectorial exams, que sirva para registar os exames (nome da cadeira, nota e data), medida que so feitos. 4. Definir uma classe School, que sirva para gerir um conjunto de estudantes que se inscrevem, pagam propinas e vo fazendo exames. 5. Acrescentar operadores razoveis classe Time (apresentada nos Exerccios 2).
75
C++
76
Classe String
Em C++, v-se pouco char *. porque as cadeias de caracteres vm em classes. Que operaes queremos para Strings, alm dos construtores? comprimento, comparao, afectao, acesso aos caracteres, concatenao,
class class String String {{ Apontador para memria dinmica private: private: char *p; char *p; int int size; size; public: Construtores (quatro) public: String(); String(); String(int); String(int); String(char String(char *); *); Construtor de cpia String(const String(const String&); String&); ~String(void); ~String(void); int int Length(void); Length(void); void void Print(void); Print(void); void void PrintLn(void); PrintLn(void); Destrutor Outras funes Igualdade
um objecto (ou vrios) do tipo class indicado e devolve espao para ele class String String {{ ... (ou para o primeiro). ... public: public: String() String() {p {p == new new char; char; size size == 1; 1; *p *p == 0;} 0;} String(int n) {p = new char[size=n]; String(int n) {p = new char[size=n]; *p *p == 0;} 0;} String(char String(char *s) *s) {p {p == strcpy(new strcpy(new char[size char[size == strlen(s)+1],s);} strlen(s)+1],s);} String(const String& String(const String& s) s) {p {p == strcpy(new strcpy(new char[size=s.size], char[size=s.size], s.p);} s.p);} ... ... Operador new para vectores: p = new T[dim] Com um operador assim, deixa de ser preciso recorrer ao malloc
Utilizao:
... ... String String s1; s1; String String s2("abcd"); s2("abcd"); String String s3(8); s3(8); String String s4(s2); s4(s2); String String s5 s5 == s4; s4; ... ...
friend friend int int operator operator == == (const (const String&, String&, const const String&); String&); String& String& operator operator == (const (const String&); String&); Afectao }; }; C++ char& char& operator operator [] [] (int); (int);
77
Indexao
C++
78
Destrutores
A classe String tem um destrutor ~String(). Se uma classe tem destrutor, o destrutor chamado quando um objecto dessa classe destrudo. 1 Os objectos automticosso destrudos sada do bloco. 2 Os objectos estticos so destrudos no fim do programa. 3 Os objectos dinmicos so destrudos por meio do operador delete. 4 Os objectos-membro so destrudos quando so destrudos os objectos de que fazem parte. Operador delete: liberta a memria
class class String String {{ ... ... apontada pelo seu argumento. NB: sem o destrutor, a memria apontada por p continuaria reservada, mesmo depois do objecto ter desaparecido.
Se uma classe aloca memria, melhor que a liberte quando os objectos so destrudos.
C++
80
Comparao
Quantas vezes nos enganamos em C, comparando cadeias com ==, esquecendo-nos que estamos a comparar os apontadores? Em C++ isso no aconteceria:
class class String String {{ ... ... Passagem por referncia, constante
Afectao
Em C++, a afectao entre dois objectos da mesma classe , por defeito, uma cpia membro a membro. No isso que faz falta na classe String, pois queremos duplicar a cadeia em memria dinmica. preciso redefinir o operador =. Aparentemente basta fazer:
String& String& String::operator String::operator == (const (const String& String& s) s) {{ pp == strcpy(new strcpy(new char[size char[size == s.size], s.size], s.p); s.p); return *this; return *this; Passagem por }} referncia, constante (de novo)
friend friend int int operator operator == == (const (const String& String& s1, s1, const const String& String& s2) s2) {return {return strcmp(s1.p, strcmp(s1.p, s2.p) s2.p) == == 0;} 0;} ... ...
Utilizao:
String String s1; s1; String String s2; s2; ... ... if if (s1 (s1 == == s2) s2) ... ... else else ... ... Agora, este operador == a funo camarada da classe String, e no a igualdade de apontadores.
A funo devolve uma referncia para String (e no uma String directamente), para imitar o funcionamento habitual do operador = em C. Utilizao:
String String s1; s1; String String s2("asdf"); s2("asdf"); ... ... s1 s1 == s2; s2; Qual o valor final de s1, s2 e s3? String String s1; s1; String String s2("asdf"); s2("asdf"); String String s3("1234"); s3("1234"); ... ... (s1 (s1 == s2) s2) == s3; s3; ... ... s3 s3 == s2 s2 == s1; s1;
C++
81
C++
82
Afectao, melhor
A definio anterior m, porque no cuida de libertar a memria ocupada pela cadeia do lado esquerdo (o apontador muda e a memria fica inacessvel, mas marcada como ocupada). Devemos libertar a memria, antes de alocar memria nova:
X(const X&)
O construtor de cpia X(const X&) serve para inicializar um objecto da classe X com outro objecto (j inicializado) da mesma classe.
String::String(const String::String(const String& String& s) s) {{ pp == strcpy(new strcpy(new char[size=s.size], char[size=s.size], s.p); s.p); }}
String& String& String::operator String::operator == (const (const String& String& s) s) {{ if(this NB: o operando da esquerda no if(this != != &s) &s) {{ pode estar no inicializado delete p; delete p; pp == strcpy(new strcpy(new char[size char[size == s.size], s.size], s.p); s.p); }} return return *this; *this; }}
Utilizao:
String String s1; s1; String String s2; s2; String String s3; s3; ... ... s1 s1 == "qwert"; "qwert"; s2.operator s2.operator =("zxcvb"); =("zxcvb"); s3.operator s3.operator =(s2); =(s2); C++
Aqui h uma converso implcita de char * para String, usando o terceiro construtor, antes da afectao. Os operadores podiam ser chamados como se fossem funes
era problemtica: a inicializao de s1 feita pelo terceiro construtor, mas a de s2 seria feita por cpia membro a membro. 84
83
C++
NB: Afectao e inicializao so operaes diferentes. No confundir! (Por exemplo, um objecto declarado const pode ser inicializado com outro objecto da mesma classe, mas no pode receber uma afectao.)
C++
85
int int main(void) main(void) {{ String String xx == "lisboa"; "lisboa"; String String y; y; O objecto w (local yy == Paren(x); Paren(x); funo) copiado, y.Print(); y.Print(); inicializando uma }} varivel temporria C++ que depois afectada a y.
86
Quando se diz que um apontador constante, o que que constante? o valor do apontador (um endereo), ou o valor do objecto apontado, ou as duas coisas? Depende:
char char *p *p == "lisboa"; "lisboa"; apondador varivel, objecto apontado varivel apondador varivel, objecto apontado constante apondador constante, objecto apontado varivel
const const char char ** const const pp == "lisboa"; "lisboa"; C++ apondador constante, objecto apontado constante
C++
88
Indexao
Convm reservar o operador [] para a indexao, com o sentido habitual. A indexao devolve uma referncia, para poder ser usada do lado direito ou do lado esquerdo de uma afectao.
class class String String ... ... char& char& operator operator [] [] (int (int i){return i){return p[i];} p[i];} ... ...
Indexao const
O operador [], tal como est, no d para indexar cadeias constantes. Para ter essa funcionalidade, vamos sobrecarreg-lo:
class class String String ... ... const const char& char& operator operator [] [] (int (int i) i) const const {return {return p[i];} p[i];} ... ... Assim se declara que o objecto resultado da funo, uma referncia para char, no pode ser modificado.. Assim se declara que a funo no modifica o objecto.
Com a definio da pgina anterior, isto seria proibido (porque Utilizao: no havia garantia que const const String String s2 s2 == "mirandela"; "mirandela";a funo no char modificava o objecto). char c; c; ... ... cc == s2[5]; s2[5];
Assim no:
s2[5] s2[5] == 'x'; 'x'; C++
89
90
o que, no entanto, era perfeitamente legtimo com a funo camarada, pois actuava uma converso de char * para String no primeiro argumento. 91 C++
Assim no:
char char *p; *p; ... ... pp == s1; s1; Erro: "String" cannot be converted to "char*".
C++
92
Ambiguidade
Ao introduzir o operador char* () introduzimos, sem querer, uma ambiguidade curiosa. O que que significa, por exemplo, a igualdade:
String String s1; s1; ... ... ... ... s1 s1 == == "elvas"... "elvas"...
Exerccios 4
1. Acrescentar um operador + classe String para fazer a concatenao. Prever a variante de acrescentar um carcter. 2. Idem, mas na forma +=. 3. Acrescentar operadores !=, <, <=, >, >=. 4. Acrescentar uma funo para dar a representao decimal de um inteiro, e a inversa desta. 5. Acrescentar um operador tal que s1 s2 a maior subcadeia de s1 que comea igual a s2. Prever a variante em que s2 um carcter. Acrescentar tambm um operador -=. 6. Etc.
Como no haver nenhum operador == entre String e char *, tem que se converter qualquer coisa. Mas o qu? Converte-se a String s1 para char *, ou a cadeia "elvas" para String? No primeiro caso usaramos o operador == da classe String, no segundo o operador == da linguagem, para comparar dois apontadores para char. (Provavelmente queramos a primeira interpretao, mas o compilador no adivinha)
Erro: Call to "operator ==" matches some functions best in some arguments, but no function is a best match for all arguments.
C++
93
C++
94
Streams
Em C, o output e o input no so seguros (type safe). Frequentemente, enganamo-nos no especificador de converso, com consequncias desagradveis. Por exemplo:
int int main main (void) (void) {{ double double x; x; ... ... printf("%d", printf("%d", x); x); ... ...
cout
A stream library define uma stream de output (ostream) chamada cout, por defeito associada ao ecr do terminal (pode ser redirigida). Aquilo que em C se fazia assim:
#include #include <stdio.h> <stdio.h> int int main() main() {{ int int xx == 142; 142; printf("x printf("x == %d\n", %d\n", x); x); }} return return 0; 0;
Em C++, estas desgraas evitam-se fazendo o input e o output atravs das classes ostream e istream, que so type safe. 95
OK?
C++ C++
96
Classe ostream
A instruo
cout cout << << "x "x == "" << << xx << << '\n'; '\n';
cin
A stream complementar de cout, a stream de input (istream) cin, por defeito associada ao teclado (pode ser redirigida). Aquilo que em C se fazia assim:
#include #include <stdio.h> <stdio.h> int int main() main() {{ int int x; x; printf("Qual printf("Qual oo nmero? nmero? "); "); scanf("%d", &x); scanf("%d", &x); printf("O printf("O nmero nmero : : %d\n", %d\n", x); x); return 0; return 0; }}
espectacular, mas no tem nada de especial. O operador << um operador da classe ostream, sobrecarregado para todos os tipos pr-definidos:
O operador << class devolve uma class ostream ostream {{ ... ostream&. ... public: public: ostream& ostream& operator operator << << (const (const char*); char*); ostream& ostream& operator operator << << (char); (char); ostream& ostream& operator operator << << (int); (int); ostream& operator << ostream& operator << (long); (long); ostream& ostream& operator operator << << (double); (double); ostream& ostream& operator operator << << (void*); (void*); ... ... }}
O operador << associa da esquerda para a direita, como convm. A instruo significa:
((cout.operator<<("x ((cout.operator<<("x == ")).operator<<(x)).operator<<('\n'); ")).operator<<(x)).operator<<('\n'); << para char
97
98
Classe istream
A classe istream parecida com a classe ostream:
class class istream istream {{ ... ... public: public: ostream& ostream& operator operator >> >> (ichar*); (ichar*); ostream& operator >> ostream& operator >> (char&); (char&); ostream& ostream& operator operator >> >> (int&); (int&); ostream& ostream& operator operator >> >> (long&); (long&); ostream& operator >> ostream& operator >> (double&); (double&); ... ... }}
Ciclo de leitura
Eis um programa que ecoa para cout os nmeros inteiros, um por linha, que vai lendo de cin. Termina quando aparecer um no-int (talvez o fim do ficheiro).
#include #include <iostream.h> <iostream.h> int int main() main() {{ int int x; x; while while (cin (cin >> >> x) x) cout << x cout << x << << endl; endl; }} return return 0; 0;
Todas estas funes saltam espaos em branco antes de apanharem o que tm para ler. Em esquema:
istream& istream& istream::operator istream::operator >> >> (T& (T& t) t) {{ // // saltar saltar oo espao espao em em branco branco // ler um T para t // ler um T para t No fim devolve-se return return *this; *this; uma referncia para a }} prpria stream.
C++
99
100
get e put
Quando no se quer ignorar os espaos em branco, usa-se a funo get da classe istream. Eis um programa para copiar integralmente de cin para cout:
#include #include <iostream.h> <iostream.h> int int main() main() {{ char char x; x; while while (cin.get(x)) (cin.get(x)) cout.put(x); cout.put(x); }} return return 0; 0; Podia ter-se feito cout << x; mas fica mais simtrico usar a funo put da classe ostream.
Definia-se assim:
ostream& ostream& operator operator << << (ostream& (ostream& s, s, const const String& String& x) x) {{ return return ss << << x.p; x.p; }} istream& istream& operator operator >> >> (istream& (istream& s, s, String& String& x) x) {{ char char buf[256]; buf[256]; s.get(buf, s.get(buf, 256, 256, '\n'); '\n'); Afectao da classe xx == buf; buf; String. return return s; s; }} C++
A funo get tem uma variante com trs parmetros: char*, int e char. Serve para ler para o primeiro argumento no mais que o nmero de caracteres indicados pelo segundo argumento, parando se aparecer o carcter indicado no terceiro argumento. Exemplo:
char char buf[32]; buf[32]; ... ... Se fizssemos s cin.get(buf, cin.get(buf, 32, 32, '\n'); '\n'); cin >> buf; podamos transbordar
C++
101
102
Exerccios 5
1. Definir operadores << e >> para os tipos Complex, Date, Person, etc. 2. Definir um tipo String mais sofisticado que o anterior, que evite duplicao de memria quando h afectaes. Em vez disso, usa-se um apontador para uma zona de memria comum, e faz-se cada objecto lembrar-se de quantas vezes foi afectado a outro (isto , quantos apontadores h para ele). Comear por definir uma classe auxiliar SRep assim:
Apontador para a cadeia em class class SRep SRep {{ memria dinmica. private: private: char char *s; *s; int int n; n; Contador de referncias SRep(void); SRep(void); friend friend class class String; String; }} Com esta declarao, todas as funes da classe String ficam camaradas da classe SRep (podendo portanto aceder a todos os membros, pblicos e privados). C++
Exerccios 6
No deprimente jogo MonsterMania intervm trs personagens: dois horrveis monstros e um suculento ser humano. Para sobreviver aos monstros que o perseguem, o humano tem que conseguir alcanar a tempo um frasco contendo uma poo mgica que instantaneamente o transformar tambm num horrvel monstro. As posies iniciais dos personagens so fixas. O programa comea por pedir as coordenadas do frasco, a velocidade do ser humano e a dos monstros, aps o que realiza a animao dos acontecimentos at ao desenlace final.
Informao sobre operaes para manipular o ecr Para limpar o ecr: cout << "\033"<<"[2J"; Para colocar o cursor na linha x, coluna y: cout << "\033"<< "[" << y << ";" << x << "H";
103
C++
104
Estrutura sugerida
Point
Actor
e ainda: Classes Classescoleco coleco Classes Classesde deinterface interface Programao Programaode debibliotecas bibliotecasde declasses classes Ferramentas Ferramentasde dedesenvolvimento desenvolvimento Iteradores Iteradores
Monster 2
Man
Game
e praticar, e estudar, e praticar, e estudar Como variante, implemente a relao Actor tem-um Point, em vez da relao Actor -um Point.
C++
105
C++
106
por isso
Esta pgina fica em branco intencionalmente. Se quiser use-a para amaldioar o C++.
continua
C++
107
C++
108
Programao com C++, fazendo amplo uso de classes, herana, funes virtuais e outras coisas que tais (segunda parte complementos)
por Pedro Guerreiro Curso preparado especialmente para a cadeira de Linguagens de Programao da licenciatura em engenharia informtica da FCT/UNL Maio de 1994
C++
109
110
Os estudantes so pessoas
Eis as declaraes de Person e Student:
class class Person Person {{ private: private: String String name; name; const const char char sex; sex; public: public: Person Person (String (String name, name, char char sex): sex): name(name), sex(sex) name(name), sex(sex) {{ }; }; char char Sex() Sex() const const {return {return sex;} sex;} String Name() const String Name() const {return {return name;} name;} void void Dance(void) Dance(void) const const {cout {cout << << "I, "I, (" (" << << name name << << ") ") am am dancing" dancing" << << endl;} endl;} }; }; As pessoas class class Student: Student: public public Person Person {{ danam private: private: String school; String school; int int year; year; public: public: Student Student (String (String name, name, char char sex, sex, String school): String school): Person(name, Person(name, sex), sex), e os school(school), school(school), estudantes year(1) year(1) estudam. {{ }} void Pass(void) {year++;} void Pass(void) {year++;} void void Study(void) Study(void) const const {cout {cout << << "I, "I, (" (" << << Name() Name() << << ") ") am am studying" studying" << << endl;} endl;} }; }; 111 Ateno: name um membro C++ privado de Person; no est acessvel nas classes derivadas.
C++
112
C++
113
C++
114
Experimentemos:
... ... Person Person x("Maria", x("Maria", 'F'); 'F'); Student Student y("Jorge", y("Jorge", 'M', 'M', "Arquitectura"); "Arquitectura"); ShowPerson1(x); ShowPerson1(x); ShowPerson1(y); ShowPerson1(y); ShowPerson2(x); ShowPerson2(x); ShowPerson2(y); ShowPerson2(y); ... ...
O resultado :
Person: Maria, F Person: Jorge, M Person: Maria, F Student: Jorge, M, Arquitectura
OK? 115
C++
116
Erros de compilao
Os erros de compilao ajudam a perceber o que se passa:
int int main(void) main(void) {{ ... ... Study1(p); Study1(p); Study2(p); Study2(p); Study3(rp); Study3(rp); p.Study(); p.Study(); rp->Study(); rp->Study(); }} ... ... No suitable constructor or conversion function exists for conversion from "Person" to "Student". "Person" cannot be converted to "Student&". "Person* cannot be converted to "Student*". "Study" is not a member of "Person".
Study1(p); Study1(p);
est OK. As
Erro: Conversion from "Person" to a reference to a non-const type "Student&" requires a temporary. As mesmas mensagens de antes
Quer dizer, se houvesse uma funo de converso de Person para Student, j tudo correria bem?
claro que na funo Study2 a referncia podia ser constante (estudar muda um estudante?):
Assim a void void Study2(const Study2(const Student Student &p) &p) chamada {{ Study2(p) cout cout << << p.Name() p.Name() << << "" is is studying." studying." est certa. << << endl; endl; 118 C++}}
C++
117
Os pssaros voam
Se a herana pblica quer dizer -um, nem sempre -um quer dizer herana pblica.
class class Bird Bird {{ private: private: public: public: virtual virtual void void Fly(); Fly(); }; }; class class Penguin: Penguin: public public Bird Bird {{ private: private: public: public: }; }; ... ... Penguin Penguin p; p; ... ... p.Fly(); p.Fly(); ... ...
Bird
Penguin
Alm disso, dava erro na definio das funes Study, porque a funo Person::Name() const deixava de estar acessvel. 119 C++
O problema que apesar de normalmente as aves voarem, h aves que no voam. Logo a funo Fly no devia estar na classe Bird. 120
C++
Esta a soluo a la Smalltalk. De facto, significa que os pinguins podem voar, mas um erro tentarem faz-lo!
void void Error(char Error(char *s) *s) {{ cerr cerr << << ss << << endl; endl; }} cerr: stream de sada de erros C++
121
122
124
NonFly ingBird
Flying Bird
NonEdi bleBird
Edible Bird
Penguin
Chicken
Duck
Eagle
}}
long long pop; pop; pop pop == d.Population(); d.Population(); return return 0; 0;
Ento, que classe deve ocupar-se da populao? A classe Bird, claro, mas preciso recorrer s classes de base virtuais 125
C++
C++
126
Triangle
C++
Rectangle 127
C++
128
Construtor:
Triangle::Triangle(Point Triangle::Triangle(Point p0, p0, Point Point p1, p1, Point Point p2): p2): Polygon(3) Polygon(3) {{ Polygon::operator[](0)= Polygon::operator[](0)= p0; p0; Polygon::operator[](1)= Polygon::operator[](1)= p1; p1; Polygon::operator[](2)= Polygon::operator[](2)= p2; p2; C++}} Fica assim porque o apontador p um membro privado de Polygon.
Construtor:
Rectangle::Rectangle(Point Rectangle::Rectangle(Point lowerLeft, lowerLeft, Point Point upperRight): upperRight): Polygon(4) Polygon(4) {{ ... ... }} C++
129
130
Carro
Funes virtuais puras: Voc Voctem temque quefornecer fornecera afuno; funo;eu euno no fao ideia como voc vai fazer isso. fao ideia como voc vai fazer isso. Funes virtuais simples: Se Sequiser quiserfornea forneaa afuno; funo;se seno nofornecer fornecer vai acabar por usar a que eu forneci. vai acabar por usar a que eu forneci.
C++
Descapotavel
Berlina
131
C++
132
C++
133
C++
134
Carros a gasleo
Agora aparece uma nova subclasse de carros, as carrinhas, que andam a gasleo. Como a funo virtual se chama MeterCombustivel, podemos pensar, distraidamente, que no vale a pena definir a funo na nova classe:
class class Carrinha: Carrinha: public public Carro Carro {{ private: private: public: public: Carrinha(String Carrinha(String matricula, matricula, String String marca): marca): Carro(matricula, marca) { Carro(matricula, marca) { }} }; };
Desgraa:
int int main() main() {{ Descapotavel Descapotavel d("HB-03-38", d("HB-03-38", "BMW"); "BMW"); Berlina b("48-75-BR", Berlina b("48-75-BR", "Opel"); "Opel"); Carrinha Carrinha c("99-04-DE", c("99-04-DE", "Ford"); "Ford"); d.MeterCombustivel(10); d.MeterCombustivel(10); b.MeterCombustivel(20); b.MeterCombustivel(20); c.MeterCombustivel(30); c.MeterCombustivel(30); }} C++ ... ... Esta carrinha vai meter gasolina em vez de gasleo
135
Os membros protegidos (declarados na zona protected) so acessveis pelas classes derivadas, tal como os membros pblicos, mas s pelas classes derivadas, e no por funes globais. 136 C++
class class Carrinha: Carrinha: public public Carro Carro {{ private: private: public: public: ... ... void void MeterCombustivel(int MeterCombustivel(int n) n) {{ cout cout << << matricula matricula << << ": ": Meti Meti "" << << nn << << "" litros litros de de gasoleo" gasoleo" << << endl; endl; }} }; }; Se esquecssemos de definir isto, o compi137 C++ lador dava erro: An object of abstract class "Carrinha" cannot be created.
Como o membro matricula protegido, pode ser usado aqui assim. Se fosse privado no podia, claro.
138
Exerccios 7
1. Fazer experincias com as hierarquias de classes apresentadas e outras semelhantes. 2. Investigar a ordem de construo e de destruio ao longo da hierarquia de classes. 3. Observar a construo e destruio de objectos temporrios. 4. Fazer heranas mltiplas variadas e ver o que acontece. 5. Esquecer deliberadamente de definir funes virtuais puras nas classes derivadas. 6. Completar o exemplo dos polgonos, introduzindo quadrados, losangos, etc.
Resposta:
Descapotavel Berlina Carrinha Carro Berlina
C++
139
C++
140
C++
141
C++
142
Listas
Uma lista uma estrutura de dados linear, onde podemos inserir e remover elementos em qualquer posio. Como insero e remoo cabea so frequentes, temos funes para isso. Para aceder aos elementos da lista usamos a indexao. Como frequente precisar do comprimento, usamos um membro para conter esse valor:
class class List List {{ private: private: Node Node *head; *head; int int length; length; public: public: List(void): List(void): head(0), head(0), length(0){ length(0){ }} virtual virtual ~List(void) ~List(void) {{ }} virtual virtual int int Length(void) Length(void) const const Inserir cabea {return length;} {return length;} virtual virtual List& List& Cons(int); Cons(int); virtual List& Remover a cabea virtual List& Tail(void); Tail(void); virtual virtual List& List& Insert Insert (int (int item, item, int int position); position); virtual List& virtual List& Remove(int Remove(int position); position); virtual virtual int& int& operator[](int) operator[](int) const; const; virtual virtual void void Display(void) Display(void) const; const; }; };
Agora vai ser s uma lista de int. Mais adiante veremos como ter uma lista de objectos de um tipo qualquer.
143
Como esta classe tem vocao para servir de base, todas as funes so virtuais, para 144 poderem ser redefinidas. C++
Como a funo Cons devolve uma List, podemos escrever expresses assim:
List s; s.Cons(5).Cons(4).Cons(9);
Exerccio: Reprogramar estas operaes sob a forma de operadores + para o Cons e (un-rio) para Tail.
145
Idem (o intervalo agora zero List &List::Remove(int position) List &List::Remove(int position) at length1) {{ position position == min(position, min(position, length length -- 1); 1); if(position if(position <= <= 0) 0) return return Tail(); Tail(); Node Node *p *p == head; head; while(position) while(position) pp == pp -> -> next; next; p->next p->next == p->next->next; p->next->next; length; length; return return *this; *this; }} Exerccio: modificar esta funo (e as outras que for preciso), para recuperar a memria 146 C++ que pertenceu aos ns removidos. Programar tambm o destrutor de List.
Como este operador devolve int&, podemos modificar o valor dos ns, assim, por exemplo:
List s; s.Cons(5).Cons(4).Cons(9); s[2] = 7; ...
Mostrar:
void void List::Display(void) List::Display(void) const const {{ for(Node for(Node *p *p == head; head; p; p; pp == p->next) p->next) cout cout << << p->value p->value << << "" "; "; cout cout << << endl; endl; }} Esta funo apenas para controlo. C++
Ateno: os conjuntos no so listas. Nos conjuntos no h noo de posio, nem pode haver repeties, etc. 147
C++
148
Conjuntos
A funo privada Where calcula a posio de um elemento na lista representativa. depois utilizada na funo Has e na funo Remove:
int int Set::Where(int Set::Where(int x) x) {{ int int i=rep.Length(); i=rep.Length(); while(i) while(i) if(rep[i]==x) if(rep[i]==x) break; break; return return i; i; }} int int Set::Has(int Set::Has(int x) x) {{ return return Where(x) Where(x) >= >= 0; 0; }} Set& Set& Set::Insert(int Set::Insert(int x) x) {{ if(!Has(x)) if(!Has(x)) rep.Cons(x); rep.Cons(x); return return *this; *this; }} Set& Set& Set::Remove(int Set::Remove(int x) x) {{ int int i(Where(x)); i(Where(x)); if(i>= if(i>= 0) 0) rep.Remove(i); rep.Remove(i); return *this; return *this; }} C++ Exerccio: Programar as restantes operaes habituais com conjuntos (reunio, interseco, diferena). Usar operadores +, *, , sobrecarregados, de modo a que se possa fazer, por exemplo, a reunio de um conjunto com um elemento isolado. Programar tambm os operadores +=, *= e =. Exerccio: Programar sacos (em ingls bags). Sacos so como conjuntos, mas toma-se nota do nmero de ocorrncias de cada elemento. Sugesto: usar uma representao com duas listas: uma para os elementos, e outra, paralela, com o nmero de ocorrncias.
149
pblica, podamos usar class class Set: Set: private private List List {{ indexao na lista, etc., o que private: private: int no interessa aqui. int Where(int); Where(int); public: public: Warning: Set(): Set(): List() List() {{ }} "Set::Insert(int) hides ~Set(void) ~Set(void) {{ }} Set& Insert(int); the virtual function Set& Insert(int); Set& "List::Insert(int,int)". Set& Remove(int); Remove(int); int Has(int); int Has(int); Acedendo aos int int Cardinality(void) Cardinality(void) const const {return List::Length();} membros {return List::Length();} void (pblicos) da void Display(void) Display(void) const const {List::Display();} classe be base: {List::Display();} }; List::Length(), }; Exerccio: List::Display(). 150 Definir as funes que faltam. C++
Podemos dizer, com propriedade, que um Conjunto uma Coleco, e como tal, fazer a classe Set herdar publicamente da classe Collection.
C++
151
C++
152
Iteradores (1)
A funo int Set::Has(int) muito ineficiente
int int Set::Has(int Set::Has(int x) x) int {{ int Set::Where(int Set::Where(int x) x) { return Where(x) >= 0; return Where(x) >= 0; { int }} int i=rep.Length(); i=rep.Length(); while(i) while(i) if(rep[i]==x) if(rep[i]==x) porque, como cada break; break; indexao requer return return i; i; } um ciclo, a comple}
Iteradores (2)
Ateno a esta definio:
class class Node Node {{ private: private: int int value; value; Node Node *next; *next; public: public: ... ... friend friend class class List; List; friend friend class class Iterator; Iterator; }; };
Classes camaradas. Os objectos da classe Iterator tm acesso a todos os membros das classes Node e List.
O que faz falta um mecanismo que nos permita visitar sucessivamente todos os ns da lista de representao do conjunto. Esse mecanismo so os iteradores. Um iterador (de lista, neste caso) um objecto capaz de se lembrar qual o n corrente da lista, e de saber como passar ao n seguinte. Quer dizer, a classe Iterator ter, pelo menos, um membro privado que um apontador para um Node, e uma funo para avanar na lista. 153 C++
class class List List {{ private: private: Node Node *head; *head; int Inicializa-se int length; length; public: com a cabea public: ... da lista. ... Devolve-se friend class Iterator; friend class Iterator; o valor do }; }; n corrente class Ter acabado class Iterator Iterator {{ private: se j no private: Node* current; existir n Node* current; public: corrente. public: Iterator(List Iterator(List s): s): current(s.head) current(s.head) {{ }} int& int& Current(void) Current(void) {return {return current->value;} current->value;} int int NotExhausted(void) NotExhausted(void) {return {return current current != != 0;} 0;} void Next(void) {current = current->next;} void Next(void) {current = current->next;} }; }; C++ Avanar na lista fcil
154
Iteradores (3)
Recorrendo ao iterador, a funo Has fica muito mais jeitosa:
int int Set::Has(int Set::Has(int x) x) {{ for for (Iterator (Iterator next(rep); next(rep); next.NotExhausted(); next.NotExhausted(); next.Next()) next.Next()) if(next.Current()==x) if(next.Current()==x) return return 1; 1; return return 0; 0; }} Exerccios: 1. Programar a funo Remove usando iteradores. 2. Reprogramar as operaes habituais sobre conjuntos (reunio, etc.) usando iteradores. 3. Acrescentar um iterador classe String, e utiliz-lo para programar uma funo para por uma String em maisculas e outra para pr em minsculas.
Iteradores de conjuntos
claro que a classe Set deve tambm oferecer um iterador aos seus clientes:
Esta classe camarada da class class SetIterator SetIterator {{ classe Set, para poder aceprivate: private: der ao membro privado rep. Iterator Iterator rep; rep; public: public: SetIterator(Set SetIterator(Set s): s): rep(s.rep){ rep(s.rep){ }} const const int& int& Current(void) Current(void) {return {return rep.Current();} rep.Current();} int int NotExhausted(void) NotExhausted(void) {return {return rep.NotExhausted() rep.NotExhausted() != != 0;} 0;} void Next(void) {rep.Next();} void Next(void) {rep.Next();} }; };
Exemplo:
void void DisplayI(Set& DisplayI(Set& s) s) {{ for(SetIterator for(SetIterator next(s); next(s); next.NotExhausted(); next.NotExhausted(); next.Next()) next.Next()) cout cout << << next.Current() next.Current() << << "" "; "; cout << endl; cout << endl; }}
C++
155
156
Operador ()
Eis um outro tipo de iterador, para a classe List, baseado no operador ():
class class Iterator Iterator {{ private: private: Node* Node* current; current; public: public: Iterator(List Iterator(List s): s): current(s.head) current(s.head) {{ }} int* operator()(void); int* operator()(void); }; }; Se no se usasse o
void ficava assim: int* int* Iterator::operator()(void) Iterator::operator()(void) int* operator()(); {{ if(current) if(current) Devolve-se um int*, {{ e no um int, para Node* Node* temp(current); temp(current); poder usar o 0 para current = current->next; current = current->next; assinalar o fim de return return &temp->value; &temp->value; iterao. }} return 0; return 0; }}
Exemplo de utilizao:
int int Set::Has(int Set::Has(int x) x) {{ Iterator Iterator next(rep); next(rep); int int *p; *p; while(p while(p == next()) next()) if(*p==x) if(*p==x) return return 1; 1; return return 0; 0; } C++ } Exerccios: 1. Programar o iterador de conjuntos assim. 2. Substituir o outro iterador por este.
157
158
Aqui h 4 pessoas DisplayCount(); DisplayCount(); e 2 estudantes Person* Person* ppp; ppp; ppp ppp == new new Student("Miguel", Student("Miguel", 'M', 'M', "Medicina"); "Medicina"); delete ppp; delete ppp; Aqui h 4 pessoas e 2 estuDisplayCount(); DisplayCount(); dantes, tambm. Mas se o delete delete pp; pp; delete delete ss; ss; }} return return 0; 0; destrutor no fosse virtual, haveria 4 pessoas e trs estudantes!
Os membros estticos so definidos a nvel global, assim, por exemplo: int ou int Person::numberOfPersons(0); Person::numberOfPersons(0);
int int Person::numberOfPersons Person::numberOfPersons == 0; 0; int int Student::numberOfStudents(0); Student::numberOfStudents(0); int Student::numberOfStudents = 0; C++ int Student::numberOfStudents = 0;
Se pusermos mensagens nos construtores e nos destrutores, poderemos observar a construo e destruio dos objectos.
e ou 159
void void DisplayCount(void) DisplayCount(void) {{ cout cout << << "Persons: "Persons: "" << << HowManyPersons() HowManyPersons() << ", Students: << ", Students: "" << << HowManyStudents() HowManyStudents() << << endl; endl; }} C++
160
Excepes
O mecanismo das excepes serve para facilitar a gesto dos erros, em particular nos programas formados por componentes independentes. assim: quando uma funo encontra uma condio imprevista, ou impossvel, ou anormal, deve lanar(em C++ throw) uma excepo, aps o que retorna imediatamente. Essa excepo ser apanhada pelo bloco envolvente mais prximo que tenha declarado ser capaz de apanhar (em C++ catch) excepes do tipo da que foi lanada. Para um bloco ser capaz de apanhar excepes, tem que ser um bloco de ensaio (em C++ bloco try). Em esquema:
void void f(...) f(...) {{ try try {{ ... ... g(); g(); ... ... }} catch(int catch(int n) n) {{ ... ... }} }} C++ A funo g pode lanar a excepo -46. A funo f tem um bloco try e est pronta para a apanhar. void void g(...) g(...) {{ ... ... if if (...) (...) throw throw 46; 46; ... ... }}
Classe Stack
Eis uma classe Stack, para representar uma pilha de inteiros, implementada em termos de um vector:
class class Stack Stack {{ private: private: int int *s; *s; int int *top; *top; const const long long size; size; public: public: Stack(long); Stack(long); ~Stack(void) ~Stack(void) {delete {delete [] [] s;} s;} int Empty(void) const int Empty(void) const {return {return top top == == s;} s;} int Full(void) const {return tops int Full(void) const {return tops == == size;} size;} Stack& Stack& Push(int Push(int n); n); Stack& Stack& Pop(void); Pop(void); int int Top(void) Top(void) const const {return {return *(top *(top 1);} 1);} int Depth(void) const {return int Depth(void) const {return top top s;} s;} }; }; Stack::Stack(long Stack::Stack(long size): size): size(size), size(size), s(new s(new int int [size]) [size]) {{ top = s; Stack& Stack::Push(int top = s; Stack& Stack::Push(int n) n) }} {{ *top++ *top++ == n; n; Stack& Stack::Pop(void) return return *this; *this; {Stack& Stack::Pop(void) }} { top; top; return return *this; *this; }} O que que pode correr
161
162
Apanhando excepes
Como exemplo, eis uma funo main com bloco try:
Pilha com capacidade int para quatro int main(void) main(void) {{ try try {{ Stack Stack s1(4); s1(4); Est s1.Push(3).Push(4).Push(5).Push(6); s1.Push(3).Push(4).Push(5).Push(6); cheia. A funo lana a excepo "Stack s1.Push(7); s1.Push(7); is full. Cannot Push." s1.Pop(); s1.Pop(); Nem se chega aqui. }} catch(const catch(const char* char* s) s) {{ cerr cerr << << "Exception: "Exception: "" << << ss << << endl; endl; abort(); abort(); }} Afixa-se o texto da excepo, e o programa termina por abort(). return return 0; 0; int int main(void) main(void) {{ try try {{ Stack Stack s1(1000000000); s1(1000000000); ... ... }}
}}
163
? 164
int int main(void) main(void) {{ try try Aqui apanham-se as {{ excepes tipo ... ... const char * }} catch(const catch(const char* char* s) s) {{ cerr cerr << << "Exception: "Exception: "" << << ss << << endl; endl; abort(); abort(); e aqui as de tipo }} Stack::Exception. catch catch (Stack::Exception (Stack::Exception exc) exc) {{ switch(exc){ switch(exc){ case case Stack::IsFull: Stack::IsFull: cerr cerr << << "Stack "Stack exception: exception: Stack Stack is is full" full" << endl; << endl; break; break; case case Stack::IsEmpty: Stack::IsEmpty: cerr cerr << << "Stack "Stack exception: exception: Stack Stack is is empty" empty" << endl; << endl; break; break; }} Os enumerados definidos }} numa classe ficam no return 0; mbito da classe. Fora dela return 0; }} tm que ser qualificados com o nome da classe. 166 C++
Parametrizando as excepes
As excepes so objectos. Tm tipo. Podemos usar classes como excepes e fazer derivaes, etc. Eis uma classe de excepes para as pilhas, capaz de transmitir o elemento que causou a excepo (no caso de pilha cheia):
class class StackException{ StackException{ private: private: public: public: enum enum Type{IsFull, Type{IsFull, IsEmpty}; IsEmpty}; const const int int offending; offending; const const Type Type type; type; StackException(void): StackException(void): type(IsEmpty), type(IsEmpty), offending(0) offending(0) {{ }} StackException(int StackException(int value): value): type(IsFull), type(IsFull), offending(value) offending(value) {{ }} }; }; Stack& Stack& Stack::Push(int Stack::Push(int n) n) {{ if(top if(top -- ss == == size) size) throw throw StackException(n); StackException(n); *top++ *top++ == n; n; return return *this; *this; }} Stack& Stack& Stack::Pop(void) Stack::Pop(void) {{ if(top if(top == == s) s) throw throw StackException(); StackException(); top; top; return return *this; *this; C++ }} As expresses no throw so os construtores da classe.
Se uma funo no indica as excepes que lana ento presume-se que pode lanar qualquer excepo. 167 C++
Excepes desconhecidas
Se alguma funo lanar uma excepo desconhecida, que no for apanhada por ningum, o programa vai terminar anormalmente, chamando implicitamente a funo terminate(), a qual, em princpio, chamar a funo abort(). Podemos, mesmo assim, retomar o controlo, antes de o programa terminar, para efectuar algumas operaes de ltima hora (fechar ficheiros, afixar uma mensagem, etc.) Para isso basta substituir a funo terminate por uma nossa. Faz-se assim, em esquema:
Funo que queremos chamar antes de o programa void void UncaughtException(void) UncaughtException(void) terminar {{ cerr cerr << << "An "An unknown unknown exception exception was was thrown thrown "" "and nobody caught it.\n"; "and nobody caught it.\n"; abort(); abort(); Para usar abort() temos que }} incluir o ficheiro <stdlib.h>. int int main(void) main(void) {{ set_terminate(UncaughtException); set_terminate(UncaughtException); try try {{ Para usar ... set_terminate() temos ... }} que incluir o ficheiro C++ <terminat.h>.
169
170
Exerccios 8
1. Programar a classe Queue. Os objectos desta classe representam filas de espera, nas quais os elementos so atendidos segundo a regra first in first out (por oposio s pilhas, onde a regra last in first out). Preveja iteradores e excepes. 2. Implementar a classe Deque (double ended queue). Uma Deque como uma Queue, mas os objectos podem ser acrescentados e retirados de qualquer dos extremos da fila. 3. Implementar a classe PriorityQueue, que serve para representar filas de espera com prioridade. Em todos estes casos, deve comear-se por usar filas de objectos de tipos simples (por exemplo int ou char), mas deixando as coisas de forma a que, sem muito esforo, se possa generalizar a, por exemplo, filas de clientes (no barbeiro ou no banco)
C++
171
Isto no uma classe: uma class template. Especifica como que as diversas classes pilha vo ser construdas. 172 C++
OK?
C++
173
C++
174
As funes Cons, Tail, Insert, Remove, o operador [] e a funo Display so definidos parte. 175
C++
176
177
Iterador genrico
Para o iterador a mesma coisa. Assim:
template <class T> template <class class Iterator {{T> class Iterator private: private: Node<T>* Node<T>* current; current; public: public: Iterator(List<T> s): {{ }} Iterator(List<T> s): current(s.head) current(s.head) T& Current(void) {return current->value;} T& Current(void) {return current->value;} int NotExhausted(void) int NotExhausted(void) {return current {return current != != 0;} 0;} void Next(void) void Next(void) {current {current == current->next;} current->next;} }; };
Funes genricas
Tambm pode haver funes globais genricas (function templates). Eis trs exemplos, s para ilustrar o mecanismo:
template template <class <class T> T> void void Print2 Print2 (List<T> (List<T> &s) &s) {{ for(Iterator<T> for(Iterator<T> next(s); next(s); next.NotExhausted(); next.NotExhausted(); next.Next()) next.Next()) cout cout << << next.Current() next.Current() << << "" "; "; cout << endl; cout << endl; }} template template <class <class T> T> void void Print1 Print1 (List<T> (List<T> &s) &s) {{ A propsito: era mais apropriado TT *p; usar estas funes (Print1 ou *p; Iterator<T> Iterator<T> next(s); next(s); Print2) para mostrar a lista, em while(p vez da funo membro while(p == next()) next()) Display(). Assim, no havia cout << *p << " "; cout << *p << " "; dificuldades com tipos que no cout cout << << endl; endl; tivessem o operador ostream <<. }} template template <class <class T> T> void void PrintTwice PrintTwice (List<T> (List<T> &s) &s) {{ Print1(s); Print1(s); Print2(s); Print2(s); }}
ou assim:
template <class T> template <class class Iterator {{T> class Iterator private: private: Node<T>* Node<T>* current; current; public: public: Iterator(List<T> s): Iterator(List<T> s): current(s.head) current(s.head) {{ }} T* operator()(void); T* operator()(void); }; };
com:
template <class template <class T> T> T* Iterator<T>::operator()(void) T* Iterator<T>::operator()(void) {{ if(current) if(current) {{ Node<T>* temp(current); Node<T>* current ==temp(current); current->next; current current->next; return &temp->value; return &temp->value; }} return 0; }} return 0; C++
179
C++
180
O construtor para a classe T ser chamado implicitamente para cada um dos elementos quando o vector for construdo. Inversamente, o destrutor ser chamado quando o vector for destrudo. 181 C++
for(;;) for(;;) try try {{ Este cout << "indice: "; programa cout << "indice: "; (cin (cin >> >> i).getline(line, i).getline(line, 80); 80); ilustra if(i<-100) tambm um if(i<-100) break; esquema break; cout cout << << "cadeia "cadeia corrente: corrente: "" para << recuperar de << sv[i] sv[i] << << endl; endl; cout excepes cout << << "nova "nova cadeia: cadeia: "; "; cin cin >> >> sv[i]; sv[i]; cout cout << << ii << << "" "" << << sv[i] sv[i] << << endl; endl; }} catch(const catch(const char* char* s) s) {{ cout cout << << "Exception: "Exception: "" << << ss << << endl; endl; }} for(...) try { return 0; return 0; ... }} } catch { 182 C++ ... } ...
183
184
Para ver se um elemento est na rvore, v-se se ele est num dos ns pendurados na raiz (directamente ou indirectamente), se a raiz j existir:
template template <class <class T> T> int Tree<T>::Has(const int Tree<T>::Has(const T& T& t) t) {{ return return root root ?? root->Has(t) root->Has(t) :: 0; 0; }}
Estas duas funes so, afinal, a base das chamadas recursivas (estas so implementadas pelas funes da classe genrica Node). 185
Os valores dos ns vo ser escritos segundo a ordem determinada pelo operador <= na classe paramtrica T.
C++
C++
186
Exerccios 9
1. Reprogramar as classes Queue, Deque e PriorityQueue genericamente. Basear as implementaes na classe List<T>. 2. Programar a classe AssocArray (associative array) capaz de conter objectos que so pares (String, int), onde o int representa, por exemplo, o nmero de ocorrncias da String, e onde esse nmero possa ser consultado por indexao. Em esquema:
class class AssocArray AssocArray {{ private: private: ... ... public: public: ... ... int& int& operator[](const operator[](const String&); String&); ... ... }}
Consulta da rvore
Implementar isto de duas formas: primeiro base de um vector dinmico capaz de crescer em caso de overflow, depois base de uma lista genrica.
187
C++
188
Exerccios 10
1. Generalizar a classe AssocArray e programar a classe genrica Map, capaz de guardar pares de valores de classes arbitrrias K e V. Os objectos da classe K so as chaves (key) e os elementos da classe V so os valores. A declarao da classe assim, em esquema:
template template <class <class K, K, class class V> V> class class Map Map {{ private: private: ... ... public: public: ... ... V& V& operator[](const operator[](const K&); K&); ... ... }}
Pode admitir-se que a classe K ordenada e que dispe dos operadores == e <=. Basear a implementao em rvores genricas. (Os ns da rvore sero pares (K, V)).
C++
189
Seleccionar um flags() flags() const; const; conjunto flags por flags(long flags(long f); f); oring dos enumerados setf(long setf(long setbits, setbits, long long field); field); setf(long) setf(long) ;; Seleccionar as flags unsetf(long) unsetf(long) ;; uma a uma. width() width() const; const; Largura do campo width(int width(int w); w); fill(char) fill(char) ;; Carcter de enchimento fill() fill() const; const; precision(int) precision(int) ;; precision() precision() const const ;; Preciso dos nmeros de vrgula flutuante.
Exemplo:
cout cout << << 1234 1234 << << '' '' << hex << << hex << 1234 1234 << << '' '' << << oct oct << << 1234 1234 << << endl; endl;
d:
1234 4d2 2322
191
C++
192
193
Construir, avanar,
As funes pblicas so assim:
WordStream::WordStream(char WordStream::WordStream(char *s): *s): stream(s), stream(s), lastChar(0) lastChar(0) {{ Se a abertura do ficheiro falhar, if if (stream) (stream) a stream fica a valer 0. if (SkipChars()) if (SkipChars()) ReadWord(); ReadWord(); Assim, a primeira palavra fica }} logo disponvel. void void WordStream::Next(void) WordStream::Next(void) {{ if if (SkipChars()) (SkipChars()) ReadWord(); ReadWord(); }} int int WordStream::End(void) WordStream::End(void) {{ return return stream stream == == 0; 0; }} String String WordStream::Current(void) WordStream::Current(void) {{ return return String(current); String(current); }}
Listar as palavras
O seguinte programa lista no terminal as palavras do ficheiro cujo nome passado na lista de comando:
int int main main (int (int argc, argc, char* char* argv[]) argv[]) {{ for(WordStream for(WordStream stream(argv[1]); stream(argv[1]); !stream.End(); !stream.End(); stream.Next()) stream.Next()) cout cout << << stream.Current() stream.Current() << << endl; endl; }} return return 0; 0;
OK?
Exerccios:
OK?
1. Listar as palavras por ordem alfabtica, sem repeties. 2. Listar as palavras juntamente com o nmero de ocorrncias.
OK?
195
C++
196
String Streams
Normalmente as streams esto associadas a ficheiros. Mas tambm podem estar associadas a cadeias de caracteres. Usam-se as classes istrstream e ostrstream. Um exemplo:
#include #include <iostream.h> <iostream.h> Para usar streams de cadeias #include #include <strstrea.h> <strstrea.h> preciso incluir este. int main(void) main(void) {int { char line[80]; Declarao de uma istrstream, char s[80]; line[80]; char com inicializao. char s[80]; int int x; x; for(;;) for(;;) {{ cout ou cout << << "Um "Um numero numero ou uma uma cadeia: cadeia: "; "; cin.getline(line, 80, '\n'); cin.getline(line, 80, '\n'); if(!*line) if(!*line) break; break; ist1(line, strlen(line)); istrstream istrstream ist1(line, strlen(line)); As if(ist1>>x) if(ist1>>x) variveis {{ cout declaradas cout << << "Um "Um inteiro: inteiro: "" << << xx << << endl; endl; continue; no bloco Outra istrstream continue; }} so istrstream ist2(line, istrstream ist2(line, strlen(line)); strlen(line)); destru-das if(ist2>>s) if(ist2>>s) no fim de {{ cout cada passo cout << << "Uma "Uma cadeia: cadeia: "" << << ss << << endl; endl; continue; da iterao. continue; }} }} Podia-se dispensar as variveis e trabalhar logo return 0; sobre os objectos: return 0; }} if(istrstream (line, strlen(line)) >> x) C++
Como diz o Stroustrup: Please consult your manual for details, or experiment.
C++
197
198
Problemas finais
1. Problema da mquina de vender latas: Construir um programa para simular o funcionamento de uma mquina de vender latas (de coca-cola, pepsi, sumol, etc.), ou maos de cigarros, ou sandes. O utilizador pede o que quer carregando em botes que existem na mquina e paga metendo moedas numa ranhura. Antes de meter a ltima moeda, o utilizador pode arrepender-se, carregando no boto para cancelar. Depois de metida a moeda que perfaz ou ultrapassa o preo do artigo escolhido, a mquina entrega esse artigo. Normalmente, a mquina dar troco. No entanto, se excepcionalmente no tiver moedas suficientes para isso, a mquina devolve o dinheiro introduzido e pede desculpa, mas no d o produto. 2. Problema do analisador ortogrfico: Escrever um programa que verifique se cada palavra existente num texto est presente num dicionrio. As palavras desconhecidas devem ser listadas no fim, por ordem alfabtica.
C++
terminate();
199
C++
200