Você está na página 1de 100

Programao com C++, fazendo amplo uso de classes, herana, funes virtuais e outras coisas que tais (primeira

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

CSP C Mesa Modula-2 Oberon C++ Ada Smalltalk

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++

Bibliografia sobre C++

Bibliografia sobre Object-Oriented


Revistas: 1. Object -Oriented Programming, Peter Coad, Jill Nicola, Prentice Hall, 1993. 2. Object Oriented Design, Grady Booch, Benjamin/Cummings, 1991. 3. Object-Oriented Modeling and Design, James Rumbaugh et al, Prentice Hall, 1991. 4. Object-Oriented Software Construction, Bertrand Meyer, Prentice Hall, 1988. 5. Eiffel, The Language, Bertrand Meyer, Prentice Hall, 1992. 6. Object-Oriented Software Engineering, I. Jacobson et al, Addison-Wesley, 1992. ...

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++

O que uma classe? A classe Point


Uma Umaclasse classe uma umaestrutura estruturacom comfunes, funes, com uma parte privada e outra com uma parte privada e outrapblica pblica class class Point{ Point{ private: private: int int xx; xx; member functions int int yy; yy; public: public: Point(void); Point(void); void void Set(int, Set(int, int); int); void Move(int, void Move(int, int); int); double DistanceTo(Point); double DistanceTo(Point); void void Display(void); Display(void); }; }; Os membros da seco private s podem ser acedidos pelas funes-membro da classe; os da seco public constituem a interface dos objectos da classe. E Eonde onde que quedefinimos definimosas asfunes? funes?
C++ data members

As funes-membro da classe Point


Point::Point(void) Point::Point(void) { { xx xx = = 0; 0; yy = 0; yy = 0; } }
O construtor, chamado automaticamente quando um objecto criado, garante explicitamente que no h objectos no inicializados.

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++

double sqr(double x) { return x * x; }

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); }; };

Nveis de acesso (2)


Definies:
int int AccessLevels::GetReadOnly(void) AccessLevels::GetReadOnly(void) const const {{ return return readOnly; readOnly; }} preciso repetir o void AccessLevels::SetReadWrite(int value) void AccessLevels::SetReadWrite(int value) const aqui {{ nas readWrite = value; readWrite = value; definies. }} int int AccessLevels::GetReadWrite(void) AccessLevels::GetReadWrite(void) const const {{ return readWrite; return readWrite; }} void void AccessLevels::SetWriteOnly(int AccessLevels::SetWriteOnly(int value) value) {{ writeOnly writeOnly == value; value; }} void void AccessLevels::PrivateFunction(void) AccessLevels::PrivateFunction(void) {{ // // ... ... Tal como est, esta }} funo no serve para nada e o membro noAccess est mesmo inacessvel.

Aqui, o especificador const significa que as funes deixam o objecto na mesma.

Definies na pgina seguinte 11

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).

Um driver para a classe Point


int int main(void) main(void) { { int int x; x; int int y; y; Point Point p; p; Point Point q; q; Point origin; Point origin; double double d; d; printf("Duas printf("Duas coordenadas:\n"); coordenadas:\n"); scanf("%d%d", scanf("%d%d", &x, &x, &y); &y); p.Display(); p.Display(); p.Set(x, p.Set(x, y); y); p.Display(); p.Display(); d d= = p.DistanceTo(origin); p.DistanceTo(origin); printf("Dist printf("Dist 'a 'a origem: origem: %lf\n", %lf\n", d); d); p.Move(3, 7); p.Move(3, 7); p.Display(); p.Display(); q q= = p; p; q.Display(); q.Display(); return return 0; 0;
afectao de objectos da mesma classe: membro a membro

Por vezes, as classes tambm tm destrutores, que so chamados automaticamente quando os objectos so destrudos.
C++

} } 13
C++

14

Os objectos podem ser definidos s quando so precisos


int int main(void) main(void) {{ int int x; x; int int y; y; Point Point p; p; double double d; d; printf("Duas printf("Duas coordenadas:\n"); coordenadas:\n"); scanf("%d%d", scanf("%d%d", &x, &x, &y); &y); p.Display(); p.Display(); Definies no meio p.Set(x, p.Set(x, y); y); das instrues p.Display(); p.Display(); Point Point origin; origin; dd == p.DistanceTo(origin); p.DistanceTo(origin); printf("Dist printf("Dist 'a 'a origem: origem: %lf\n", %lf\n", d); d); p.Move(3, 7); p.Move(3, 7); p.Display(); p.Display(); Point Point q; q; qq == p; p; q.Display(); q.Display(); return return 0; 0;

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.

}}

Outro exemplo tpico:


for(int for(int i=0; i=0; ii << n; n; i++) i++) {{ ... ... }} C++

Escrever tambm drivers para testar as classes. 15


C++

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

Programao orientada pelos objectos


1. perfeitamente trivial. 2. pena no funcionar. 3. C eu, j fao isso h imenso tempo. O que a programao orientada pelos objectos (OOP)? OOP OOP um ummtodo mtodode deimplementao implementaosesegundo o qual os programas gundo o qual os programasso soorganizados organizados na naforma formade decoleces colecesde deobjectos objectosque que cooperam uns com os outros, cooperam uns com os outros,cada cadaum umdesdesses objectos sendo uma instncia de alguma ses objectos sendo uma instncia de alguma classe, classe,e eestando estandoas asclasses classesintegradas integradas numa hierarquia de classes numa hierarquia de classesque quedefine define relaes deumas umaspara paraoutras. outras. relaesde deherana heranade (Booch) (Booch) Conceitos chave: objecto classe hierarquia herana
C++

O que se diz da OOP


Os Osobjectivos objectivosda daOOP OOPso somelhorar melhorara a produtividade dos programadores, produtividade dos programadores,pelo pelo aumento aumentoda daextensibilidade extensibilidadee ea areutilizareutilizao do software e pelo controlo da o do software e pelo controlo dacomcomplexidade plexidadee edo docusto custoda damanuteno. manuteno. (R. (R.Wiener) Wiener) Os OsProgramadores ProgramadoresOO OOprecisam precisamde detanta tanta preparao como os pilotos dos avies. preparao como os pilotos dos avies. (T. (T.Love) Love) A Aprogramao programaocom comobjectos objectos uma umamumudana revolucionria na Programao dana revolucionria na Programao mais maisrevolucionria revolucionriaque quea amudana mudanado do assembler para as linguagens assembler para as linguagensde deprograprogramao. mao.(P. (P.Coad, Coad,J. J.Nicola) Nicola) Qual Qual o omaior maiorobstculo obstculoque quese seope opea a uma mais vasta aceitao da programao uma mais vasta aceitao da programao com comobjectos? objectos?Primeiro, Primeiro,o oexagero exageroe ea a fantasia dos fanticos OOP (J. Coggins) fantasia dos fanticos OOP (J. Coggins)

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(?) (?)

Outras operaes? Por exemplo: escrever o valor? ver se atingiu o mximo?

s vezes, para descobrir as classes, procuram-se os substantivos


C++

21

C++

22

Classe Count (implementao)


#include #include "count.hpp" "count.hpp" Count::Count(void) Count::Count(void) {{ value value == resetValue resetValue == 0; 0; }} void void Count::Init(int Count::Init(int n) n) {{ value value == resetValue resetValue == n; n; }} void void Count::Inc(void) Count::Inc(void) {{ value++; value++; }} void void Count::Dec(void) Count::Dec(void) {{ value; value; }} void void Count::Reset(void) Count::Reset(void) {{ value value == resetValue; resetValue; }} int int Count::Value(void) Count::Value(void) {{ return return value; value; }}

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?

Como que isto se usa?


C++

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. }; };

Outras operaes: eliminar um comando. 25 26

C++

C++

Classe Menu (implementao)


#include #include "menu.hpp" "menu.hpp" #include <stdio.h> #include <string.h> <stdio.h> #include #include <string.h> Menu::Menu(void) {Menu::Menu(void) { size = 0; Valores por defeito size = 0; strcpy(label, "LABEL"); strcpy(label, "LABEL"); strcpy(prompt, "PROMPT"); }} strcpy(prompt, "PROMPT"); void Menu::Init(char Menu::Init(char *label0, *label0, char char *prompt0) *prompt0) {void { strcpy(label, label0); strcpy(label, label0); strcpy(prompt, prompt0); }} strcpy(prompt, prompt0); void Menu::AddItem(char Menu::AddItem(char *item) *item) {void { strcpy(items[size++], item); }} strcpy(items[size++], item); void Menu::Select(int Menu::Select(int *n) *n) {void { char line[80]; char line[80]; int int i; i; gets(line); gets(line); for == size -- 1; for (i (i size 1; ii >= 00 && *items[i]!=*line; >= && *items[i]!=*line; i) ;; i) *n = i + 1; }} *n = i + 1; void Menu::Display(void) Menu::Display(void) {void { int i; int i; printf("%s\n", label); printf("%s\n", label); for (i = 0; i < size; ++i) for (i = 0; i < size; ++i) printf("%s\n", items[i]); printf("%s\n", prompt); items[i]); C++ printf("\n%s\n", }} printf("\n%s\n", prompt);

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

}} ... ... C++

30

Membros que so classes


Parece que a funo DoCommand devia pertencer classe Menu. Mas ento o objecto tinha que ter acesso ao contador. Mas, neste caso, DoCommand devia ficar privado, pois devamos ter uma funo DoMenu que tratava de tudo! Precisvamos de uma coisa assim:
const const int int MAXITEMS MAXITEMS == 10; 10; Casos de classes com class membros que so clasclass Menu Menu {{ private: ses j tinham aparecido private: char label[32]; com os Segmentos e os char label[32]; char char items[MAXITEMS] items[MAXITEMS] [32]; [32];Tringulos int int size; size; char char prompt[32]; prompt[32]; Count Count itsCount; itsCount; Uma funo privada void void DoCommand(int); DoCommand(int); public: public: Menu(void); Menu(void); void void Init(char Init(char *, *, char char *, *, int); int); void void AddItem(char AddItem(char *); *); void void Select(int Select(int *); *); void Display(void); Para o valor inicial void Display(void); void do contador void DoMenu(void); DoMenu(void); }; };

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); }; };

O membro quit para ver se o comando "quit" foi seleccionado.

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

Mas podemos fazer melhor, com o mecanismo da herana. 31 C++

CountMenu tem-um (has-a) Count;

32

Exemplo dos pontos com cor


