1. Classes
1.1. Palavra chave this
1.2. Palavra chave friend
1.3. Palavra chave const
2. Membros estáticos
2.1. Dados estáticos
2.2. Funções estáticas
1.1. Palavra chave this
A palavra this é definida dentro de um objecto como sendo um
ponteiro para o próprio objecto.
this
Objecto
Se uma função (por exemplo, set_string) for o método de uma
classe (por exemplo, string) então podemos usar nesta função os
nomes dos dados e das funções. Não é necessário usar o nome da
classe, i.e., podemos escrever só o nome dos seus dados.
Consideremos um exemplo.
Vamos examinar as seguintes declarações:
string str1;
string str2;
e invocamos as funções:
str1.display_string();
str2.display_string();
Suponhamos que as duas funções realizam a seguinte instrução:
cout << str<<endl;
Como é que a primeira função sabe que deve usar o dado str
pertencente ao objecto str1 e a segunda função sabe que
deve usar o atributo str pertencente ao objecto str2?
Cada objecto (no nosso exemplo eles são str1 e str2) tem um
ponteiro escondido que o identifica. Outras possíveis
explicações são as seguintes:
Quando declaramos um novo objecto o
compilador reserva a memória
necessária e chama o construtor para
esta zona da memória.
Esta memória tem um campo especial
que indica o início da área reservada
para o objecto. O valor deste ponteiro
pode ser obtido através da palavra
chave this. Cada função (não estática)
que pertence à classe recebe este
ponteiro como um argumento adicional
escondido. O ponteiro para as funções
não constantes é do seguinte tipo:
Memória
this
str1
this
str2
my_class *const this;
 é impossível mudar do valor do ponteiro this;
 é impossível utilizar o endereço do ponteiro this.
Na maioria dos casos o ponteiro this é utilizado de maneira implícita.
void string::display_string()
{
cout << str << endl;
}
void string::display_string()
{
cout << this->str << endl;
}
void string::f()
{
int a;
this = &a;
&this;
}
impossível
class my_class
{
int x,y;
public:
void set (int X, int Y) { x=X; y=Y; }
my_class f (my_class);
my_class* ff (void) { x = y = 100; return this; }
void display () { cout << x << ‘\t’ << y << endl; }
};
my_class my_class::f(my_class M)
{
x += M.x; y += M.y;
return *this;
}
void main(void)
{
my_class A,B;
B.ff()->display();
// O resultado 100 100
A.set(10,20);
A.display();
// O resultado 10
20
A.f(B).display();
// O resultado 110 120
}
Vamos analisar a seguinte linha:
my_class *const this;
1. this aponta para o início da memória do computador onde
o objecto está localizado.
2. O ponteiro this é uma variável local no corpo da função
pertencente à classe, por isso não pode ser acedido fora do corpo
da função.
3. Como this é declarado como *const não pode ser alterado,
mas o objecto por ele apontado pode.
4. O ponteiro this não necessita de ser declarado.
5. O ponteiro this é passado como um argumento escondido para
todas as funções pertencentes à classe dum determinado objecto,
que não sejam do tipo static.
Este ponteiro é bastante útil quando se usam ponteiros e
especialmente, no uso de listas ligadas duplas quando se
quer referenciar um ponteiro para o objecto que se quer
introduzir na lista
class dlink
{
dlink* prev; // elemento anterior
dlink* next; // elemento seguinte
public:
void append(dlink*);
list_head
// ...
};
prev
this
next
prev
p
next
z
void dlink::append(dlink* p)
{
p->next=next;
// equivalente a p->next=this->next
p->prev=this;
// uso explícito de this
next->prev=p;
// equivalente a this->next->prev=p
next=p;
// equivalente a this->next=p
}
prev
prev
prev
next
p
next
this
prev
this
next
prevp
p
z
next
z
void dlink::append(dlink* p)
{
p->next=next;
// equivalente a p->next=this->next
p->prev=this;
// uso explícito de this
next->prev=p;
// equivalente a this->next->prev=p
next=p;
// equivalente a this->next=p
}
dlink* list_head;
// Início da lista
void f(dlink* a, dlink* b)
{
// ...
list_head->append(a); // introdução do elemento a na lista
list_head->append(b); // introdução do elemento b na lista
}
prev
this
next
prev
p
next
z
1.2. Palavra chave friend
Todos os membros privados de uma classe são inacessíveis do exterior. Se quiser
que uma função (não membro) tenha acesso aos membros privados de uma classe,
terá que declarar esta função como amiga (friend).
A declaração de amizade deve ocorrer dentro da declaração da classe.
As amigas de uma classe podem ser funções globais, funções membros de uma
outra classe ou uma outra classe inteira (i.e. todas as funções membros dela serão
amigas).
A declaração de funções friend é feita dentro da classe e começa com a palavra
chave friend. Esta declaração pode ser feita tanto na parte private coma na parte
public da declaração de uma classe.
Uma função global do tipo friend obviamente não tem o ponteiro this.
class X;
class Y
{ public:
class X
{
int i;
void f1(X* x);
void f2(X* x);
public:
X() { i = 0; };
friend void Y::f1(X*); // função f1 da Y é amiga
friend class Z; // toda a classe é amiga
friend void h(); // função global é amiga
};
};
class Z
{
int j;
public:
void Y::f2(X* x)
{
x->i = 33;
}
Z() { X x; j = x.i; };
void g(X* x) { x->i = j; };
};
void Y::f1(X* x)
{
x->i = 22;
}
erro
void h()
{
X x;
x.i = 100; //acesso a membro privado
}
class my_class2;
// declaração forward
class my_class1
{public:
friend void compare(my_class1&, my_class2&);
my_class1(int A) : a(A) {}
// .......................................
private:
int a;
};
class my_class2
{public:
friend void compare(my_class1&, my_class2&);
my_class2(int A): a(A) {}
// .......................................
private:
int a;
};
void compare(my_class1 &cl1, my_class2 &cl2)
{
if(cl1.a==cl2.a) cout << "equal\n";
else cout << " not equal\n";
}
Uma classe que esteja definida dentro de outra (i.e. uma classe
aninhada), não consegue ter acesso à informação que está na parte
private da classe englobante. Muito fácilmente se consegue este
acesso através da declaração de friend da classe aninhada na
classe englobante.
class englobante
{
int a;
public:
class aninhada; //declaração antecipada da classe aninhada
friend class aninhada; //declaração de amiga
class aninhada
// definição da classe aninhada
{
........................
};
};
Exemplo:
Ponteiros inteligentes
GoodArray
Pointer
 As classe GoodArray e Pointer são fortemente relacionados => é razoável
