Upload
carlos-veiga-valente
View
228
Download
8
Embed Size (px)
Citation preview
Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Classes em C++
Programação em C++
Classes em C++ 5 - 2 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Abstract Data Type• Abstract Data Type - tipos de dados abstractos
definidos pelo utilizador - a simplicidade e clareza de um programa consegue-se definindo novos tipos de dados.
• Em C++ podemos definir novos tipos de dados através da definição de classes.
• Na definição duma classe, interessa ter em consideração dois factores:• Quais os modos como desejamos poder iniciar os objectos dessa classe. • Quais as operações que desejamos dispor para interactuar com objectos
da classe (quais as acções que pretendemos realizar).
Classes em C++ 5 - 3 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Classes em C++• O C++ associa a definição de um novo tipo de dados, à
definição de um agregado de membros (dados + funções) que toma as denominações alternativas struct ou class.
• Estruturas e classes são agregados de membros através dos quais se especificam os dados, o conjunto de operações e as funções necessárias à manipulação de objectos do tipo que esses agregados definem.
• O espaço de memória ocupado por cada objecto de um tipo definido pelo utilizador, é apenas o necessário para os dados, sendo o código dos métodos partilhado por todos os objectos desse tipo que sejam instanciados.
Classes em C++ 5 - 4 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Terminologia• Classe
• Tipo de dados definido pelo programador.
• Método• Membro função de uma classe.
• Atributo• Membro dado de uma classe.
Classes em C++ 5 - 5 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Terminologia• Objecto
• Instancia duma classe.
• Instanciar um objecto • Criar e iniciar um objecto de um tipo definido
pelo programador.
• Enviar uma mensagem a um objecto• Evocar o método.
• Receptor de uma mensagem • Objecto sobre o qual se evoca o método.
Classes em C++ 5 - 6 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Programa exemploPrograma em C++ que, dado um ano, mostra no standard output os meses em que nesse ano ocorre sexta-feira em dia 13.
#include "Date.h”#include <iostream.h>void main(){ int year; cout<< "Qual o ano? "; cin>>year;
Date d(year, 1, 13); //Instanciar o objecto.cout <<”Ocorre sexta dia 13 em:" << endl;for( int month=1; month<=12 ; ++month ) {
d.setMonth(month);if (d.getDayOfWeek()==FRI)
cout<<' '<< d.getMonthName ();}
}
Classes em C++ 5 - 7 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
AcessibilidadeDados
Métodos
Privado
Público
Os membros de uma estrutura ou classe podem declarar-se como públicos (acessíveis por todos) ou privados (acessíveis pelos seus próprios métodos), através das palavras chave public e private.
class X {private: int m; // Atributo privadopublic: int getM() { return m; }// Acesso para ler. void setM(int n) {m=n} // Acesso para escrever.};
void main() { X xa; // Instancia o objecto xa. int a, b; // Define os inteiros a e b. xa.setM(6); // Afecta xa.m com 6. a=xa.getM(); // Afecta a com xa.m. b=xa.m; // ERRO, o membro m é privado.}
•Os métodos da parte pública de uma classe constituem a interface para os objectos dessa classe.
•Os únicos membros da classe que são acessíveis do exterior, são aqueles que se situam na zona definida como pública.
•Os métodos de uma classe têm livre acesso a quaisquer membro dessa classe (públicos ou privados).
Classes em C++ 5 - 8 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
• As palavras chave struct e class, são praticamente sinónimos em C++, com a excepção da acessibilidade atribuída aos seus membros, por omissão de especificador de acesso.
Acessibilidade
struct y {
int f();
private:
int k;};
equivale a
class y {
int k;public:
int f();
};
A diferença entre estas duas declarações é a acessibilidade por omissão ( private na class e public na struct).
Classes em C++ 5 - 9 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Overload de nomes de métodos
Os métodos , tal como as funções globais, têm a capacidade de partilhar o mesmo nome.
class Student { char name[20]; unsigned number, note;public: void set(const char* nn,unsigned nb,unsigned nt)
{ set(nn, nb); set(nt); } void set(const char* nn, unsigned nb)
{ strncpy(name,nn,20);name[19]=0;number=nb;} void set(unsigned nt)
{ note = nt % 21; }};
Student s;s.set(“Luis Manuel”, 1024);...s.set(16);
• Pode-se a atribuir o mesmo nome a dois métodos distintos, desde que difiram entre si pelo número, ou elo tipo de parâmetros, ou pela ordem com que esses parâmetros são declarados.
• O compilador decide qual dos métodos set() é evocado, comparando o tipo dos parâmetros actuais com o tipo dos parâmetros formais de todos os métodos set().
Classes em C++ 5 - 10 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Construtores• A instanciação de um objecto de uma dada classe, pode simultaneamente
envolver a sua iniciação com um conjunto de valores explicitados.
• O modo de iniciar um objecto, é definir como membro da classe uma função com esse propósito explícito, que toma a denominação de construtor.
• Um construtor é reconhecido por ter o mesmo nome que a classe.
class Date { ...public: Date(int , int, int); //Ano, mes e dia Date(int); // Primeiro dia do ano Date(); // Data de hoje Date(char*);// Representação em string ...};
Date today;Date firstDay(1998);Date anyDay ("2/7/1992");Date date(1992, 6, 5);
class Date { ...public: Date(int yy=0, int mm=0, int dd=0); Date(char*); // Representação string};
Date::Date(int yy, int mm, int dd) { if ( yy == 0 ) { /*data recolhida do sistema*/} else if (dd == 0) { dd= 1; } else if (mm == 0) {mm = 1; } // teste de data válida etc.}
Obtenção de vários construtores recorrendo à sobrecarga de funções.
Obtenção de vários construtores recorrendo á definição de parâmetros por omissão (default).
Classes em C++ 5 - 11 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Membros ConstantesQuer os atributos quer os métodos de uma classe, podem ser declarados como constantes.Os atributos declarados como constantes, são iniciados com um valor no acto da instanciação do objecto e esse valor não pode sofrer alterações posteriores.Os métodos declarados como constantes, não podem alterar os atributos do objecto para o qual sejam evocados.
class Circle { const double pi; double radius;public: Circle(double r) : pi(3.1414159) { radius = r; } double getArea() const { return pi * radius * radius; } double getRadius() const { return radius;} void setRadius(double r) { radius = r; }};
•De uma forma geral os métodos get???() são constantes, porque retornam o valor do atributo ???. Os métodos set???() não são constantes porque tem como finalidade alterar o valor do atributo ???.•O critério sistemático de declarar como constantes os membros que pretendemos se comportem como tal, permite que o compilador detecte como erros gramaticais, lapsos que por omissão dessa declaração constituiriam erros lógicos do programa, difíceis (e morosos) de
identificar.
Classes em C++ 5 - 12 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Métodos ConstantesOs métodos que não são declarados como constantes só podem ser invocados por objectos não constantes, enquanto que um método constante pode sempre ser invocado, quer os objectos sejam ou não constantes.
double Circle::getArea(const Circle &c) const { return radius *= radius * 3.1414159; // Erro}
•O compilador assinala erro gramatical, porque não é possível alterar o valor dos atributos em métodos constantes.
void f(const Circle &c) {if (c.getRadius() < 1) // Ok. c.setRadius(1); // Erro // ...
}
•O compilador assinala erro gramatical, porque se está a invocar um método não constante sobre um objecto constante.
Classes em C++ 5 - 13 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Métodos inline• Os métodos de uma classe podem ser definidos inline, tal como
qualquer função global. Quando uma função (método ou função global) é definida como inline, o compilador insere o próprio código da função em todos os pontos em que for evocada .
class Count { const unsigned mod; unsigned val;public: Count(unsigned m){mod = m; val = 0; } //Métodos inline — implicitamente. unsigned get() const {return val; } void set(unsigned v) {val = v % mod;} void inc() {if (++val==mod) val=0; }};
class Count {const unsigned mod; unsigned val;
public: Count( unsigned ); //Construtor.unsigned get() const;void set( unsigned );void inc();
};
//Métodos inline — explicitamente. inline Count::Count(unsigned m) { mod = m; val = 0; }inline unsigned Count::get() const{ return val; }inline void Count::set(unsigned v){ val = v % mod; }inline void Count::inc() { if(++val==mod) val=0; }
Quando um método é definido na própria definição de classe, é implicitamente considerado inline.
Usando o prefixo inline na definição do método, é explicitamente considerado inline.
Classes em C++ 5 - 14 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe Dateclass Date {public:
enum DayOfWeek { MON, TUE, WED, THU, FRI, SAT, SUN, INVALID };Date( int y, int m = 1, int d = 1 );void setYear(int y);void setMonth( int m );void setDay( int d );DayOfWeek getDayOfWeek();const char* getMonthName();const char* getDayOfWeekName();
private:unsigned year; // Anounsigned char month, day;// Mes e dia do mês// Atributos auxiliares// Número de dias desde 1 de Janeiro.int daysOfYear;
// Dia da semana de 1 de Janeiro DayOfWeek dayOfWeekYear; // Métodos auxiliaresBool leapYear( int y ) const; // Ano bissextoint getDaysOfYear();DayOfWeek getDayOfWeekYear();
};
Classes em C++ 5 - 15 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
inline Date::Date( int y, int m = 1, int d = 1 ) {setYear(y); setMonth(m); setDay(d);
}
inline Bool Date:: leapYear( int y ) const { return !(y % 400) || !(y%4) && (y%100);
}
inline void Date::setYear(int y) { year=y; dayOfWeekYear=INVALID; daysOfYear=-1;
}
inline void Date::setMonth( int m ) { month=m; daysOfYear=-1;
}
inline void Date:: setDay( int d ) {if (daysOfYear!=-1) daysOfYear+=d-day; day=d;
}
Classe Date (definição dos métodos inline)
Classes em C++ 5 - 16 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Classe Date (definição dos métodos)
// Devolve o nome do dia da semana.const char* Date::getWeakDayName() { static char *weakDayName[] = { "Segunda-feira", "Terca-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sabado", "Domingo" }; return weakDayName[ getWeakDay() ];}
// Devolve o nome do dia do mês.const char* Date::getMonthName() { static char *monthName[] = { "Janeiro", "Fevereiro", "Marco", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro","Outubro", "Novembro", "Dezembro" }; return monthName[month-1]; }
Classes em C++ 5 - 17 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Classe Date - método getWeekYearDay()
Toma-se como referência a segunda feira de 1/1/1. Tem-se em consideração que o dia de ano novo num ano não bissexto avança um dia (365%7=1) e nos anos bissextos avança dois dias (366%7=2)
const int DAYS_OF_ONE_WEAK = 7;
WeakDay Date::getYearWeakDay () { unsigned int d = MONDAY;
for (int y=1 ; y<year ; ++y ) d += leapYear(y)?2:1;
return WeakDay ( d % DAYS_OF_ONE_WEAK );}
Classes em C++ 5 - 18 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Classe Date - método getYearDay()
int Date::getYearDay() { const int JAN = 31, FEV = 28, MAR = 31, APR = 30, MAY = 31, JUN = 30, JUL = 31, AGO = 31, SEP = 30, OCT = 31, NOV = 30, DEC = 31;static int daysTillMonth[13] = { // Dias até.
0, JAN, JAN+FEV, JAN+FEV+MAR, JAN+FEV+MAR+APR,JAN+FEV+MAR+APR+MAY, JAN+FEV+MAR+APR+MAY+JUN, JAN+FEV+MAR+APR+MAY+JUN+JUL, JAN+FEV+MAR+APR+MAY+JUN+JUL+AGO, JAN+FEV+MAR+APR+MAY+JUN+JUL+AGO+SEP, JAN+FEV+MAR+APR+MAY+JUN+JUL+AGO+SEP+OCT, JAN+FEV+MAR+APR+MAY+JUN+JUL+AGO+SEP+OCT+NOV
}; return day –1 + daysTillMonth[month-1] + (month > 2 && leapYear(year));
}
Classes em C++ 5 - 19 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Classe Date - método getWeekDay()
Calcula o dia da semana a partir do dia da semana em que começa o ano e o número de dias desde o início do ano. Actualiza o dia da semana em que começa o ano (yearWeekDay) se este não estiver actualizado (yearWeekDay == INVALID). Actualiza o número de dias do ano (yearDay) se não estiver actualizado (yearDay == -1) .
WeakDay Date::getWeakDay(){if (yearWeakDay == INVALID) yearWeakDay = getYearWeakDay(); if (yearDay == -1) yearDay = getYearDay();return WeakDay((yearWeakDay+yearDay) % DAYS_OF_ONE_WEAK);
}
Classes em C++ 5 - 20 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Palavra chave this• Os métodos operam sobre os objectos para os quais sejam
chamados (ou evocados). • Um apontador para o objecto para o qual o método é invocado
(objecto receptor), constitui um argumento oculto (hidden) dessa função que pode ser explicitado referindo-o como this.
• Por exemplo, test é um objecto da classe Date e getDay é um método da classe Date, a chamada à função:
test.getDay()opera sobre o objecto test. Por outras palavras, test é o receptor da mensagem getDay(). De modo semelhante, se testPtr é um apontador para um objecto tipo Date, a chamada à função:
testPtr->getDay() // (*testPtr).getDay()
opera sobre o objecto apontado por testPtr.
Classes em C++ 5 - 21 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Palavra chave this• Um apontador para o objecto para o qual o método é invocado
(objecto receptor), constitui um argumento oculto (hidden)
desse método que pode ser explicitado referindo-o como this.
• Nos métodos não constantes, de uma classe X, o apontador this é implicitamente declarado como:
//*this é um objecto do tipo X X* const this;
nos métodos constantes, para prevenir modificações do objecto receptor, como:
//*this é um objecto do tipo const Xconst X* const this;
tanto num caso como noutro é iniciado para apontar para o objecto sobre o qual esses métodos sejam invocado.
Classes em C++ 5 - 22 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
• O uso do apontador this é implícito. Qualquer referência a um membro na definição dos métodos da classe, usa o apontador this explicita ou implicitamente para aceder ao objecto apropriado.
Palavra chave this
Quando pretendemos referir membros do objecto receptor (objecto sobre o qual se evocou o método) é desnecessário usar explicitamente o this.
class Count { const unsigned mod; unsigned val;public: Count(unsigned m){mod=m; val=0; } //Métodos inline — implicitamente. unsigned get() const {return val; } void set(unsigned v) {val = v%mod;} void inc() {if (++val==mod) val=0;}};
class Count { const unsigned mod; unsigned val;public: Count(unsigned m) {this->mod = m; this->val = 0; } //Métodos inline — implicitamente. unsigned get() const {return this->val; } void set(unsigned v) {this->val = v % this->mod; } void inc() {if (++this->val==this->mod)this->val=0;}};
Equivalente
Classes em C++ 5 - 23 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Retornar o objecto receptorDefinição de um método da classe Date, que incrementa uma data (dia seguinte) e retorna a data depois de incrementada .
Date Date::inc() { static int monthDays[12]={31,29,31,30,31,30,31,31,30,31,30,31}; if ( yearDay != -1 ) ++ yearDay; if ( month==2 && day>=28+leapYear(year) || day >= monthDays[month-1]) {
day = 1;if (month == 12) { month = 1; yearDay = 0; if ( yearWeakDay != INVALID)
yearWeakDay = WeakDay((yearWeakDay + leapYear(year)? 2 : 1) % DAYS_OF_ONE_WEAK); ++year; } else ++month; } else ++day; return *this;//Retorna o objecto receptor.}
Classes em C++ 5 - 24 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Objecto receptor como parâmetro actual Definição de um método da classe Date, que retorna o número de dias que medeiam entre duas datas .
long Date::getDays( Date other ) { if ( cmp(other) > 0 ) return subNorm(other);//this->subNorm(other) return - other.subNorm( *this );}
Objecto receptor como parâmetro actual
Classes em C++ 5 - 25 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Membros estáticos• Um atributo de uma classe declarado estático, significa que
é partilhado por todos os objectos dessa classe.
• Trata-se de um atributo da classe. Dos atributos comum duma classe são feitas tantas instâncias quantos os objectos instanciados dessa classe, enquanto que dos atributos estáticos só é feita uma única instancia, independentemente do número de objectos dessa classe que se instanciem, ou mesmo que não se instancie nenhum objecto dessa classe.
• Um atributo estático comporta-se como uma variável global, salvo que não polui o espaço de nomes globais, dado que, mesmo que seja um membro público.
Classes em C++ 5 - 26 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Atributos estáticosQuer os atributos quer os métodos de uma classe, podem tomar a natureza estática, antecedendo a sua declaração pela palavra chave static.
class X {public: static int countXs;//Exclusivamente declaração. X () { ++countXs; }; // ...};int X::countXs=0;// Definição e iniciação.
void main() { X arr[7]; // Constrói 7 objectos X construtor // sem parâmetros - X::countXs = 7. cout<<"Foram instanciados "<<X::countXs << " objectos da classe X." endl; }
Um atributo de uma classe declarado estático, significa que é partilhado por todos os objectos dessa classe.
O atributo estático countXs é incrementado de 1 por cada objecto X instanciado.
Realça-se o facto de se tornar necessário definir fora da definição da classe, e eventualmente iniciar, os atributos declarados como estáticos.
Um atributo estático, quando invocado fora da classe é qualificado pelo nome da
classe. Quando este programa for executado será escrita a mensagem:
“Foram instanciados 7 objectos da classe X”.
Classes em C++ 5 - 27 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Métodos estáticosOs métodos estáticos de uma classe, comportam-se como funções globais, salvo que não poluem o espaço de nomes.
class X { static int countXs;//Atributo privado. public: X () { ++countXs; }; static int getCount() // Método público. { return countXs; } // ...};int X::countXs=0;//Definição e iniciação
void main() { X arr[7];// Constrói 7 objectos X com construtor
// sem parâmetros countObjects = 7. cout << X::getCount() << endl; }
Ao declarar uma função como método estático de uma classe em lugar de a declarar como função global, além de não se poluir o espaço dos nomes pode-se controlar o seu acesso ou o acesso aos atributos estáticos da classe.
Apesar de tolerarem ser invocados com a sintaxe dos métodos comuns, os métodos estáticos não atendem para qual dos objectos da sua classe são evocados, não sendo sequer necessário haver algum objecto instanciado dessa classe para que possam ser evocados.
Como os métodos estáticos não têm objecto receptor, o argumento oculto this não existe.
Classes em C++ 5 - 28 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Construtor sem parâmetros• Um construtor evocado sem parâmetro é referido
como construtor default (por omissão). A razão deste facto prende-se com os seguintes factos:
Caso não se defina nenhum construtor para a classe, é implicitamente definido um construtor com corpo vazio na parte publica. Ao instanciar um objecto sem indicar uma lista de parâmetros, é reservado reserva espaço para os seus atributos, sem no entanto lhes atribuir valores.
• Se se definir, pelo menos, um construtor com parâmetros, não é implicitamente definido o construtor sem parâmetros, pelo que só se pode instanciar objectos com os construtores explicitamente definidos.
Classes em C++ 5 - 29 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Arrays de Objectos• Sempre que se pretenda definir um array de objectos, é imprescindível a
existência de construtor sem parâmetros (implícito ou explícito), o que implica que, ou não se defina nenhum construtor com parâmetros, ou que se defina explicitamente um construtor sem parâmetros.
class Date { ...public: Date(int yy=0, int mm=0, int dd=0); ...};
Date dates[10];//Os 10 objectos são //iniciados por omissão //com (0,0,0).
class Date { ...public: Date(int yy, int mm=0, int dd=0); ...};
Date dates[10]; //ERRO, não existe //construtor por omissão.
Instanciar um array de 10 objectos do tipo Date, cada um iniciado com o construtor sem parâmetros, por omissão (0, 0, 0).
Não é licito a instanciação do array porque o construtor declarado requer pelo menos um argumento.
Classes em C++ 5 - 30 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Construtor - como conversor de tipo• Um construtor com um único argumento, especifica uma
conversão do tipo do argumento para o tipo da sua classe. Tais conversões são usadas implicitamente para coerção automática.
Tendo em conta a definição:class X { ... X(int); }; void f (X arg) { ... }
São possíveis as instrução:
X a = 1; // X a(1)a = 2; // a = X(2) f(3); // f(X(3))X g() { ...return 1; }
class X { ... X(int); ... }
class Y { ... Y(X); ... };
Y a = 1; // Ilegal: Y(X(1)) // não foi tentado.
As conversões implícitas podem ser usadas em afectações, iniciadores, argumentos de funções e valores retornados por funções.
Se nenhum construtor para a classe X aceita o tipo atribuído, não é esboçada nenhuma tentativa de coerção para compatibilização de tipos, e o compilador assinala erro.
Classes em C++ 5 - 31 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
DestrutorEm função das acções executadas pelo construtor, pode tornar-se necessário declarar um destrutor por forma a complementar as suas acções.
/*A classe CharPtr, que tem como finalidade despreocupar o programador quanto à libertação da memória reservada para um array de caracteres através do operador new.*/
class CharPtr {char *ptr; // Atributo dinâmico.
public: // Construtor. CharPtr(int len){ ptr = new char[len]; } // Destrutor. ~CharPtr() { delete [] ptr; } // Apontador. char *getPtr() { return ptr; }};
•A assinatura de um destrutor para uma classe X será ~X() (complemento de construtor). O destrutor não tem parâmetros nem tipo de retorno porque é invocado implicitamente.•Normalmente, as acções do destrutor são complementares às do construtor. Assim, no caso do construtor reservar espaço em memória livre através do operador new, o destrutor deve providenciar a devolução desse espaço, através do operador delete.•Quando um objecto é destruído e caso esteja definido na classe desse objecto um método destrutor, este será posto em execução automaticamente.
Classes em C++ 5 - 32 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
DestrutorQuando um objecto é destruído e caso esteja definido na classe desse objecto um método destrutor, este será automaticamente posto em execução.
Um objecto é destruído:• Ao terminar o programa, se for global ou estático; Ao terminar o bloco ou a função onde foi declarado, se não for estático; Quando for destruído o objecto em que está contido; Quando o array do qual é elemento for destruído; No final da avaliação da expressão, se for um objecto temporário dela; Quando for evocado o operador delete sobre ele.
Classes em C++ 5 - 33 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Destrutor ( exemplo de um espião )
O uso do método destrutor na classe Spy, permite que se espie a função factorial em cada instância da recursividade.
class Spy{ int s; public:
Spy(int ss) { cout << "Inicio - " << (s = ss) << endl;}~Spy() { cout << "Fim - " << s << endl; }
};
int factorial(int n) {Spy s(n);return (n > 1) ? n * factorial(n-1) : 1 ;
}
Classes em C++ 5 - 34 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Exemplo de utilização do destrutorPodem também utilizar-se construtores e destrutores para criar segmentos de programa que sejam executados antes e depois da execução da função main(). Com efeito, os objectos que são definidos como globais, são instanciados antes da execução de main(), e só desaparecem quando o programa termina.
#include <iostream.h>
class Message {public: Message() { cout << "Inicio do programa." << endl;} ~Message(){ cout << "Fim do programa." << endl; }};
Message msg; // Instanciação do objecto msg.void main() { cout << "MAIN ... " << endl; }
Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Alojamento na memória livre
Programação em C++
Classes em C++ 5 - 36 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Alojamento de dados• A linguagem C++ utiliza a memória principal de três
modos diferentes: Modo estático – Os objectos alojados neste modo persistem até
terminar o programa, mantendo sempre o mesmo endereço. São alojadas deste modo os atributos estáticos das classes, os objectos globais, e os objectos declarados como estáticos no corpo das funções.
Modo automático – Os objectos alojados neste modo são automaticamente criados e destruídos. Tomam este modo de alojamento os parâmetros actuais das funções e os objectos locais não estáticos são alojadas deste modo.
Alojamento livre – Neste modo de alojamento o espaço de memória é explicitamente requerido e explicitamente devolvido.
Classes em C++ 5 - 37 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Alojamento de dados• Variáveis e objectos instanciados como estáticos,
residem na zona de dados do programa.• Variáveis e objectos instanciados como
automáticos (locais não estáticos), com tempo de vida limitado ao seu alcance, são alojados no stack.
• A região de memória livre disponível em tempo de execução de um programa, refere-se como heap ou memória livre.
Classes em C++ 5 - 38 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Alojamento na memória livre• Os objectos criados em tempo de execução,
diremos que são criados dinamicamente. Quando se instancia objectos em tempo de execução, é reservado espaço para o seu alojamento e iniciado esse espaço com os valores passados como parâmetros ao construtor.
• A memória livre aceita reservas de memória para objectos alojados dinamicamente através do operador new, e desalojados através do operador delete.
Classes em C++ 5 - 39 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Operador new• O operador new reconhece a classe do objecto e chama
automaticamente o construtor dessa classe para iniciar o bloco de memória que vai alojar. Retorna um apontador para o objecto instanciado, ou um apontador NULL se houve insucesso.
• Quando se pretende instanciar um elemento isolado, a sintaxe abreviada deste operador é a seguinte:• new type_name(<list_of_parameters>)
• Caso se pretenda instanciar dinamicamente um array de elementos da classe type_name, a sintaxe será:• new type_name [number_of_elements]
como em qualquer array, cada um dos elementos será iniciado com o construtor sem parâmetros ou o ou o construtor em que todos os parâmetros possam ser omitidos.
Classes em C++ 5 - 40 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Operador new• O operador new instancia um objecto da classe type_name alojando sizeof(type_name) bytes na memória livre (heap), e chama o construtor em correspondência com o número de parâmetros explícitos, assegurando assim a adequada iniciação do objecto. O alojamento do objecto persiste até que o operador delete promova o seu desalojamento (destruição) ou até ao fim do programa, altura em que toda a memória livre é libertada de uma só vez.
• Por exemplo a instrução:
Date *datePtr = new Date(1998, 10, 15); reserva espaço e inicia a data em 15 de Outubro de 1998.
Classes em C++ 5 - 41 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Operador delete• O operador delete, desaloja da memória objectos
instanciados através do operador new. Devolve à memória livre o bloco de memória ocupado pelo objecto, após realizar todas as acções que estejam explicitadas no corpo do destrutor do objecto desalojado. O operador delete é invocado com o nome do apontador para o objecto a desalojar.
delete pointer;
• No caso de se pretender desalojar um array de objectos, intercala-se entre o operador delete e o seu
operando apontador a notação []. delete [] pointer;
Classes em C++ 5 - 42 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Operador delete• Resultam erros imprevisíveis da aplicação
do operador delete sobre um apontador que não corresponda a uma acção de new, ou da aplicação repetida do operador delete sobre o mesmo apontador.
• É indefinida a acção do operador delete se for omitida a notação [] em arrays.
• É licito, a aplicação do operador delete sobre um apontador de valor NULL.
Classes em C++ 5 - 43 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Operadores new e delete• Relativamente à actuação dos operadores new e delete, devemos reter que: O operador new não se limita a reservar espaço
para um objecto residir, mas providencia também a sua iniciação, conforme a definição de um construtor explícito desse objecto.
O operador delete quando invocado sobre o apontador para um objecto alojado dinamicamente, não se limita a devolver o espaço de memória do objecto a destruir. Põe previamente em execução a função destrutor definida para a classe desse objecto.
Classes em C++ 5 - 44 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe CharPtrA classe CharPtr, tem como finalidade despreocupar o programador quanto à libertação da memória reservada para um array de caracteres através do operador new.
class CharPtr {char *ptr; // Atributo dinâmico.
public: // Construtor CharPtr(int len) { ptr= new char[len];} // Destrutor. ~CharPtr() { delete [] ptr; } // Apontador. char *getPtr() { return ptr; }};
.
Classes em C++ 5 - 45 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Contentores• Uma larga variedade de objectos são por natureza
dinâmicos, isto é, são susceptíveis de variar o espaço reservado para os seus dados ao longo da sua existência.
• As classes contentoras de objectos ( Strings, Tabelas, Filas de Espera, Listas, etc.) destinadas a conter objectos de outras classes, e cujo montante é impossível de prever no momento da sua definição, pelo que o espaço para eles reservado terá de ser ajustado ao longo do programa.
• Declarar um membro apontador numa classe, revela normalmente a natureza dinâmica das instâncias da classe.
Classes em C++ 5 - 46 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Atributos com alojamento dinâmico
• Diremos que um objecto tem natureza dinâmica se algum dos seus atributos for apontador para estruturas residentes na memória livre (criadas através do operador new).
• Diremos que um objecto foi alojado dinamicamente se a sua instanciação for efectuada através do operador new, isto é, resida na memória livre e a sua destruição seja em run-time (tempo de execução) explicitamente determinada em programa através do operador delete.
Classes em C++ 5 - 47 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe Stringclass String { char *str; int cap;// Dimensão do espaço reservado para a cadeia. int len;// Numero de caracteres válidos da cadeia.public:
String(const char *s=NULL, int len=-1); ~String(); int lenght() const;int capacity() const;const char *str_c() const;void assign(const char *, int len=-1);
void assign(const String &);const char at(unsigned index) const;void at(unsigned index, char c);
};
Classes em C++ 5 - 48 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
inline String::String( const char *s, int length= -1 ) {str = NULL; len = cap= 0; assign(s, length);
}
inline int String:: length() const { return len; }
inline int String::capacity() const { return cap-1; }
inline const char * String::str_c() const{ return str; }
inline char String::at( unsigned index ) const{ assert(index < length()); return str[index];}
inline void String::at( unsigned index, char c){ assert(index < length()); str[index]=c;}
inline void String:: assign(const String &s) { assign(s.str, s.len); }
Classe String (definição dos métodos inline)
Classes em C++ 5 - 49 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Classe String - método assign()
Se o espaço em memória livre que o objecto disponha não for suficiente para conter a cadeia de caracteres que se lhe pretende afectar, será necessário:• reservar um novo espaço com dimensão suficiente;• devolver ao espaço livre a memória onde residia a cadeia anterior
// Afecta String com l caracteres da cadeia st.void String::assign(const char *st, int l) { if(l==-1) l = strlen(st); if (l+1>cap) { // Se espaço insuficiente. delete[] str; // Sem efeito se s=NULL. str=new char[sz=l+1];// Reserva novo espaço. } strncpy(str,st,len = l);// Copia os caracteres. str[len]=0; // Terminador da string.}
Classes em C++ 5 - 50 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Operador afectaçãoSe não for definido explicitamente um operador de afectação, o compilador usa o operador afectação por omissão que se limita a copiar membro a membro.
A função f() construiu dois objectos String, ao executar a afectação s1=s2 (s1.str é afectado com s2.str), a string "Blablabla" fica perdida algures na memória livre, e os objectos s1 e s2 ficam ambos a apontar para o mesmo endereço da memória livre (s2.str), onde reside a string "Blublu”.Ao terminar a execução de f(), é evocado automaticamente o destrutor para s1 e para s2, a operação delete será executada duas vezes sobre um mesmo apontador e não será feito delete do bloco de memória onde continuará a residir a string "Blablabla". Enfim, uma barafunda.
void f() {
String s1("Blablabla" ), s2("Blublu”); s1 = s2; // Afectação não definida, copia membro a membro.}
Classes em C++ 5 - 51 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Operador AfectaçãoConsideremos a class String:
class String { char *str; int len; public: String(int l) {str=new char[(len=l)+1]; } ~String( ) { delete [] str; }};
void f() { String s1(10), s2(15); s1 = s2;}
A afectação de s1 por s2, coloca s1.str e s2.str a apontar para a mesma “string”. No final da função seriam feitos 2 deletes sobre o mesmo apontador e não seria feito delete sobre o apontado inicialmente por s1.str.
SoluçãoDefinição do operador afectação (=)para String
class String { … public: String(int l) { str = new char[(len=l)+1]; } ~String( ) { delete[] str; } String &operator=(const String &); };
Classes em C++ 5 - 52 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Construtor por cópiaSe não for definido explicitamente um construtor por cópia, o compilador gera por omissão um construtor por cópia que inicia o objecto com a cópia membro a membro.
Se não se providenciar a adequada definição do construtor por cópia, é criado um objecto s2 iniciado com o seu apontador s2.str a apontar para a string de caracteres pertencente a s1 (s1.str). Advinha-se as consequências do facto… Quando terminar a execução de f(), e for invocado (automaticamente) o destrutor para s1 e s2, será executada a operação delete duas vezes sobre um mesmo apontador. Enfim, nova barafunda.
void f() { String s1(“Blabla”); String s2=s1; // Equivalente a String s2(s1). // Iniciação membro a membro de s2 com s1.}
Classes em C++ 5 - 53 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Construtor por cópiaConsideremos a class String:
class String { char *str; int len; public: String(int l) { str=new char[(len=l)+1]; } ~String( ) { delete[] str; }};
void f() { String s1(10); String s2 = s1;}
A iniciação de s1 por s2, coloca s1.str e s2.str a apontar para a mesma “string”. No final da função seriam feitos 2 deletes sobre o mesmo apontador .
SoluçãoDefinição do construtor por cópia paraString
class String { … public: String(int l) { str = new char[(len=l)+1]; } String(const String &); ~String( ) { delete[] str; } String &operator=(const String &); };
Classes em C++ 5 - 54 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe Stringclass String { char *str; int cap;// Dimensão do espaço reservado para a cadeia. int len;// Numero de caracteres válidos da cadeia.public:
String(const char *s=NULL, int len=-1); String(const String&);~String(); int lenght() const;int capacity() const;const char *str_c() const;const char at(int index) const;void assign(const char *s, int lenght=-1);void at(int index, char c);String &operator=(const String&);
};
Classes em C++ 5 - 55 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Operações de afectação e iniciação•Quando se pretenda definir uma classe com atributos dinâmicos sem a pretensão de invocar em programa o construtor por cópia e o operador afectação, recomenda-se (para salvaguarda dos incautos) que estes métodos sejam definidos como privados, para impedir que sejam invocados os métodos default. •Desta forma a invocação inadvertida destes métodos acusará na compilação erro gramatical, evitando erros lógicos mais morosos de resolver.
.
Classes em C++ 5 - 56 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe CharPtrclass CharPtr { int *ptr; // Atributo dinâmico. // Inviabilizar a construção por cópia // e a afectação. CharPtr(const CharPtr&) {} void operator=(const CharPtr&) {}public: // Construtor CharPtr(int len) { ptr= new char[len];} // Destrutor. ~CharPtr() { delete [] ptr; } // Apontador. char *getPtr() { return ptr; }};
.
Classes em C++ 5 - 57 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe String (método at())
class String { char *str; // Cadeia de caracteres. unsigned cap;// Dimensão do espaço reservado. unsigned len;// Número de caracteres válidos. void assign(const char *s=NULL, int len=-1);public: String(const char *s=NULL, int len=-1); String(const String &); //Construtor por cópia ~String(); int lenght() const; const char *str_c() const; String &operator=(const char *s); char &at(unsigned index); char at const(unsigned index);};
Classes em C++ 5 - 58 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método char &at(unsigned)
Dado que o método retorna uma referência para carácter, tanto pode ser invocado no lado esquerdo de uma afectação (para escrita), como do lado direito (para leitura).
Os métodos at, distinguem-se pela natureza const de um deles que será o único que pode ser invocado sobre objectos String declarados como constantes. O outro método, será posta em execução quando invocada por um objecto não constante.
.
Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Contentores
Programação em C++
Classes em C++ 5 - 60 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Contentores • De entre os tipos de dados que numa dada
aplicação importa que o utilizador defina e utilize no programa, emerge com destaque uma família numerosa referida como classes contentores.
• Referem-se por contentores, as classes destinadas a conter objectos de um qualquer tipo, disponibilizando ao utilizador métodos cómodos de acesso (inserção, remoção, pesquisa ou ordenação) aos objectos contidos.
Classes em C++ 5 - 61 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Contentores • Conforme os métodos disponíveis na sua interface, os
contentores tomam diferentes denominações:• Stack (pilha)
• Queue (fila de Espera)
• Deque (double-ended queue)
• Bag (saco),
• Set (conjunto),
• Vector, etc.
• Cada contentor é caracterizado pelo conjunto de métodos que põe disponíveis e pela estrutura de dados em que se suporte.
Classes em C++ 5 - 62 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Contentores • Aos contentores exige-se fundamentalmente um bom
desempenho frente à especificidade das aplicações que os utilizam. Compreende-se por isso as múltiplas variantes de contentores que fazem parte de uma biblioteca, com estruturas de dados de suporte alternativos.
• Deve-se escolher a variante de implementação do contentor que mais se adequar às exigências da aplicação, tendo em conta:• os métodos de acesso predominantes;• a dimensão média que se preveja que o contentor tome;• as limitações de memória de dados da plataforma
hardware em que se instale.
Classes em C++ 5 - 63 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Classe contentora - Stack• O termo stack (empilhamento) refere de um modo genérico
o armazenamento de objectos em coluna (pilha) tal que o primeiro a ser inserido seja o último a ser removido. A arrumação de pratos em pilha, é paradigmático deste contentor.
• O acesso à pilha, quer para inserir quer para remover, é feito exclusivamente pelo topo.
• A classe contentora que implementa o stack, é caracterizada por disponibilizar os métodos públicos push() para inserir um objecto no topo do stack e pop() para extrair um objecto do topo do stack.
pushpop
Classes em C++ 5 - 64 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe CharStackclass CharStack{ int size; char * top, *buffer; //Inviabilizar construção por cópia e a afectação. CharStack (const CharStack&){}//Construção por cópia. void operator=(const CharStack &){} //Afectaçãopublic: CharStack(int sz){ top=buffer=new char[size=sz]; } ~CharStack () { delete [] buffer; } void push(char c){ assert(!full()); *top++ = c;} char pop() { assert(!empty()); return *--top; } bool full()const { return top-buffer==size; } int empty() const { return top == buffer; }};
Classes em C++ 5 - 65 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Classe contentora - CharStack• Além dos métodos públicos push() e pop(), dispõe dos
métodos:• empty() - testa se o stack não contem objectos;• full() - testa se já não dispõe de espaço para armazenar
mais objectos;
• Construtor - reserva na memória livre (heap) um bloco de memória para o array de objectos;
• Destrutor - providencia a devolução à memória livre do espaço reservado;
• e dos métodos privados construtor por cópia e operator =() - inviabilizam a cópia e a afectação.
Classes em C++ 5 - 66 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição das classes CharStack e IntStack
class CharStack{ int size; char * top, *buffer;public: CharStack(int sz){ top=buffer=new char[size=sz]; } ~CharStack () { delete [] buffer; } ~CharStack () { delete [] buffer; } void push(char c){ assert(!isFull()); *top++ = c;} char pop() { assert(!isempty()); return *--top; } bool full()const { return top-buffer==size; } int empty() const { return top == buffer; }};
class IntStack{ int size; int * top, *buffer;public: IntStack(int sz){ top=buffer=new int[size=sz]; } ~IntStack () { delete [] buffer; } void push(int c){ assert(!isFull()); *top++ = c;} int pop() { assert(!isempty()); return *--top; } bool full()const { return top-buffer==size; } int empty() const { return top == buffer; }};
Classes em C++ 5 - 67 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe Stack
/*Por mera mudança das definições de char ou int por T, podemos redefinir o Stack para trabalhar com qualquer tipo de objectos. */typedef char T;class Stack{ int size; T * top, *buffer;public: Stack(int sz){ top=buffer=new T[size=sz]; } ~Stack () { delete [] buffer; } ~CharStack () { delete [] buffer; } void push(T c){ assert(!isFull()); *top++ = c;} T pop() { assert(!isempty()); return *--top; } bool full()const { return top-buffer==size; } int empty() const { return top == buffer; }};
Classes em C++ 5 - 68 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Estrutura array ordenado A estrutura array ordenado, quanto a desempenho, apresenta
as seguintes características, no que se refere à: Pesquisa de um elemento, o array tem um excelente
desempenho, se adoptarmos pesquisa dicotómica - desempenho da ordem de (log n) sendo n o número de elementos a ordenar;
Remoção de um elemento, é altamente penalizante, dado que implica deslocar elementos. Se o elemento a remover se situar no início do array e o array contiver n elementos, implica executar n transferências de elementos;
Inserção de um elemento, ainda se pode tornar-se mais gravoso, na medida em que, além de ter de abrir espaço, se o array já não comportar mais nenhum elemento é necessário criar novo espaço mais dilatado.
Classes em C++ 5 - 69 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Estrutura Lista• Uma alternativa será adoptar uma estrutura lista
ligada, que permita um melhor desempenho das inserções e remoções, mesmo que penalizando, relativamente aos arrays, as acções de pesquisa.
• Quanto a desempenho insuportável, tanto pode acontecer com os arrays como com as listas e a opção entre um e outro destes contentores terá de ser feita, frente aos métodos de acesso que uma dada aplicação tenha como mais frequentes, o número de objectos, etc.
Classes em C++ 5 - 70 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Estrutura Lista• Uma lista ligada, pode referir-se como um contentor de
objectos na qual podemos inserir ou remover um objecto em qualquer posição.
• Cada objecto da lista aponta para outro (ou outros) objectos da lista, dizendo-se por isso que os objectos estão ligados ou encadeados entre si.
• Os elementos de uma lista, para os quais não se disponha de apontadores fixos específicos, só podem ser acedidos sequencialmente. Daí o gravoso desempenho das acções de pesquisa relativamente aos arrays, os quais aceitam pesquisa dicotómica.
• A principal virtude das listas reside na facilidade (rapidez) com que permitem a execução de acções de inserção e remoção.
Classes em C++ 5 - 71 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Exemplo de uma lista ordenada de inteiros
• O critério para organizar uma lista simplesmente ligada é ligar todos os nós, colocando cada nó a apontar para o nó seguinte.
• Os objectos nós além de conterem o inteiro a inserir, explicitam o nó seguinte, isto é, têm dois atributos: um data que contem os dados a armazenar e um ptNext que aponta para o node seu sucessor na lista, ou toma o valor NULL quando não haja sucessor.
7 10 NULL4
Classes em C++ 5 - 72 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe IntList
class IntList { struct IntNode { // Definição da classe IntNode
int data;IntNode* ptNext;IntNode(int n) { data=n;}
};IntNode* ptList; // Apontador para o 1o nó
...public:
IntList() { ptList=NULL; } // Inicia a lista~IntList(); // Destroi todos os nodesbool insert(int n); // Inserir ordenadobool remove( int n); // Removerbool contain(int n)const;//Testa se n existebool empty() const; //Testa lista vaziavoid print() const;
};
data ptNext
Classes em C++ 5 - 73 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método para inserir ordenadamente
??ptList
NULLprev next
v
? ?nextprev
va) b)
• Este método percorre sequencialmente a lista com dois apontadores prev e next iniciando prev a NULL e next com ptList (cabeça da lista). Quando a iteração termina distingue duas condições: • (a) A lista está vazia ou o inteiro v a inserir é menor que o
associado ao IntNode na cabeça da lista (prev == NULL).
• (b) Caso contrário (prev != NULL) insere o novo nó como sucessor do nó apontado por prev.
Classes em C++ 5 - 74 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método para inserir ordenadamentebool IntList:: insert( int val ) { IntNode *prev, *next; if( find(val, prev, next) )
return false; // Reserva memória para o novo nó. IntNode * ins= new IntNode(val); //Ligar o novo IntNode. if ( prev != NULL )
prev->ptNext= ins;//Ligar o anterior ao novo. else ptList = ins; //Ligar a cabeça ao novo. ins->ptNext= next;//Ligar o novo ao seguinte. return true;}
Classes em C++ 5 - 75 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método find()
bool IntList::find(int val, IntNode*& prev, IntNode*& next) const{ for ( next = ptList, prev = NULL;
next && next->data < val; prev = next, next= next->ptNext);
return next && next->data == val;}
7 10 NULL4ptList
pt
Classes em C++ 5 - 76 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método para remover ordenadamente• Percorre sequencialmente a lista com dois apontadores prev
e curr iniciando prev a NULL e curr com ptList (cabeça da lista). Quando a iteração termina distingue duas condições:
• Caso o nó a remover seja o primeiro da lista (prev == NULL, o nó será removido actualizando o apontador ptList;
• Caso contrário (prev != NULL), actualiza-se o sucessor do nó apontado por prev.
7 10 NULL4ptList
nextprev
Classes em C++ 5 - 77 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método para remover ordenadamente
bool IntList:: remove(int val) {IntNode *prev, *next;if( !find(val, prev, next) )
return false;if ( prev )
// Desliga do anterior.prev->ptNext =next->ptNext;
else // Desliga da cabeça.ptList = next->ptNext;
// Liberta a memória. delete next;return true;
}
Classes em C++ 5 - 78 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Métodos contain(), print()e destrutor
bool IntList:: contain( int val ) const { for( IntNode *pt=ptList;//Apontar para o inicio.
pt && pt->data < val; pt=pt->ptNext); //Avançar para o próximo.
return pt && pt->data == val;}
void IntList::print() const { for( IntNode* pt =ptList; pt; pt = pt->ptNext)
cout << pt->data << ' '; cout << endl;}
IntList::~IntList() { while(!empty()) { IntNode *aux=ptList; ptList= ptList->ptNext; delete aux; }}
Classes em C++ 5 - 79 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Lista com sentinela.
• Permite a simplificação dos algoritmos dos métodos de inserção e remoção, dado que deixa de ser necessário considerar separadamente o caso da inserção e remoção á cabeça.
7 104?sentinela
• O atributo da lista é um nó auxiliar (guard), iniciado em anel.
?sentinela
Classes em C++ 5 - 80 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe IntList com sentinela
class IntList { struct IntNode { int data; IntNode* ptNext; IntNode(int n, IntNode *n) { data=n; ptNext = n;} IntNode() {data=0;ptNext=this;} //Construir guard. }; IntNode guard;//Auxiliar para simplificar os métodos. ...public:
~IntList(); // Destroi todos os nodesbool insert(int val); // Inserir ordenadobool remove( int val); // Removerbool contain(int val)const;//Testa se val existebool empty() const; //Testa lista vaziavoid print() const;
};
Classes em C++ 5 - 81 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método insert()
bool IntList::insert( int val ) {IntNode *prev, *next;if( find(val, prev, next) )
return false;prev->ptNext= new IntNode(val, next);return true;
}
/*Na lista sem nó guard { IntNode *prev, *next; if(find(val,prev,next))return false; IntNode * ins= new IntNode(val); //Criar o novo nó. if ( prev!=NULL ) prev->ptNext= ins;//Ligar-se ao anterior else ptList = ins; //ou à cabeça. ins->ptNext= next; //Ligar-se ao seguinte. return true;} */
Classes em C++ 5 - 82 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método find()
bool IntList::find( int val, IntNode*& prev, IntNode*& next ) const { for (guard.data= val, next=guard.ptNext, prev=&guard;
next->data < val; prev = next, next = next->ptNext);
return next != &guard && next->data == val;}
/*Na lista sem nó guard { for ( next = ptList, prev = NULL;
next && next->data < val; prev = next, next= next->ptNext);
return next != NULL && next->data == val;} */
Classes em C++ 5 - 83 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método remove()
bool IntList:: remove(int val) {IntNode *prev, *next;if(!find(val, prev, next))
return false;prev->ptNext = next->ptNext; // Desligar.delete next; // Libertar a memória.return true;
}
/*Na lista sem nó guard { IntNode *prev, *next; if(!find(val,prev,next))return false; if (prev) prev->ptNext=next->ptNext;//Desliga do anterior. else ptList = next->ptNext; //Desliga da cabeça. delete next; //Liberta a memória. return true;} */
Classes em C++ 5 - 84 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Lista duplamente ligada.
sentinela?
Mantém-se o critério de utilizar como atributo da IntDList umIntDNode auxiliar (guard) com fecho em anel .
Permite:• Simplificar ainda mais os algoritmos de inserção e remoção;• Acesso sequencial nos dois sentidos.
O critério para organizar uma lista duplamente ligada é ligartodos os nós, colocando cada nó a apontar para o nó seguinte e para o nó anterior.
10sentinela
74?
Classes em C++ 5 - 85 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Definição da classe IntDList
class IntDList { struct IntDNode {
int data;IntDNode* ptNext, *ptPrev;IntDNode( int n, IntDNode *next);IntDNode();~IntDNode();
}; IntDNode guard;//Auxiliar para simplificar. ...public:
~IntDList(); // Destroi todos os nodesbool insert(int n); // Inserir ordenadobool remove( int n); // Removerbool contain(int n)const;//Testa se n existebool empty() const; //Testa lista vaziavoid print() const;
};
Classes em C++ 5 - 86 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Construtores do IndDNode
// Iniciaçãoinline IntDNode::IntDNode() {
ptNext = ptPrev = this; }
// Inserção de um node na listainline IntDNode::IntDNode(int val,IntDNode*next){ data = val; ptNext = next; ptPrev = next->ptPrev; ptNext->ptPrev = ptPrev->ptNext = this;}
??
next
valthis
sentinela?
Classes em C++ 5 - 87 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Destrutor do IndDNode (remoção).
7 10 NULL4ptList
pt
// O nó desliga-se da lista.IntDNode::~IntDNode() {
ptPrev->ptNext = ptNext;ptNext->ptPrev = ptPrev;
}
Classes em C++ 5 - 88 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método insert()
bool IntDList:: insert(int val) {IntDNode *next;if ( find(val, next) )
return false;new IntDNode(val, next);return true;
}/* Simplesmente ligadabool IntList::insert(int val){
IntNode *prev, *next;if( find(val, prev, next) ) return false;prev->ptNext= new IntNode(val, next);return true;
}*/
Classes em C++ 5 - 89 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método remove()
bool IntDList:: remove(int val) {IntDNode *next;if(!find(val, next))
return false;delete next;return true;
}
/* Simplesmente ligadabool IntList:: remove(int val) { IntNode *prev, *next; if(!find(val, prev, next)) return false; prev->ptNext = next->ptNext; delete next; return true;} */
Classes em C++ 5 - 90 Programação em C++Pimenta RodriguesPedro PereiraManuela Sousa
Método find()
bool IntDList::find(int val,IntDNode *&next){for (guard.data = val, next=guard.ptNext; next->data < val;
next=next->ptNext);return next!=&guard && next->data==val;
}
/*Na lista simplesmentebool IntList::find(int val, IntNode*& prev,IntNode*& next)const{ for (guard.data= val, next=guard.ptNext, prev=&guard;
next->data < val; prev = next, next = next->ptNext);
return next != &guard && next->data == val;}*/