Suponhamos que precisamos de pontos coloridos. Como j temos a classe Point, a nova classe ColoredPoint obtm-se por derivao:
class class ColoredPoint: ColoredPoint: Point Point {{ private: private: unsigned unsigned char char color; color; public: public: ColoredPoint(void); ColoredPoint(void); Paint(unsigned Paint(unsigned char); char); Display(void); Display(void); }; }; ColoredPoint::ColoredPoint(void) ColoredPoint::ColoredPoint(void) {{ color color == 0; 0; }} ColoredPoint::Paint(unsigned ColoredPoint::Paint(unsigned char char c) c) {{ Qualificao do memcolor color == c; c; bro Display, da classe }} ColoredPoint. Sem qualificao tnhamos ColoredPoint::Display(void) ColoredPoint::Display(void) uma recursividade {{ infinita Point::Display(); Point::Display(); printf("The color is: %d", color); printf("The color is: %d", color); }} C++

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++

Classes de base pblicas, privadas


Um ponto colorido pode ser movido? Isto , podemos ter ... ...
ColoredPoint ColoredPoint cp; cp; ... ... cp.Move(2, cp.Move(2, 5); 5); ... ...

Afectao entre a classe de base e a classe derivada


Pode-se? Sim, da classe derivada para a de base, se a de base for pblica:
class class ColoredPoint: ColoredPoint: public public Point Point {{ private: private: ... ... public: public: ... ... }; }; int int main main (void) (void) {{ ColoredPoint ColoredPoint cp; cp; Point Point p; p; p.Set(2, p.Set(2, 5); 5); cp.Set cp.Set (3, (3, 7); 7); p.Display(); p.Display(); printf("\n"); printf("\n"); cp.Display(); cp.Display(); printf("\n"); printf("\n"); pp == cp; cp; p.Display(); p.Display(); printf("\n"); printf("\n");

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

Classes abstractas: DisplayBox


Certas classes so feitas sobretudo para servirem de base a outras. Por exemplo, uma DisplayBox, que depois seria herdada para fazer DisplayBoxes de inteiros, de strings, etc.
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 void Display(void); Display(void); }; }; DisplayBox::DisplayBox(void) DisplayBox::DisplayBox(void) {{ *message *message == 0; 0; }} void void DisplayBox::Init(char DisplayBox::Init(char *s) *s) {{ strcpy(message, s); strcpy(message, s); }} void void DisplayBox::Display(void) DisplayBox::Display(void) {{ printf("%s", printf("%s", message); message); }}

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); }; };

Usa-se as funes da classe de base, quando preciso:


CountDisplayBox::CountDisplayBox(void) CountDisplayBox::CountDisplayBox(void) {{ O construtor fica em branco }} void void CountDisplayBox::Init(void) CountDisplayBox::Init(void) {{ Inicializao da classe itsCount.Init(0); itsCount.Init(0); membro e da classe DisplayBox::Init("Valor: DisplayBox::Init("Valor: "); "); derivada. }} void void CountDisplayBox::Display(void) CountDisplayBox::Display(void) {{ DisplayBox::Display(); DisplayBox::Display(); printf("%s\n", printf("%s\n", itsCount.Value()); itsCount.Value()); }} C++

Assim s, parece que no serve para grande coisa


C++

39

40

Problemas com a relao tem-um


A CountDisplayBox tem um contador, o CountMenu tambm, mas devia ser o mesmo 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); }; }; Como fazer com que os dois membros sejam o mesmo? class CountMenu: public Menu { class CountMenu: public Menu { private: private: Count Usando aponCount itsCount; itsCount; int tadores, claro! int quit; quit; void DoCommand(int); void 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); }; };

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++

Diz-se que a classe CountDisplayBox conhece-um (knows-a) Count.

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); }; };

Classe CountMenu (cont.)


... ... void void CountMenu::DoMenu(void) CountMenu::DoMenu(void) {{ int int command; command; Display(); Display(); Select(&command); Select(&command); DoCommand(command); DoCommand(command); }} void void CountMenu::DoCommand(int CountMenu::DoCommand(int n) n) {{ switch(n){ switch(n){ case case 1: 1: itsCount->Inc(); itsCount->Inc(); break; break; case case 2: 2: itsCount->Dec(); itsCount->Dec(); break; break; case case 3: 3: itsCount->Reset(); itsCount->Reset(); break; break; case case 5: 5: quit=1; quit=1; break; break; default: default: break; break; }} }} int int CountMenu::Quit(void) CountMenu::Quit(void) {{ return return quit; quit; }}

Esta funo chama outras funes do mesmo objecto.

O resto tambm parecido:


CountMenu::CountMenu(void) CountMenu::CountMenu(void) {{ itsCount itsCount == 0; 0; quit quit == 0; 0; }} Apontadores

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 }}

int CountMenu::Quit(void) CountMenu::Quit(void) {int { return this->quit; C++}} return this->quit;

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:

A aplicao tambm uma classe?


O que que caracteriza uma classe? As operaes que se podem fazer com os objectos dessa classe. Ento, que tal esta classe?
class class MyApplication MyApplication {{ private: private: Count Count myCount; myCount; CountMenu CountMenu myMenu; myMenu; CountDisplayBox CountDisplayBox myDisplayBox; myDisplayBox; void void Initiate(void); Initiate(void); int int Continue(void); Continue(void); void void ...::Run(void) ...::Run(void) void void Operate(void); Operate(void); {{ void Terminate(void); void Terminate(void); Initiate(); Initiate(); public: public: while(Continue()) while(Continue()) MyApplication(void); MyApplication(void); Operate(); Operate(); void Run(void); void Run(void); Terminate(); Terminate(); }; }; }}

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++

Classes abstractas, de novo


Haver uma sobreclasse til para a classe MyApplication? Dificilmente, pois cada aplicao ter os seus dados, a sua inicializao, continuao, etc. Mas, precisamente, recolhamos a essncia de todas as aplicaes nessa sobreclasse:
class class Application Application {{ private: private: virtual virtual void void Initiate(void); Initiate(void); virtual int virtual int Continue(void); Continue(void); virtual virtual void void Operate(void); Operate(void); virtual virtual void void Terminate(void); Terminate(void); public: public: Application(void); Application(void); virtual virtual void void Run(void); Run(void); }; };

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.

Estas no fazem nada, por defeito!

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

Funes virtuais puras


Podemos especificar que uma funo virtual tem que ser ultrapassada, declarando-a pura, assim:
class class Application Application {{ private: private: virtual virtual void void Initiate(void) Initiate(void) == 0; 0; virtual int Continue(void) virtual int Continue(void) == 0; 0; virtual virtual void void Operate(void) Operate(void) == 0; 0; virtual void Terminate(void) virtual void Terminate(void) == 0; 0; public: public: Application(void); Application(void); virtual virtual void void Run(void); Run(void); }; };

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

Derivando a classe Application


A classe de que precisamos assim:
#include #include "appl.hpp" "appl.hpp" #include #include "count.hpp" "count.hpp" #include #include "cmenu.hpp" "cmenu.hpp" #include #include "cdbox.hpp" "cdbox.hpp" classe Application classe Count classe CountMenu classe CountDisplayBox

As funes da classe derivada


Ficam assim:
CountApplication::CountApplication(void) {CountApplication::CountApplication(void) }{ } void CountApplication::Initiate(void) CountApplication::Initiate(void) {void { itsCount.Init(0); itsCount.Init(0); itsDisplayBox.Init(&itsCount); itsDisplayBox.Init(&itsCount); itsMenu.Init(&itsCount); }} itsMenu.Init(&itsCount); int CountApplication::Continue(void) CountApplication::Continue(void) {int { return !itsMenu.Quit(); }} return !itsMenu.Quit(); void CountApplication::Operate(void) CountApplication::Operate(void) {void { itsDisplayBox.Display(); itsDisplayBox.Display(); itsMenu.DoMenu(); }} itsMenu.DoMenu(); void CountApplication::Terminate(void) CountApplication::Terminate(void) {void { printf("Muito obrigado.\n"); }} printf("Muito obrigado.\n");

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:

Estrutura do programa completo


DisplayBox Count Menu

#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++

Nota: estes smbolos no pertencem a nenhuma metodologia especial.

Application B tem um membro que um objecto da classe A (B has-a A). 56

A
C++

55

Evitando a redeclarao de classes


Por vezes, em programas com uma estrutura complexa, acabamos por incluir (indirectamente) num mesmo ficheiro vrias vezes o mesmo ficheiro de headers. Se este ficheiro de headers contiver declaraes de classes, isso vai causar problemas pois cada classe s pode ser declarada uma vez. Para evitar esses problemas, recorre-se ao preprocessador, assim, por exemplo:
#ifndef #ifndef _H_Count _H_Count #define #define _H_Count _H_Count 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); }; }; #endif #endif C++

Funes na linha (inline functions)


Quando as funes so pequeninas, mais prtico defini-las logo na declarao da classe:
class class CountApplication: CountApplication: public public Application Application {{ private: private: Count Count itsCount; itsCount; CountMenu CountMenu itsMenu; itsMenu; CountDisplayBox CountDisplayBox itsDisplayBox; itsDisplayBox; void void Initiate(void); Initiate(void); int int Continue(void){return Continue(void){return !itsMenu.Quit();} !itsMenu.Quit();} void void Operate(void) Operate(void) {{ itsDisplayBox.Display(); itsDisplayBox.Display(); itsMenu.DoMenu(); itsMenu.DoMenu(); }} A funo Continue a funo Operate, e void Terminate(void); void Terminate(void); o construtor j no public: public: aparecem no CountApplication(void){} CountApplication(void){} ficheiro .cpp. }; };

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

Classe Count, revisitada


Muitas das funes que j apareceram deveriam ter sido definidas dentro da declarao da classe. Por exemplo, na classe Count:
class class Count Count {{ private: private: int int value; value; int int resetValue; resetValue; public: public: Count(void){value Count(void){value == resetValue resetValue == 0;} 0;} void Init(int n){value void Init(int n){value == resetValue resetValue == n;} n;} void void Inc(void){value++;} Inc(void){value++;} void void Dec(void){value;} Dec(void){value;} void void Reset(void){value Reset(void){value == resetValue;} resetValue;} int int Value(void){return Value(void){return value;} value;} }; };

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);

Tambm h um construtor sem parmetros. Por isso, a seguinte declarao


Complex Complex w; w;

Funes camaradas (friend functions) C++

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;} ... ... }; };

Argumentos por defeito


Por vezes consegue evitar-se a proliferao de funes anlogas, usando valores por defeito para os argumentos. Por exemplo, um nico construtor Complex faria o servio dos trs:
class class Complex Complex {{ private: private: ... ... public: public: Complex(double Complex(double rr == 0.0, 0.0, double double ii == 0.0) 0.0) {re = r; im = i;} {re = r; im = 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

Inicializao vs. afectao


Um dos construtores chamado quando o objecto criado, para ficar inicializado logo. Por exemplo:
... ... Complex Complex zz (3.0, (3.0, 4.0); 4.0); Complex Complex aa == 12; 12; ... ...

Sobrecarga de funes (2)


A funo Print tambm est sobrecarregada:
class class Complex Complex {{ ... ... void void Print(void); Print(void); void void Print(FILE Print(FILE *); *); ... ... }; };

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)); }}