fazer com que Pointer seja uma classe aninhada dentro de GoodArray.
 Dado que Pointer é uma classe separada é possível criar muitos ponteiros e
utilizá-los para seleccionar elementos diferentes do array.
 Dado que Pointer é uma classe (não apenas um ponteiro simples) é possível
garantir que esta nunca ultrapasse os limites do array e vai sempre apontar para
um elemento válido.
const int sz = 5;
class GoodArray
{
int a[sz];
public:
GoodArray ();
class
class Pointer;
Pointer;
friend
friend class
class Pointer;
Pointer;
class
class Pointer
Pointer
{{
GoodArray*
GoodArray* h;
h;
int*
int* p;
p;
public:
public:
Pointer(GoodArray*
Pointer(GoodArray* rv);
rv);
};}
};
classe
aninhada
void
void next();
next();
void
void previous();
previous();
void
void top();
top();
void
void end();
end();
//// passa
passa para
para oo elemento
elemento seguinte
seguinte
//// passa
passa para
para oo elemento
elemento anterior
anterior
//// passa
passa para
para oo primeiro
primeiro elemento
elemento do
do array
array
//// passa
passa para
para oo último
último elemento
elemento do
do array
array
int
int read();
read();
void
set(inti);i);
void set(int
//aceder
aoelemento
elemento
corrente
array
//aceder ao
corrente
do do
array
//modificar
elemento
corrente
array
//modificar ooelemento
corrente
do do
array
GoodArray::GoodArray()
{
memset(a, 0, sz * sizeof(int));
}
void GoodArray::Pointer::top()
{
p = &(h->a[0]);
}
GoodArray::Pointer::Pointer(GoodArray* rv)
{
h = rv;
p = rv->a;
}
void GoodArray::Pointer::end()
{
p = &(h->a[sz - 1]);
}
void GoodArray::Pointer::next()
{
if(p < &(h->a[sz - 1]))
p++;
}
int GoodArray::Pointer::read()
{
return *p;
}
void GoodArray::Pointer::previous()
{
if(p > &(h->a[0]))
p--;
}
void GoodArray::Pointer::set(int i)
{
*p = i;
}
int main(int argc, char* argv[])
{
GoodArray array;
GoodArray::Pointer p1(&array), p2(&array);
for(int i = 0; i < sz; i++)
{
p1.set(i);
}
p1.top(); p2.end();
p1.next();
pointer 1 = 0, pointer 2 = 4
pointer 1 = 1, pointer 2 = 3
pointer 1 = 2, pointer 2 = 2
pointer 1 = 3, pointer 2 = 1
pointer 1 = 4, pointer 2 = 0
for(i = 0; i < sz; i++)
{
cout << "pointer 1 = " << p1.read()
<< ", pointer 2 = " << p2.read() << endl;
p1.next();
p2.previous();
}
return 0;
}
Duas propriedades de funções e classes amigas, é a de amizade não
poder ser herdada nem transitiva. Se uma classe é derivada de outra,
esta classe não mantém as propriedades de amizade para com as
funções que a classe da qual foi derivada mantinha, i.e.
não há herança de amizade.
class Z
{ int j;
public:
Z() { X x;
};
OK
j = x.i; };
class X
{
int i;
public:
X() { i = 0; };
friend class Z;
};
classe Z é
amiga da
classe X
class DZ : public Z
{
public:
DZ ():Z () { X x; }; x.i; };
};
erro
classe DZ não é amiga da classe X!
amiga
amiga
A amizade não é transitiva.
X
Z
Y
não é amiga
class Z
{
OK
int j;
public:
Z() { X x;
j = x.i; };
friend class Y;
};
class X
{
int i;
public:
X() { i = 0; };
friend class Z;
};
classe Y é
amiga da
classe Z
classe Z é
amiga da
classe X
class Y
{
public:
Y () { X x; x.i; };
};
erro
classe Y não é amiga da classe X!
1.3. Palavra chave const
 constantes (para eliminar #define)
 objectos
 ponteiros
 argumentos de funções
 tipos de retorno das funções
 em classes
Constantes (para eliminar #define)
#define NUM_ALUNOS 150
(pre-processador, não há informação
sobre tipos, não há endereço)
const int num_alunos = 150;
(compilador, há informação sobre
tipos, pode-se obter o endereço)
Objectos
Se tiver um objecto cujo valor não se pretende alterar durante o tempo de vida
deste objecto, é melhor declarar o objecto como constante.
int main(int argc, char* argv[])
{
cout << "Introduza uma letra..." << endl;
const int a = cin.get();
cout << "O codigo ASCII da letra " << char(a) << " e "<< a << endl;
return 0;
}
Introduza uma letra...
A
O codigo ASCII da letra A e 65
Ponteiros
const pode ser aplicado ou a ponteiro (i.e. ao endereço que o ponteiro guarda)
ou a objecto para o qual o ponteiro aponta.
char c1 = 'a', c2 = 'b';
const char* letra = &c1;
letra = &c2;
*letra = 'c'; //erro
*letra = c2; //erro
char c1 = 'a', c2 = 'b';
char const* letra = &c1;
=
letra = &c2;
*letra = 'c'; //erro
*letra = c2; //erro
char c1 = 'a', c2 = 'b';
char* const letra = &c1;
letra = &c2;
*letra = 'c';
*letra = c2;
objecto
constante
//erro
ponteiro
constante
Ponteiros
É possível fazer com que ambas as partes sejam constantes, i.e. o ponteiro (o
endereço que o ponteiro guarda) e o objecto para o qual o ponteiro aponta.
char c1 = 'a', c2 = 'b';
const char* const letra = &c1;
letra = &c2; //erro
*letra = c2; //erro
É possível atribuir a um ponteiro constante o endereço de um
objecto não constante.
char c1 = 'a';
const char* const s = &c1;
É impossível atribuir a um ponteiro não constante o endereço de um
objecto constante.
const char c1 = 'a';
char* s = &c1; // erro
Download

ppt1