Declarar sem inicializar e fazer uma afectao depois outra coisa:


... ... Complex Complex z; z; Complex Complex a; a; ... ... zz == Complex(3.0, Complex(3.0, 4.0): 4.0): aa == 12; 12; Aqui, usa-se o construtor sem parmetros e as variveis ficam a zero. Aqui, cria-se um objecto Complex temporrio, annimo, e copia-se para z.

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++

Inicializao das classes membro


Quando uma classe tem membros que so de outras classes, ao inicializ-la tem que se pensar em inicializar as classes membro.
class class Point{ Point{ private: private: int int xx; xx; int int yy; yy; public: public: Point(void); Point(void); Point(int Point(int x, x, int int y){xx y){xx == x; x; yy yy == y;} y;} ... ... }; }; ... ... class class Triangle Triangle {{ private: private: Point Ateno a esta sintaxe: Point p1; p1; Point Point p2; p2; X(, ):, {} Point Point p3; p3; public: public: Triangle(void); Triangle(void); Triangle(int Triangle(int x1, x1, int int y1, y1, int int x2, x2, int int y2, y2, int int x3, x3, int int y3): y3): p1(x1, p1(x1, y1), y1), p2(x2, p2(x2, y2), y2), p3(x3, p3(x3, y3){} y3){} ... ... }; }; C++ Construtores da classe Point

Inicializao das classes membro (2)


Na classe Triangle, haveria vrios construtores:
class class Triangle Triangle {{ private: private: Point Point p1; p1; Point Point p2; p2; Point Point p3; p3; public: public: Triangle(void); Triangle(void); Triangle(int Triangle(int x1, x1, int int y1, y1, int x2, int int x2, int y2, y2, int int x3, x3, int int y3): y3): p1(x1, p1(x1, y1), y1), p2(x2, p2(x2, y2), y2), p3(x3, p3(x3, y3){} y3){} Triangle(Point a1, Point a2, Point Triangle(Point a1, Point a2, Point a3): a3): p1(a1), p1(a1), p2(a2), p2(a2), p3(a3) p3(a3) {{ }} }; }; ... ...

Curiosamente, o terceiro construtor poderia ser programado assim:


class class Triangle Triangle {{ Triangle(Point Triangle(Point p1, p1, Point Point p2, p2, Point Point p3): p3): p1(p1), p2(p2), p3(p3) p1(p1), p2(p2), p3(p3) {{ }} }; }; C++ ... ...

67

68

Inicializao das classes membro (3)


Nestes exemplos, esta nova forma de inicializao seria dispensvel, pois poderamos recorrer a afectaes. Exemplo:
class class Date Date {{ private: private: int int year; year; int int month; month; int int day; day; public: public: Date(){year Date(){year == 1901, 1901, month month == 1, 1, day day == 1;} 1;} Date(int y, int m, int d) Date(int y, int m, int d) {year {year == y, y, month month == m; m; day day == d;} d;} int Before(Date, Date); int Before(Date, Date); ... ... }; };

Inicializao das classes membro (4) caso dos membros constantes


Um membro declarado const constante e o seu valor no pode mudar. Logo no pode aparecer esquerda do operador =. Exemplo:
class class Person Person {{ private: private: char char name[32]; name[32]; O nome pode const const int int idNumber; idNumber; mudar, o BI no! Date Date born; born; public: public: Para o Person(int Person(int idNumber, idNumber, char char *s *s == 0): 0): membro born idNumber(idNumber) idNumber(idNumber) usa-se o {{ construtor if (s) strcpy(name, s); if (s) strcpy(name, s); Date(void). else else *name *name == 0; 0; }} Person(int Person(int idNumber, idNumber, char char *s, *s, Date Date t): t): idNumber(idNumber), born(t) idNumber(idNumber), born(t) {strcpy(name, {strcpy(name, s);} s);} ... ... Aqui tambm no se podia fazer }; }; Person(): , name(s), { }

Mas ficaria melhor assim:


class class Date Date {{ ... ... public: public: Date(): Date(): year(1901), year(1901), month(1), month(1), day(1) day(1) {{ }} Date(int y, int m, int d): Date(int y, int m, int d): year(y), year(y), month(m), month(m), day(d) day(d) {{ }} int int Before(Date, Before(Date, Date); Date); ... ... }; };

Neste caso no se podia escrever:

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.

Inicializao da classe de base


Ao inicializar uma classe derivada, tem que se pensar em inicializar a classe de base. Faz-se como para as classes membro. Exemplo:
class class Student: Student: public public Person Person {{ private: private: int int tuition; tuition; public: public: Student(void); Student(void); Student(int Student(int id, id, char char *s, *s, Date Date t): t): Person(id, s, t), tuition(1000) Person(id, s, t), tuition(1000) {{ }} void void Pay(int Pay(int x) x) {tuition {tuition -= -= x;} x;} ... ... }; };

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);

}; };

O primeiro como uma funo-membro:


Complex Complex Complex::operator Complex::operator -(void) -(void) {{ return return Complex(-re, Complex(-re, -im); -im); }}

No obrigatrio definir o construtor na declarao da classe. Podia ficar para depois:


class class Student: Student: public public Person Person {{ ... ... Student(int, Student(int, char char *, *, Date); Date); void void Pay(int Pay(int x) x) {tuition {tuition -= -= x;} x;} ... ... }; }; Student::Student(int Student::Student(int id, id, char char *s, *s, Date Date t): t): Person(id, s, t), tuition(1000) Person(id, s, t), tuition(1000) {{ }} C++

Usa-se como seria de esperar:


Complex Complex z, z, a; a; ... ... zz == a; a;

Mas podia usar-se como uma funo-membro de nome operator :


zz == a.operator a.operator (); (); C++

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); }} ... ...

Funes camaradas (2)


Pode haver funes camaradas que no so operadores, claro. Eis um exemplo na classe Point:
class class Point{ Point{ private: private: int int xx; xx; int int yy; yy; public: public: ... ... double double DistanceTo(Point); DistanceTo(Point); friend friend double double Distance(Point, Distance(Point, Point); Point); ... ... }; }; double double Distance(Point Distance(Point p1, p1, Point Point p2) p2) {{ return return sqrt(sqr(p1.xx-p2.xx)+ sqrt(sqr(p1.xx-p2.xx)+ sqr(p1.yy-p2.yy)); sqr(p1.yy-p2.yy)); }}

A funo-membro chama-se assim:


...p1.DistanceTo(p2)... ...p1.DistanceTo(p2)... Reflexo OO.

As funes camaradas no esto associadas a nenhum objecto da classe. Por isso nunca so chamadas na forma z.f(). 73 C++

e a funo camarada assim:


...Distance(p1, ...Distance(p1, p2)... p2)... C++ Reflexo funcional.

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

Classe String Construtores


H um construtor sem parmetros, um com um parmetro inteiro, para reservar espao, um com um parmetro char *, para inicializar com uma cadeia, e o construtor de cpia, para inicializar com uma cpia de outra String. Operador new: aloca espao para

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

Passagem por referncia &


Em C++, h dois modos de passagem de argumentos: por valor (como em C) e por referncia (como os parmetros var em Pascal). A passagem por referncia usa-se quando, ou se quer modificar o valor do argumento, ou o argumento muito volumoso e no se justifica a cpia (mesmo se no quisermos modific-lo). No quarto construtor, o argumento passado por referncia, e, como o seu valor no vai ser modificado, especifica-se que constante:
class class String String {{ ... ... String(const String(const String&) String&) {p {p == strcpy(new strcpy(new char[size=s.size], char[size=s.size], s.p);} s.p);} ... ...

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 no houvesse este construtor a situao


... ... String String s1 s1 == "abcd"; "abcd"; String s2 String s2 == s1; s1; ... ...

~String(void){delete ~String(void){delete p;} p;} ... ...

resultava na cpia, membro a membro, de s1 para s2. 79 C++

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); }}

Se a afectao for da forma s = s, no podemos libertar a memria

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; }}

Se no houvesse este construtor a seguinte situao


void void f(void) f(void) {{ String String s1("lkjhg"); s1("lkjhg"); String String s2 s2 == s1; s1; ... ... }}

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

Isto uma inicializao, no uma afectao. (O objecto s2 nem est inicializado)

era problemtica: a inicializao de s1 feita pelo terceiro construtor, mas a de s2 seria feita por cpia membro a membro. 84

83

C++

X(const X&) X& operator =(const X&) ~X(void)


Quando uma classe X reserva e liberta memria, ela precisa de um construtor de cpia e de um operador de afectao, para evitar a cpia membro a membro. Em esquema:
class class XX {{ private: private: ... Isto muito habitual ... public: public: X(outroTipo); X(outroTipo); X(const X(const X&); X&); X& X& operator operator =(const =(const X&); X&); ~X(void); ~X(void); ... ... }}

Cpia na passagem por valor e no retorno de funes


Os construtores so usados na passagem por valor, e no retorno de funes. Quando um argumento passado a uma funo, ele vai inicializar a varivel que representa o parmetro respectivo. Em certas situaes, tambm preciso copiar o valor retornado (argumento do return) para inicializar uma varivel temporria, que utilizada no local da chamada da funo. Exemplo:
String String Paren(String Paren(String s) s) {{ String String w; w; ww == "(" "(" ++ ss ++ ")"; ")"; return return w; w; }} Concatenao de Strings. Definida frente, como exerccio. Com tantas inicializaes e variveis temporrias, h por aqui muitas chamadas de construtores, e depois de destrutores

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.

O objecto x copiado, inicializando s (parmetro da funo).

86

Passagem por referncia, constante


A passagem por referncia constante uma coisa boa: no se perde tempo a copiar o argumento e no se corre o risco de, inadvertidamente, modificar o objecto. Alm disso, se se omitisse a especificao const perdia-se funcionalidade. Objectos constantes deixariam de poder ser usados como argumento.
String& String& String::operator String::operator == (String& (String& s) s) {{ Afectao errada: falta const. ... ... ... ... const const String String s1 s1 == "coimbra"; "coimbra"; String String s2; s2; ... Erro: "const String" ... s2 cannot be converted to s2 == s1; s1; ... "String&". ...

Usar const, sempre que possvel


O qualificador const serve para indicar que o objecto nunca vai ser modificado (aps a inicializao).
... ... const const String String s1 s1 == "coimbra"; "coimbra"; const const int int kk == 1024; 1024; ... ...

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 *p *p == "lisboa"; "lisboa";

Perdia-se tambm a possibilidade de fazer algumas converses implcitas.


... ... Erro: Conversion from s2 "char *" to a reference to a s2 == "porto"; "porto"; ... non-const type "String&" ... s2 requires a temporary. s2 == "(" "(" ++ s1 s1 ++ ")"; ")"; ... ... Erro: Conversion from 87 "String" to a reference to a non-const type "String&" requires a temporary. char char ** const const pp == "lisboa"; "lisboa";

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.

Uma implementao mais cuidadosa verificaria se o ndice aceitvel Utilizao:


String String s1 s1 == "porto"; "porto"; char c; char c; ... ... cc == s1[2]; // s1[2]; // 'r' 'r' s1[1] = 'e'; // s1[1] = 'e'; // "perto" "perto" ... ... Tambm se podia cc == s1.operator[](4); s1.operator[](4); usar a notao s1.operator[](4) = 'x'; s1.operator[](4) = 'x'; funcional. ... ... C++

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

Erro: Assigment to a constant expression is not allowed.

90

Diferena entre funes-membro e funes camaradas


A comparao, por exemplo, podia ter sido uma funo membro, definida assim:
class class String String ... ... int int operator operator == == (const (const String& String& s) s) {return strcmp(p, s.p) {return strcmp(p, s.p) == == 0;} 0;} ... ...

Converso de String para char*


Para converter uma String para um char* pode criar-se uma cpia da cadeia e devolver um apontador para ela; ou ento devolver um apontador para a prpria cadeia, desde que esse apontador aponte para uma constante. Consegue-se isso com o operador char *():
class class String String ... ... operator operator const const char char *() *() const const {return {return p;} p;} ... ...

Ento, quando se escrevesse, por exemplo


if if (s1 (s1 == == "12345") "12345") ... ...

era o mesmo que


if if (s1.operator (s1.operator ==("12345")) ==("12345")) ... ...

Agora pode fazer-se:


String String s1("aveiro"); s1("aveiro"); const const String String s2 s2 == "mirandela"; "mirandela"; const char *cp; const char *cp; ... Converses implcitas ... cp cp == s1; s1; ... ... cp cp == s2; s2; ... ... cp Converso explcita cp == (const (const char char *) *) s1; s1;

e por isso nunca se poderia escrever


if if ("12345" ("12345" == == s1) s1) ... ... Erro: the "==" operator is not allowed between "char*" and "String".

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;

E s vezes esquecemo-nos do operador &:


int int main main (void) (void) {{ int int n; n; ... ... scanf("%d", scanf("%d", n); n); ... ...

em C++ faz-se assim:


#include #include <iostream.h> <iostream.h> operador << (put to) int int main() main() {{ int int xx == 142; 142; cout cout << << "x "x == "" << << xx << << endl; endl; }} return return 0; 0; Tecnicamente, endl um modificador. Podamos ter usado o habitual '\n'.

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*); ... ... }}

em C++ faz-se assim:


#include #include <iostream.h> <iostream.h> operador >> (get from) int int main() main() {{ int int x; x; cout cout << << "Qual "Qual oo nmero? nmero? "; "; cin >> x; cin >> x; cout cout << << "O "O nmero nmero : : "" << << xx << << endl; endl; return 0; return 0; }} C++

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

<< para char* C++

<< para int

97

Acabaram-se as preocupaes com o & no scanf!

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.

Para ecoar cadeias, era quase a mesma coisa:


#include #include <iostream.h> <iostream.h> #include #include <stdio.h> <stdio.h> int int main() main() {{ char char x[80]; x[80]; while while (cin (cin >> >> x) x) cout << x cout << x << << endl; endl; }} C++ return return 0; 0; Aqui, como no caso anterior, ignora-se os espaos em branco.

C++

Mas, se houver azar na leitura, devolve-se zero!

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.

Escrevendo e lendo Strings


As classes cujos objectos so escrevveis e legveis, devem dispor de funes camaradas sobrecarregando os operadores << e >> para tratar do output e do input dos seus objectos. Na classe String ficava assim:
class class String String ... ... friend friend ostream& ostream& operator operator << << (ostream&, (ostream&, const const String&); String&); friend friend istream& istream& operator operator >> >> (istream&, (istream&, String&); String&); ... ...

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

Ainda faltam muitas coisas


Por exemplo: Classes Classesgenricas genricas(templates) (templates) Excepes Excepes Herana Heranamltipla mltipla

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

Herana pblica significa -um


A regra mais importante da programao orientada por objectos em C++ : Herana pblica significa -um Quando se escreve que uma classe D herda publicamente de B, est-se a dizer (ao compilador e ao leitor) que cada objecto de tipo D tambm um objecto de tipo B, mas no ao contrrio. Tudo o que for aplicvel a um objecto de tipo B tambm aplicvel a um objecto de tipo D, mas no ao contrrio. Exemplos:
class class Person Person {{ ... ... }; }; class class Student: Student: public public Person Person {{ ... ... }; }; class class Vehicle Vehicle {{ ... ... }; }; class Car: public class Car: public Vehicle Vehicle {{ ... ... }; }; class Truck: public Vehicle { class Truck: public Vehicle { ... ... }; }; class class Polygon Polygon {{ ... ... }; }; class class Triangle: Triangle: public public Polygon Polygon {{ ... ... }; }; class class Recording Recording {{ ... ... }; }; class CompactDisk: class CompactDisk: public public Recording Recording {{ ... ... }; }; class public class LPRecord: LPRecord: public Recording Recording {{ ... ... }; }; class public class Cassete: Cassete: public Recording Recording {{ ... ... }; }; C++

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.

e portanto tambm danam


Uma funo que espere um argumento de tipo Person (ou Person& ou Person*) aceita argumentos que sejam de tipo Student (ou Student& ou Student*):
void void Dance1(Person Dance1(Person p) p) {{ cout << p.Name() << cout << p.Name() << "" is is dancing." dancing." << << endl; endl; }} void void Dance2(Person Dance2(Person &p) &p) {{ cout cout << << p.Name() p.Name() << << "" is is dancing." dancing." << << endl; endl; }} void void Dance3(Person Dance3(Person *p) *p) {{ cout cout << << p->Name() p->Name() << << "" is is dancing." dancing." << << endl; endl; }} Nota sobre conflitos de sobrecarga de funes: se a funo Dance2 se chamasse Dance1 teramos um conflito de sobrecarga. O erro seria: Function overloading conflict between "void(Person)" and "void(Person&)". J se fosse Dance3 a chamar-se Dance1, tudo bem.

C++

112

Pessoas e estudantes danando


Exemplo:
int int main(void) main(void) {{ Person Person p("Carlos", p("Carlos", 'M'); 'M'); Person* Person* rp rp == &p; &p; Dance1(p); Dance1(p); Dance2(p); Dance2(p); Dance3(rp); Dance3(rp); p.Dance(); p.Dance(); rp->Dance(); rp->Dance(); Student Student s("Ana", s("Ana", 'F', 'F', "FCT/UNL"); "FCT/UNL"); Student* rs = &s; Student* rs = &s; Dance1(s); Dance1(s); Dance2(s); Dance2(s); Dance3(rs); Dance3(rs); s.Dance(); s.Dance(); rs->Dance(); rs->Dance(); }} ... ...

Nem todas as pessoas so estudantes


Exemplo:
void void Study1(Student Study1(Student p) p) {{ cout cout << << p.Name() p.Name() << << "" is is studying." studying." << << endl; endl; }} void void Study2(Student Study2(Student &p) &p) {{ cout cout << << p.Name() p.Name() << << "" is is studying." studying." << << endl; endl; }} void void Study3(Student Study3(Student *p) *p) {{ cout cout << << p->Name() p->Name() << << "" is is studying." studying." << << endl; endl; }}

Os estudantes estudam, mas as pessoas no:


Ateno: o objecto s, de tipo Student, copiado implicitamente para um objecto temporrio de tipo Person, no seio da funo Dance1, pois a passagem de argumento feita por valor. int int main(void) main(void) {{ ... ... Study1(s); Study1(s); Study2(s); Study2(s); Study3(rs); Study3(rs); s.Study(); s.Study(); rs->Study(); rs->Study(); ... ... }} 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(); ... ... }} Todas estas instrues do erro de compilao

C++

113

C++

114

Slicing problem (1)


Normalmente h vantagem em passar os parmetros por referncia. Poupa-se na cpia do argumento e evita-se o slicing problem: Quando um objecto de uma classe derivada transformado num objecto da classe de base, perde as suas caractersticas especializadas (o que nem sempre o que pretendemos). Exemplo:
class class Person Person {{ ... ... virtual virtual void void Display(void); Display(void); virtual const virtual const char* char* Class(void) Class(void) const const {return "Person";} {return "Person";} }; }; Funo de void identificac void Person::Display(void) Person::Display(void) {{ o da classe cout cout << << name name << << ", ", "" << << sex; sex; }} class class Student: Student: public public Person Person {{ ... ... virtual virtual void void Display(void); Display(void); virtual virtual const const char char *Class(void) *Class(void) const const {return "Student";} {return "Student";} }; }; void void Student::Display(void) Student::Display(void) {{ Person::Display(); Person::Display(); cout cout << << ", ", "" << << school; school; }} C++

Slicing problem (2)


Qual a diferena entre estas duas funes?
void void ShowPerson1(Person ShowPerson1(Person p) p) {{ cout cout << << p.Class() p.Class() << << ": ": "; "; p.Display(); p.Display(); cout << endl; void ShowPerson2(Person cout << endl; void ShowPerson2(Person &p) &p) }} {{ cout << p.Class() << ": cout << p.Class() << ": "; "; p.Display(); p.Display(); cout cout << << endl; endl; }}

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".

Todos podemos ser estudantes


Converte-se de Person para Student com um construtor apropriado:
class class Student: Student: public public Person Person {{ private: private: String String school; school; int int year; year; public: public: Student Student (Person (Person p): p): Person(p), Person(p), school("LIFE") school("LIFE") {{ }} ... ... }; };

Agora a chamada outras no.


... ... Study2(p); Study2(p); Study3(rp); Study3(rp); p.Study(); p.Study(); rp->Study(); rp->Study(); ... ...

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

Comparando com a herana privada


Se Student herdasse privadamente de Person funes que esperassem argumentos de tipo Person (ou Person& ou Person*) no aceitariam argumentos de tipo Student (ou Student& ou Student*):
class class Person Person {{ ... ... }; }; class class Student: Student: private private Person Person {{ ... ... }; }; Private int int main(void) main(void) {{ ... ... Dance1(s); Dance1(s); Dance2(s); Dance2(s); Dance3(rs); Dance3(rs); s.Dance(); s.Dance(); rs->Dance(); rs->Dance(); ... ... }} Erro: "Person" is a private base class of "Student".

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

OK: um pinguim uma ave! Mas os pinguins no voam

Erro: private member "Person::Dance() const" cannot be accessed.

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++

Pssaros que no voam (1)


Para evitar que os pinguins voem, cria-se uma classe de aves voadoras e outra de aves no voadoras:
class class Bird Bird {{ private: private: public: public: }; }; class class FlyingBird: FlyingBird: public public Bird Bird {{ private: private: public: public: virtual virtual void void Fly(); Fly(); }; }; class class NonFlyingBird: NonFlyingBird: public public Bird Bird {{ private: private: public: public: }; }; class class Penguin: Penguin: public public NonFlyingBird NonFlyingBird {{ private: private: public: public: }; };

Pssaros que no voam (2)


Outra maneira de evitar que os pinguins voem:
void void Error(char Error(char *); *); 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: virtual virtual void void Fly(void) Fly(void) {{ Error("Penguins Error("Penguins can't can't fly"); fly"); }} }; };

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++

Agora isto est errado:


... ... Penguin Penguin p; p; ... ... p.Fly(); p.Fly(); ... ... C++ Erro: "Fly" is not a member of "Penguin".

121

122

Pssaros, pssaros, pssaros (1)


H aves que voam e aves que no voam, aves que so comestveis e aves que no so comestveis:
class class Bird Bird {{ private: private: public: public: }; }; class class FlyingBird: FlyingBird: public public Bird Bird {{ private: private: public: public: virtual virtual void void Fly(); Fly(); }; }; class class NonFlyingBird: NonFlyingBird: public public Bird Bird {{ private: private: public: public: }; }; class class EdibleBird: EdibleBird: public public Bird Bird {{ private: private: public: public: virtual virtual int int Price(void); Price(void); }; }; class class NonEdibleBird: NonEdibleBird: public public Bird Bird {{ private: private: public: public: virtual virtual int int Protected(void); Protected(void); }; };

Classificao de pssaros (1)


Classificam-se recorrendo herana mltipla:
class class Penguin: Penguin: public public NonFlyingBird NonFlyingBird public public NonEdible NonEdible {{ private: private: public: public: }; }; class class Chicken: Chicken: public public NonFlyingBird, NonFlyingBird, public public EdibleBird EdibleBird {{ private: private: public: public: int int Price(void) Price(void) {return {return 650;} 650;} }; }; class class Duck: Duck: public public FlyingBird, FlyingBird, public public EdibleBird EdibleBird {{ private: private: public: public: int int Protected(void) Protected(void) {return {return 0;} 0;} int Price(void) {return int Price(void) {return 1500;} 1500;} }; }; class class Eagle: Eagle: public public FlyingBird, FlyingBird, public public NonEdibleBird NonEdibleBird {{ private: private: public: public: int int Protected(void) Protected(void) {return {return 1;} 1;} }; };

Se se pode comer, deve ter preo

Ser que uma espcie protegida?

Agora, como se classificam os pinguins, os 123 frangos, os patos, as guias? C++

Cuidado, que a herana mltipla deve ser usada com parcimnia.


C++

124

Classificao de pssaros (2)


Bird

Ambiguidades na herana mltipla


Por exemplo: cada ave sabe qual a populao mundial estimada da espcie. Define-se isso na classe de base:
class class Bird Bird {{ private: private: long long population; population; public: public: long long Population(void) Population(void) {return {return population;} population;} }; }; int int main(void) main(void) {{ Duck Duck d; d; ... ... Erro: Ambiguous reference to "Duck::Population" in base classes"Bird" and "Bird".

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;

No sero setas a mais?

Ento, que classe deve ocupar-se da populao? A classe Bird, claro, mas preciso recorrer s classes de base virtuais 125
C++

C++

126

Herana de interface, herana de implementao


A herana pblica tem dois aspectos: herana da interface e herana da implementao. Por vezes, queremos que a classe derivada herde s a declarao (interface) de uma funo; outras vezes, que herde a declarao e a implementao, mas podendo ultrapassar (override) a implementao; e outras vezes ainda, que herde a declarao e a implementao, sem ultrapassar. Um exemplo clssico: polgonos, tringulos, rectngulos: Polygon

Polgonos, tringulos, rectngulos


Os polgonos formam uma classe de base abstracta, de onde se derivam cada classe de polgonos, classificados pelo nmero de lados:
class Polygon class Polygon Polygon {{ Polygon private: destruto knows private: knows r virtual int int numberOfSides; numberOfSides; Point Point Point *p; Point *p; public: public: Polygon(int Polygon(int n): n): numberOfSides(n), numberOfSides(n), p(new p(new Point[n]) Point[n]) {{ }} virtual ~Polygon(void) virtual ~Polygon(void) {delete {delete [] [] p;} p;} virtual double Area(void) const virtual double Area(void) const == 0; 0; virtual virtual int int Convex(void) Convex(void) const const == 0; 0; virtual virtual double double Perimeter(void) Perimeter(void) const; const; int NumberOfSides(void) int NumberOfSides(void) {return {return numberOfSides;} numberOfSides;} Point& Point& operator[](int operator[](int i) i) const const {return {return p[i];} p[i];} }; }; double double Polygon::Perimeter(void) Polygon::Perimeter(void) const const {{ double double ss == 0.0; 0.0; for(int for(int i=0; i=0; ii << numberOfSides; numberOfSides; i++) i++) ss += += Distance(p[i], Distance(p[i], p[(i+1)%numberOfSides]); p[(i+1)%numberOfSides]); return s; return s; }}

funo virtual pura funo virtual simples funo no virtual

Triangle
C++

Rectangle 127
C++

128

Herana de interface (1)


O propsito das funes virtuais puras que as classes derivadas herdem a interface apenas.
class class Triangle: Triangle: public public Polygon Polygon {{ private: private: public: public: Triangle(void): Triangle(void): Polygon(3) Polygon(3) {{ }} Triangle(Point, Triangle(Point, Point, Point, Point); Point); double Area(void) double Area(void) const; const; int int Convex(void) Convex(void) const const {return {return 1;} 1;} Funo }; }; Convex para tringulos Definio da rea para tringulos: double double Triangle::Area(void) Triangle::Area(void) const const {{ double double aa == Distance((*this)[0], Distance((*this)[0], (*this)[1]); (*this)[1]); double double bb == Distance((*this)[1], Distance((*this)[1], (*this)[2]); (*this)[2]); double double cc == Distance((*this)[2], Distance((*this)[2], (*this)[0]); (*this)[0]); double double ss == (a (a ++ bb ++ c) c) // 2; 2; return return sqrt(s sqrt(s ** (s (s -- a) a) ** (s (s -- b) b) ** (s (s -- c)); c)); }}

Herana de interface (2)


Com os rectngulos a mesma coisas:
class class Rectangle: Rectangle: public public Polygon Polygon {{ private: private: double double side1; side1; double double side2; side2; public: public: Rectangle(void): Rectangle(void): Polygon(4), Polygon(4), side1(0), side1(0), side2(0){ side2(0){ }} Rectangle(double side1, double Rectangle(double side1, double side2): side2): Polygon(4), Polygon(4), side1(side1), side1(side1), side2(side2) side2(side2) {{ }} Rectangle(Point, Rectangle(Point, Point); Point); Funo Area double double Area(void) Area(void) const const para rectn{{ return return side1 side1 ** side2; side2; }} gulos int int Convex(void) Convex(void) const const {return {return 1;} 1;} double double Perimeter(void) Perimeter(void) const const {{ return return 22 ** (side1+side2); (side1+side2); }} Funo }; Convex para }; rectngulos

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

Herana opcional de implementao


O propsito das funes virtuais simples que as classes derivadas herdem a interface e uma implementao por defeito. Por exemplo: os polgonos tm uma funo para calcular o permetro, os tringulos no. Logo, os tringulos usam a dos polgonos. J os rectngulos tm uma funo permetro especializada:
class class Rectangle: Rectangle: public public Polygon Polygon {{ ... ... double double Perimeter(void) Perimeter(void) const const {{ return return 22 ** (side1+side2); (side1+side2); }} }; };

s vezes as funes virtuais simples so perigosas


Como as funes virtuais simples fornecem uma implementao por defeito, podemos, por distraco, cair nessa implementao em casos em que no a queremos (em vez de sermos alertados para o nosso lapso pelo compilador). Eis um exemplo, com uma hierarquia de veculos:

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

Carros a gasolina (1)


Os carros formam a classe de base e tm uma funo virtual para meter combustvel. Como quase todos os carros metem gasolina, essa funo trata de meter gasolina:
class class Carro Carro {{ private: private: String String matricula; matricula; String String marca; marca; public: public: Carro(String Carro(String matricula, matricula, String String marca): marca): matricula(matricula), marca(marca) matricula(matricula), marca(marca) {{ }} virtual virtual void void MeterCombustivel(int MeterCombustivel(int n) n) {{ cout cout << << matricula matricula << << ": ": Meti Meti "" << << nn << << "" litros litros de de gasolina" gasolina" << << endl; endl; }} Funo virtual }; }; simples Tambm se pode usar o Portugus com o C++

Carros a gasolina (2)


Os descapotveis e as berlinas so carros: as classes Descapotavel e Berlina herdam publicamente de Carro:
class class Descapotavel: Descapotavel: public public Carro Carro {{ private: private: public: public: Descapotavel(String Descapotavel(String matricula, matricula, String String marca): marca): Carro(matricula, marca) { } Carro(matricula, marca) { } void void Tapar(void); Tapar(void); void Os carros destas void Destapar(void); Destapar(void); }; classes metem }; gasolina (ie, class usam a funo class Berlina: Berlina: public public Carro Carro {{ virtual). private: private: public: public: Berlina(String Berlina(String matricula, matricula, String String marca): marca): Carro(matricula, marca) { Carro(matricula, marca) { }} }; };

Que outros membros devero ter os carros, os descapotveis, as berlinas?

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) { }} }; };

Comportamento por defeito


Como evitar que as carrinhas metam gasolina? Ou seja, como oferecer comportamentos por defeito s classes derivadas, mas s se elas o pedirem mesmo? Faz-se assim, em esquema:
class class Carro Carro {{ private: private: protected protected: protected: String matricula; String matricula; String String marca; marca; public: public: Carro(String Carro(String matricula, matricula, String String marca): marca): matricula(matricula), matricula(matricula), marca(marca) marca(marca) {{ }} virtual virtual void void MeterCombustivel(int MeterCombustivel(int n) n) == 0; 0; protected: protected: Esta vai ser virtual virtual MeterGasolina(int MeterGasolina(int n) n) usada pelas {{ subclasses cout cout << << matricula matricula << << ": ": Meti Meti "" que << << nn << << "" litros litros de de gasolina" gasolina" meterem << endl; << endl; gasolina. }} }; };

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++

Utilizao de membros protegidos


Agora, cada classe derivada tem que fornecer uma implementao para MeterCombustivel, porque esta virtual pura, mas pode recorrer protegida MeterGasolina, se lhe aprouver:
class class Descapotavel: Descapotavel: public public Carro Carro {{ private: private: public: public: ... ... void void MeterCombustivel(int MeterCombustivel(int n) n) {MeterGasolina(n);} {MeterGasolina(n);} }; }; class class Berlina: Berlina: public public Carro Carro {{ private: private: public: public: ... ... void void MeterCombustivel(int MeterCombustivel(int n) n) {MeterGasolina(n);} {MeterGasolina(n);} }; };

Caso das funes no virtuais (1)


No se deve redefinir as funes no virtuais. Realmente, se a classe derivada B precisa de redefinir uma funo f no virtual da classe de base A, ento no verdade que cada B -um A. E ento B no devia derivar de A publicamente. Se o B tem mesmo que derivar publicamente de A, e fornecer a sua prpria definio de f, ento f devia ser virtual, claro. Exemplo, cada carro sabe o seu tipo:
class Carro class Carro {{ ... ... String String Type(void) Type(void) {return {return "Carro";} "Carro";} ... ... }; }; class Descapotavel: class Descapotavel: public public Carro Carro {{ ... ... String String Type(void) Type(void) {return {return "Descapotavel";} "Descapotavel";} ... ... }; }; class Berlina: class Berlina: public public Carro Carro {{ ... ... String String Type(void) Type(void) {return {return "Berlina";} "Berlina";} ... ... }; }; class Carrinha: class Carrinha: public public Carro Carro {{ ... ... String String Type(void) Type(void) {return {return "Carrinha";} "Carrinha";} ... ... }; }; C++

As carrinhas metem gasleo:

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

Caso das funes no virtuais (2)


Como cada carro sabe o seu tipo o seguinte programa escreve o qu?
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"); cout cout << << d.Type() d.Type() << << endl; endl; cout cout << << b.Type() b.Type() << << endl; endl; cout cout << << c.Type() c.Type() << << endl; endl; Carro* Carro* pc pc == &b; &b; Berlina* pb Berlina* pb == &b; &b; cout cout << << pc->Type() pc->Type() << << endl; endl; cout << pb->Type() << cout << pb->Type() << endl; endl; }} return return 0; 0; O mesmo objecto aparece com dois tipos diferentes, dependendo do modo como foi questionado. Se a funo Type fosse virtual, (como devia) o tipo do objecto apontado por pc ou pb seria sempre Berlina.

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

Tem-um faz-se por estratificao (1)


A estratificao (layering) o processo de construo de uma nova classe por cima de uma outra, fazendo com que a primeira contenha um objecto da segunda. Por exemplo:
class class String String {{ ... ... }; }; class class Date Date {{ ... ... }; }; class class Photo Photo {{ ... ... }; }; class class Address Address {{ ... ... }; }; class class TelephoneNumber TelephoneNumber {{ ... ... }; }; // // continua... continua... Estas j estudmos

Tem-um faz-se por estratificao (2)


Uma pessoa tem (has-a) nome, tem bilhete de identidade, tem um dia em que nasceu, tem fotografia, tem morada nalgum lado, tem telefone, e pode ser que tenha fax:
class class Person Person {{ private: private: String String name; name; const const int int idNumber; idNumber; Date Date born; born; Photo Photo photo; photo; Address Address address; address; TelephoneNumber TelephoneNumber phone; phone; TelephoneNumber TelephoneNumber fax; fax; public: public: ... ... }; };

mas estas no (ficam como exerccio)

Mas estudantes so (is-a) pessoas que pagam propinas:


class class Student: Student: public public Person Person {{ private: private: int int tuition; tuition; public: public: ... ... void void Pay(int Pay(int x) x) {tuition {tuition -= -= x;} x;} ... ... }; }; Exerccio: Completar estas classes e pro-gramar um driver para testar.

C++

141

C++

142

Implementa-se em termos de faz-se por estratificao


Uma coisa uma classe A ter um objecto de outra classe B; outra coisa a classe A ser implementada em termos da classe B. Ainda que ambas as situaes se exprimam por meio de estratificao. Por exemplo: consideremos uma classe List, com a ajuda da qual se podem implementar muitas outras classes (conjuntos, vectores, etc.) Comea-se por declarar a classe Node, cujos objectos so os ns da lista:
class class Node Node {{ private: private: int int value; value; Node Node *next; *next; public: public: Node(int Node(int value, value, Node* Node* next): next): value(value), next(next){ value(value), next(next){ }} ~Node(void){ ~Node(void){ }} int int Value() Value() {return {return value;} value;} friend class List; friend class List; }; }; Classe camarada (friend class): a C++ classe List tem acesso aos membros da classe Node, como se fossem seus.

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++

Implementao das listas (1)


No compete a esta classe List verificar se as chamadas so feitas em boas condies (verificar se no se remove um elemento de uma lista vazia, por exemplo). Inserir cabea, Cons:
List& List& List::Cons(int List::Cons(int x) x) {{ head head == new new Node(x, Node(x, head); head); length++; length++; return return *this; *this; }}

Implementao das listas (2)


Inserir numa dada posio:
List& List& List::Insert(int List::Insert(int item, item, int int position) position) {{ Se a posio position position == min(position, min(position, length); length); estiver fora do if(position<=0) if(position<=0) intervalo dos return return Cons(item); Cons(item); ndices (zero Node *p = head; Node *p = head; at lenght), while(position) while(position) refe-rencia-se o pp == pp -> next; -> next; n com ndice p->next = new Node(item, p->next); p->next = new Node(item, p->next);mais length++; length++; aproximado return return *this; *this; }}

Como a funo Cons devolve uma List, podemos escrever expresses assim:
List s; s.Cons(5).Cons(4).Cons(9);

Remover a cabea, Tail:


List& List& List::Tail(void) List::Tail(void) {{ head=head->next; head=head->next; length; length; return return *this; *this; }} C++ Se a lista estiver vazia isto vai estoirar!

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.

Remover uma dada posio:

Implementao das listas (3)


Indexao:
int& int& List::operator[](int List::operator[](int position) position) const const {{ position position == min(position, min(position, length length -- 1); 1); Node *p(head); Node *p(head); while(position) while(position) Se a lista estiver vazia pp == p->next; p->next; isto vai dar mau resulreturn return p->value; p->value; tado e bem feito! }}

Conjuntos implementados com Listas


Conjuntos de inteiros (no sentido da Matemtica) podem ser implementados em termos de listas de inteiros. Presisamos, pelo menos, de operaes para inserir um elemento num conjunto, retirar um elemento, e verificar se o elemento pertence:
Conjuntos implementados class em termos de Listas. class Set Set {{ private: private: List List rep; rep; Esta funo privada d a int int Where(int); Where(int); posio de um elemento. public: public: Set(): Set(): rep() rep() {{ }} Os conjuntos so ~Set(void) ~Set(void) {{ }} criados vazios. Set& Set& Insert(int); Insert(int); Set& Set& Remove(int); Remove(int); int int Has(int); Has(int); int int Cardinality(void) Cardinality(void) const const {return {return rep.Length();} rep.Length();} void void Display(void) Display(void) const const {rep.Display();} {rep.Display();} }; };

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.

Herana privada significa implementa-se em termos de


Herana privada: 1. Apontadores para a classe derivada no so convertidos para a classe de base. 2. Membros herdados da classe de base privada tornam-se privados na classe derivada. Para Paraque queserve servea aherana heranaprivada? privada? Para Paraimplementar implementaruma umaclasse classeem emtermos termosde de outra. outra. Eis, como exemplo, uma implementao alternativa para os Conjuntos: Herana privada. Se fosse

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++

Classes de base abstractas


Uma classe de base abstracta (em Ingls, Abstract Base Class ABC) uma classe que s interface: contm apenas funes virtuais puras. Por exemplo, o conceito de coleco mais abstracto que o de conjunto: uma coleco uma classe de base abstracta, com operaes para inserir um elemento, retirar um elemento, ver se o elemento l est. Em C++ seria assim:
class class Collection Collection {{ private: Destrutor virtual private: public: public: virtual virtual ~Collection(void) ~Collection(void) == 0; 0; virtual void virtual void Insert(int) Insert(int) == 0; 0; virtual virtual void void Remove(int) Remove(int) == 0; 0; virtual int Has(int) virtual int Has(int) == 0; 0; }; };

Herana mltipla, pblica e privada


Set herda publicamente de Collection, e privadamente de List, para a implementao. Seria assim:
class class Set: Set: public public Collection, Collection, private private List List {{ Esta abordagem, legtima ... ... em geral, neste caso void void Insert(int); Insert(int); depara com problemas, void void Remove(int); Remove(int); porque h funes com int Has(int); int Has(int); os mesmos nomes das ... ... duas classes herdadas. }; };

Alternativamente, podemos fazer uma implementao estratificada:


class class Set: Set: public public Collection Collection {{ private: private: Aqui no h problemas. List List rep; rep; public: public: ... ... void void Insert(int); Insert(int); void void Remove(int); Remove(int); int int Has(int); Has(int); ... ... }; };

Podemos dizer, com propriedade, que um Conjunto uma Coleco, e como tal, fazer a classe Set herdar publicamente da classe Collection.
C++

151

C++

Cuidado com a herana mltipla. uma caixinha de surpresas.

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.

xidade de Has n2, quando devia ser simplesmente n.

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

Os iteradores so um mecanismo geral de programao, e so muito teis.


C++

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

Destrutores virtuais (1)


As classes de base devem ter destrutores virtuais. S assim se pode garantir que objectos de classes derivadas referenciados por apontadores para a classe de base so destrudos apropriadamente. Eis um exemplo, com as classes Person e Student, em que se pretende contar o nmero de objectos de cada classe. Usa-se um membro esttico em cada uma:
Ao declarar como class class Person Person {{ esttico um membro private: private: valor, especifica-se String name; String name; que h apenas uma const const char char sex; sex; cpia desse membro, static int numberOfPersons; static int numberOfPersons;comum a todos os public: public: objectos da classe. Person Person (String (String n, n, char char s): s): name(n), sex(s) name(n), sex(s) Destrutor {{ virtual ++numberOfPersons; ++numberOfPersons; O construtor e o des}} trutor vo contabilivirtual virtual ~Person(void) ~Person(void) zando as pessoas {{ numberOfPersons; numberOfPersons; }} ... ... friend friend int int HowManyPersons(void) HowManyPersons(void) {return {return numberOfPersons;} numberOfPersons;} }; }; C++

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

Destrutores virtuais (2)


Com os estudantes semelhante:
class class Student: Student: public public Person Person {{ private: private: String String school; school; int int year; year; static static int int numberOfStudents; numberOfStudents; public: public: Student Student (String (String n, n, char char s, s, String String sch): sch): Person(n, s), school(sch), Person(n, s), school(sch), year(1) year(1) {{ ++numberOfStudents; ++numberOfStudents; }} ~Student(void) ~Student(void) {{ numberOfStudents; numberOfStudents; }} ... ... friend friend int int HowManyStudents(void) HowManyStudents(void) {return {return numberOfStudents;} numberOfStudents;} }; };

Destrutores virtuais (3)


Analisando o efeito dos destrutores virtuais:
int int main(void) main(void) {{ Person Person p("Carlos", p("Carlos", 'M'); 'M'); Student Student s("Ana", s("Ana", 'F', 'F', "FCT/UNL"); "FCT/UNL"); Person Person *pp; *pp; pp pp == new new Person("Rui", Person("Rui", 'M'); 'M'); Student *ss; Student *ss; ss ss == new new Student("Teresa", Student("Teresa", 'F', 'F', "IST"); "IST");

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

mal nesta classe?


C++

162

Classe Stack com excepes char*


Uma pilha estoira ao empilhar quando est cheia, ao desempilhar ou ver o topo quando est vazia. E ao construir, se no houver memria que chegue:
Stack::Stack(long Stack::Stack(long size): size): throw size(size), size(size), s(new s(new int int [size]) [size]) {{ if(s==0) if(s==0) throw throw "Out "Out of of memory"; memory"; top = s; top = s; }} Stack& Stack::Push(int n) Stack& Stack::Push(int n) {{ if(top if(top -- ss == == size) size) throw throw "Stack "Stack is is full. full. Cannot Cannot Push."; Push."; *top++ = n; *top++ = n; return return *this; *this; }} Stack& Stack::Pop(void) Stack& Stack::Pop(void) {{ if(top if(top == == s) s) throw throw "Stack "Stack is is empty. empty. Cannot Cannot Pop."; Pop."; top; top; return return *this; *this; }} int Stack::Top(void) const int Stack::Top(void) const {{ if(top if(top == == s) s) throw throw "Stack "Stack is is empty. empty. No No Top."; Top."; return *(top 1); return *(top - 1); }} C++

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); ... ... }}

}}

E que acontecia se a funo comeasse assim:


C++

E quem apanha estas excepes?

163

? 164

Excepes de vrios tipos


As excepes de pilha cheia e pilha vazia tm a ver com o funcionamento da pilha. A de falta de memria geral, e pode acontecer por todo o lado. Podia ser a classe Stack a fornecer aquelas excepes:
Tipo enumerado para class class Stack Stack {{ as excepes private: private: ... ... public: public: enum enum Exception Exception {IsFull, {IsFull, IsEmpty}; IsEmpty}; Stack(long); Stack(long); ... ... }; }; Stack::Stack(long Stack::Stack(long size): size): size(size), size(size), s(new s(new int int [size]) [size]) {{ Stack& Esta Stack& Stack::Push(int Stack::Push(int n) n) if(s==0) {{ if(s==0) est throw "Out of memory"; throw "Out of memory"; if(top igual if(top -- ss == == size) size) top throw IsFull; top == s; s; . throw IsFull; }} *top++ Stack& *top++ == n; n; Stack& Stack::Pop(void) Stack::Pop(void) return {{ return *this; *this; }} if(top if(top == == s) s) throw throw IsEmpty; IsEmpty; top--; int Stack::Top(void) const top--; Estas int Stack::Top(void) const return {{ return *this; *this; agora }} if(top == s) lanam if(top == s) throw excepes throw IsEmpty; IsEmpty; return prprias. return *(top *(top -- 1); 1); 165 C++}}

Discriminando entre excepes


Havendo vrias classes de excepes, usam-se vrios catch para discriminar entre elas, assim:

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++

Especificando as excepes na interface


Fica bem especificar as classes de excepes que uma funo pode lanar na declarao da funo. No caso da pilha era assim:
class class Stack Stack {{ private: private: int int *s; *s; Lana int int *top; *top; const char *. const long size; const long size; public: public: enum enum Exception Exception {IsFull, {IsFull, IsEmpty}; IsEmpty}; No lana Stack(long) throw Stack(long) throw (const (const char char *); *); nada. ~Stack(void) {delete [] s;} ~Stack(void) {delete [] s;} int Empty(void) const throw () int Empty(void) const throw () {return {return top top == == s;} s;} Lana int Full(void) int Full(void) const const throw throw () () Exception {return . {return top top ss == == size;} size;} Stack& Stack& Push(int Push(int n) n) throw throw (Exception); (Exception); Stack& Stack& Pop(void) Pop(void) throw throw (Exception); (Exception); int Top(void) const int Top(void) const throw throw (Exception); (Exception); int int Depth(void) Depth(void) const const throw throw () () {return {return top top s;} s;} }; };

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++

E como que se apanha isto? 168

Apanhando excepes parametrizadas


a mesma coisa. A instruo catch trata de tudo:
int int main(void) main(void) {{ try try {{ ... ... }} catch(const catch(const char* char* s) s) {{ cerr cerr << << "Exception: "Exception: "" << << ss << << endl; endl; abort(); abort(); }} Estes membros catch so pblicos. catch (StackException (StackException exc) exc) {{ switch(exc.type){ switch(exc.type){ case case StackException::IsFull: StackException::IsFull: cerr cerr << << "Stack "Stack exception: exception: "" << << "Stack "Stack was was full full "" << "when trying << "when trying to to push push "" << << exc.offending exc.offending << << endl; endl; break; break; case case StackException::IsEmpty: StackException::IsEmpty: cerr cerr << << "Stack "Stack exception: exception: "" << "Stack is << "Stack is empty" empty" << << endl; endl; break; break; }} }} } C++ } return return 0; 0;

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)

Classes genricas (templates)


Uma pilha uma pilha, independentemente do tipo de objectos que l colocamos. Com um pouco mais de trabalho, a pilha de inteiros pode ser transformada numa pilha genrica, capaz de conter objectos de uma classe qualquer. Para fazer isso, usa-se o mecanismo das templates. Fica assim:
template template <class <class T> T> class class Stack Stack {{ private: private: TT *s; *s; TT *top; *top; const const long long size; size; public: public: Stack(long Stack(long == 10); 10); ~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 int Full(void) const {return {return top top -- ss == == size;} size;} Stack<T>& Push(const Stack<T>& Push(const T&); T&); Stack<T>& Stack<T>& Pop(void); Pop(void); TT 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;} }; };

C++

171

Isto no uma classe: uma class template. Especifica como que as diversas classes pilha vo ser construdas. 172 C++

Definindo as funes das classes genricas


As funes que no so logo definidas com a declarao da classe ficam para depois, como de costume:
template template <class <class T> T> Stack<T>::Stack(long Stack<T>::Stack(long size): size): size(size), size(size), s(new s(new TT [size]) [size]) {{ top top == s; s; }} template template <class <class T> T> Stack<T>& Stack<T>& Stack<T>::Push(const Stack<T>::Push(const T& T& t) t) {{ *top++ *top++ == t; t; return return *this; *this; }} template template <class <class T> T> Stack<T>& Stack<T>& Stack<T>::Pop(void) Stack<T>::Pop(void) {{ top; top; return return *this; *this; }}

Utilizao das classes genricas


As classes genricas usam-se para declarar objectos, instanciando a classe paramtrica. Por exemplo:
Uma pilha de int, com espao para 4. int main(void) int main(void) {{ Stack<int> Stack<int> s1(4); s1(4); s1.Push(3).Push(4).Push(5).Push(6); s1.Push(3).Push(4).Push(5).Push(6); cout cout << << s1.Top() s1.Top() << << endl; endl; cout << s1.Pop().Top() Uma pilha de cout << s1.Pop().Top() << << endl; endl; String, com Stack<String>s2; espao para 10 (por Stack<String>s2; s2.Push("Lisboa").Push("Porto"); defeito). s2.Push("Lisboa").Push("Porto"); s2.Push("Coimbra"); s2.Push("Coimbra"); cout cout << << s2.Top() s2.Top() << << endl; endl; s2.Pop(); s2.Pop(); cout cout << << s2.Top() s2.Top() << << endl; endl; }} return return 0; 0;

Como tinha que ser, as pilhas de inteiros so iguaizinhas s pilhas de cadeias.

OK?
C++

173

C++

174

Listas genricas (1)


As classes contentor, ie, as classes que servem para guardar objectos de um tipo arbitrrio, programam-se normalmente na forma de class templates. muito mais prtico (ainda que um pouco mais complicado) Para ter listas genricas preciso comear por ter ns genricos:
template template <class <class T> T> class class Node Node {{ private: private: TT value; value; Node<T> Node<T> *next; *next; public: public: Node(const Node(const T& T& value, value, Node<T>* Node<T>* next): next): value(value), next(next){ value(value), next(next){ }} ~Node(void){ ~Node(void){ }} TT Value() Value() const const {return {return value;} value;} friend class List<T>; Duas classes friend class List<T>; friend camaradas friend class class Iterator<T>; Iterator<T>; }; };

Listas genricas (2)


Nas listas genricas, como nas pilhas, o melhor copiar o modelo de uma implemen-tao concreta, generalizando-o:
template template <class <class T> T> class class List List {{ private: private: Node<T> Node<T> *head; *head; int int length; length; public: public: List(void): List(void): head(0), head(0), length(0){ length(0){ }} virtual ~List(void) { virtual ~List(void) { }} virtual virtual int int Length(void) Length(void) const const {return length;} {return length;} virtual virtual List<T>& List<T>& Cons(const Cons(const T&); T&); virtual List<T>& Tail(void); virtual List<T>& Tail(void); virtual virtual List<T>& List<T>& Insert(const Insert(const T&, T&, int); int); virtual virtual List<T>& List<T>& Remove(int Remove(int position); position); virtual virtual T& T& operator[](int) operator[](int) const; const; virtual void virtual void Display(void) Display(void) const; const; }; }; friend friend class class Iterator<T>; Iterator<T>;

A lista genrica vai ter um n genrico privado:


template template <class <class T> T> class class List List {{ private: private: Node<T> Node<T> *head; *head; int int length; length; public: public: ... ... C++

As funes Cons, Tail, Insert, Remove, o operador [] e a funo Display so definidos parte. 175
C++

176

Funes das listas genricas (1)


No fundo, simples:
template template <class <class T> T> List<T>& List<T>::Cons(const List<T>& List<T>::Cons(const T& T& x) x) {{ head head == new new Node<T>(x, Node<T>(x, head); head); length++; length++; return *this; return *this; Construo de }} um n genrico template template <class <class T> T> List<T>& List<T>& List<T>::Tail(void) List<T>::Tail(void) {{ head head == head->next; head->next; length; length; return return *this; *this; }} template template <class <class T> T> List<T>& List<T>& List<T>:: List<T>:: Insert(const Insert(const T& T& item, item, int int position) position) {{ position position == min(position, min(position, length); length); if(position<=0) if(position<=0) return return Cons(item); Cons(item); Node<T> Node<T> *p *p == head; head; while(position) while(position) pp == pp -> -> next; next; p->next p->next == new new Node<T>(item, Node<T>(item, p->next); p->next); length++; length++; return return *this; *this; }} C++

Funes das listas genricas (2)


(continuao)
template template <class <class T> T> List<T>& List<T>::Remove(int List<T>& List<T>::Remove(int position) position) {{ position position == min(position, min(position, length length -- 11 ); ); if(position if(position <= <= 0) 0) return Tail(); return Tail(); Node<T> Node<T> *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; }} template template <class <class T> T> T& List<T>::operator[](int T& List<T>::operator[](int position) position) const const {{ position position == min(position, min(position, length length -- 1); 1); Node<T> Node<T> *p(head); *p(head); while(position) while(position) Esta funo s d pp == p->next; p->next; return p->value; para tipos que return p->value; }} sobrecarreguem o operador ostream <<. template <class T> template <class T> void void List<T>::Display(void) List<T>::Display(void) const const {{ for(Node<T> for(Node<T> *p *p == head; head; p; p; pp == p->next) p->next) cout cout << << p->value p->value << << "" "; "; cout << endl; cout << endl; }} 178 C++

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

Vectores genricos (1)


Podem fazer-se templates com mais que um parmetro, e com parmetros que no so classes. Por exemplo, eis uma classe Vector, parametrizada pelo tipo dos elementos e pela dimenso:
template template <class <class T, T, int int size> size> class class Vector{ Vector{ private: private: TT v[size]; v[size]; public: public: Vector(){}; Vector(){}; T& T& operator[](int) operator[](int) throw throw (const (const char char *); *); }; }; template template <class <class T, T, int int size> size> T& Vector<T, size>::operator[](int T& Vector<T, size>::operator[](int i) i) {{ if(i<0 if(i<0 || || i>=size) i>=size) throw("Index throw("Index out out of of range."); range."); return v[i]; return v[i]; }}

Vectores genricos (2)


Eis um pequeno programa para ilustar a utilizao dos vectores genricos:
int int main(void) main(void) {{ Vector<String, Vector<String, 100> 100> sv; sv; int int i; i; char char line[80]; line[80]; Ateno a este getline

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++ ... } ...

rvores genricas (1)


As rvores binrias so uma estrutura de dados muito til. No poderamos passar sem uma classe genrica para elas. parecida com a das listas:
template template <class <class T> T> class class Node Node {{ friend class Tree<T>; friend class Tree<T>; Um n para private: private: rvores tem um TT value; membro valor e value; Node<T> dois apontadores Node<T> *left; *left; Node<T> Node<T> *right; *right; Node Node (const (const T& T& t): t): value(t), left(0), value(t), left(0), right(0) right(0) {{ }} void Insert(T&); void Insert(T&); Estas funes, void void InOrder(void); InOrder(void); recursivas, so int chamadas pelas int Has(const Has(const T&); T&); }; de baixo. }; template template <class <class T> T> class class Tree Tree {{Uma rvore um private: apontador para private: um n. Node<T> Node<T> *root; *root; public: public: Tree(void){root Inserir um Tree(void){root == 0;} 0;} elemento, void Insert(T&); void Insert(T&); ordenadamente int Has(const T&); int Has(const T&); void InOrder(void); void InOrder(void); }; A rvore contm }; o elemento? C++ Percurso infixo

rvores genricas (2)


Insere-se um novo elemento recursivamente:
template template <class <class T> T> void Node<T>::Insert(T& void Node<T>::Insert(T& t) t) {{ if(t if(t == == value) value) ;; // // nothing nothing to to do do else if(t <= value) else if(t <= value) if(left if(left == == 0) 0) left = left = new new Node<T>(t); Node<T>(t); else else left->Insert(t); left->Insert(t); else else if(right if(right == == 0) 0) right right == new new Node<T>(t); Node<T>(t); else else right->Insert(t); right->Insert(t); }} preciso que a classe T tenha os operadores == e <=.

Para ver se um elemento est na rvore parecido:


template template <class <class T> T> int Node<T>::Has(const int Node<T>::Has(const T& T& t) t) {{ if(t if(t == == value) value) return return 1; 1; else else if(t if(t <= <= value) value) return left return left == == 00 ?? 00 :: left left ->Has(t); ->Has(t); else else return return right== right== 00 ?? 00 :: right->Has(t); right->Has(t); }} C++

183

184

rvores genricas (3)


Para inserir numa rvore, insere-se no n raiz, se j existir raiz:
template template <class <class T> T> void Tree<T>::Insert(const void Tree<T>::Insert(const T& T& t) t) {{ if(root if(root == == 0) 0) root root == new new Node<T>(t); Node<T>(t); else else root->Insert(t); root->Insert(t); }}

rvores genricas (4)


A funo InOrder realiza o percurso infixo da rvore, escrevendo em cout os valores dos ns visitados.
template template <class <class T> T> void Node<T>::InOrder(void) void Node<T>::InOrder(void) {{ if(left if(left != != 0) 0) left->InOrder(); left->InOrder(); cout cout << << this->value this->value << << endl; endl; if(right != 0) if(right != 0) right->InOrder(); right->InOrder(); }} preciso que a classe T sobrecarregue o operador << da classe ostream.

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; }}

A base da recursividade est na classe Tree:


template template <class <class T> T> void Tree<T>::InOrder(void) void Tree<T>::InOrder(void) {{ if(root if(root != != 0) 0) root->InOrder(); root->InOrder(); }}

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

rvores genricas (5)


Eis um pequeno programa para testar a classe genrica Tree:
int int main main (void) (void) {{ Tree<String> Tree<String> tree; tree; String String x; x; for(;;) for(;;) {{ cout cout << << "Uma "Uma cadeia: cadeia: "; "; cin >> x; cin >> x; if(x=="") if(x=="") break; break; tree.Insert(x); tree.Insert(x); tree.InOrder(); tree.InOrder(); }} for(;;) for(;;) {{ cout cout << << "Uma "Uma cadeia: cadeia: "; "; cin cin >> >> x; x; if(x=="") if(x=="") break; break; if(tree.Has(x)) if(tree.Has(x)) cout cout << << "Sim\n"; "Sim\n"; else else cout cout << << "Nao\n"; "Nao\n"; }} return return 0; 0; }} C++ Construo da rvore

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&); ... ... }}

Complementos sobre streams Formatao (1)


A classe ios, da qual derivam ostream e istream, contm informao sobre a maneira como os valores devem ser escritos ou lidos.
class class ios ios {{ public: public: /* /* Some Some enums enums are are declared declared in in ** ios to avoid pollution ios to avoid pollution of of ** global global namespace namespace */ */ /* /* flags flags for for controlling controlling format format */ */ enum { skipws=01, saltar brancos enum { skipws=01, left=02, left=02, encostar esquerda right=04, right=04, internal=010, encostar direita internal=010, dec=020, dec=020, base 10, 8, 16 sinal esquerda, resto oct=040, oct=040, direita hex=0100, hex=0100, showbase=0200, showbase=0200, zero direita showpoint=0400, showpoint=0400, uppercase=01000, uppercase=01000, showpos=02000, showpos=02000, .dddddd Edd scientific=04000, scientific=04000, fixed=010000, fixed=010000, unitbuf=020000, unitbuf=020000, stdio=040000 stdio=040000 }} ;; ... Flush aps cada carcter ... 190 C++ ... ...

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

Complementos sobre streams Formatao (2)


static static const const long long basefield basefield ;; /* dec|oct|hex /* dec|oct|hex */ */ Estas static const static const long long adjustfield adjustfield ;; constantes /* left|right|internal */ /* left|right|internal */ so usadas static const long floatfield ; static const long floatfield ; no segundo /* /* scientific|fixed scientific|fixed */ */ argumento da funo setf. ... ... public: public: ... ... long long long long long long long long long long int int int int char char char char int int int int C++

Complementos sobre streams Manipuladores


O manipuladores so objectos que se podem meter nas sequncias de << ou de >>, em vez de ter que chamar explicitamente funes de formatao ou outras.
... ... ostream& ostream& ostream& ostream& ostream& ostream& ios& ios& ios& ios& ios& ios& istream& istream& ... ... Juntar '\n' e flush endl(ostream&); endl(ostream&); Juntar '\0' e flush ends(ostream&); ends(ostream&); flush(ostream&); flush(ostream&); dec(ios&); dec(ios&); hex(ios&); hex(ios&); oct(ios&); oct(ios&); ws(istream&); Saltar brancos ws(istream&);

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

Processamento de ficheiros de texto


Para ilustrar o processamento de texto, consideremos o problema de listar no terminal todos os identificadores de um programa C++. O programa reside num ficheiro de texto que queremos considerar como um objecto que vai emitindo os sucessivos identificadores do programa. Seja a classe WordStream:
ifstream: streams class para ficheiros de input. class WordStream WordStream {{ private: preciso incluir private: ifstream <fstream.h> ifstream stream; stream; char current[80]; char current[80]; char char lastChar; lastChar; ifstream& ifstream& SkipChars(void); SkipChars(void); ifstream& ifstream& ReadWord(void); ReadWord(void); public: public: Construtor WordStream(char WordStream(char *); *); void void Next(void); Next(void); Avanar int int End(void); End(void); String Current(); String Current(); }; J acabou? }; Palavra corrente

Ler palavras, saltar separadores


As funes privadas servem para ler identificadores, e para saltar os caracteres que separam identificadores consecutivos:
ifstream& ifstream& WordStream::SkipChars(void) WordStream::SkipChars(void) {{ char preciso incluir <ctype.h> char c; c; while while (stream.get(c)) (stream.get(c)) if if (!(isspace(c) (!(isspace(c) || || isdigit(c) isdigit(c) || || ispunct(c) ispunct(c) && && cc != != '_' '_' || || iscntrl(c))) iscntrl(c))) break; break; O ltimo carcter lido pertence lastChar lastChar == c; c; ao prximo identificador return return stream; stream; }} ifstream& ifstream& WordStream::ReadWord(void) WordStream::ReadWord(void) {{ char char c; c; int int ii == 0; 0; current[i++] current[i++] == lastChar; lastChar; while while (stream.get(c)) (stream.get(c)) {{ if(!(isalnum(c) if(!(isalnum(c) || || cc == == '_')) '_')) break; break; current[i++] current[i++] == c; c; Quando o ficheiro acabar as }} funes vo devolver 0. current[i] = '\0'; current[i] = '\0'; lastChar = c; lastChar = c; return return stream; stream; 194 C++}}

No fundo, isto um iterador.


C++

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?

Exerccio: programar isto base de um iterador do outro estilo. C++

195

C++

196

Complementos sobre streams Modos de abertura


As ifstreams so abertas para leitura e as ofstreams para escrita. Mas os construtores podem tomar um segundo argumento, especificando o modo de abertura.
abrir para ler ... ... abrir para escrever enum enum open_mode open_mode {{ in=1, in=1, out=2, out=2, abrir e ir para o fim ate=4, ate=4, app=010, destruir o contedo app=010, append trunc=020, trunc=020, nocreate=040, falhar se no existir nocreate=040, noreplace=0100, noreplace=0100, falhar se existir bin=0200, bin=0200, ficheiro binrio }} binary=bin binary=bin /* /* OS2 OS2 */ */

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

Você também pode gostar