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.3. Palavra chave const
 constantes (para eliminar #define)
 objectos
 ponteiros
 argumentos de funções
 tipos de retorno das funções
 em classes
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
Arrays de caracteres
As regras mencionadas acima não funcionam com os arrays de caracteres.
O código seguinte vai compilar, mas vai causar um erro durante a execução!
char* str = "sol";
str[1] = 'a';
erro
char str [] = "sol";
str[1] = 'a';
Ok
Funções
void f1 (const int a)
{
a++; //erro
}
const int f2 (int a)
{
return ++a;
}
int main(int argc, char* argv[])
{
f1(7);
const int i1 = f2(6);
int i2 = f2(6);
}
Para os tipos built-in não há nenhuma diferença entre devolução de um valor
constante e um valor não constante.
Devolução de um valor constante torna-se importante para os tipos
abstractos.
Se uma função retorna um objecto por valor constante, o valor devolvido
não pode ser lvalue (i.e. não pode ser modificado).
class my_class
{
int i;
public:
my_class (int ii) : i(ii) {};
my_class inc () { i++; return *this; };
};
my_class f3 (my_class mc)
{
mc.inc();
return mc;
}
não pode ser lvalue
int main(int argc, char* argv[])
{
my_class obj1(3);
my_class obj2 = f3(obj1).inc();
}
const my_class f3 (my_class mc)
{
mc.inc();
return mc;
}
Se passar um endereço numa função, deve fazer este endereço
constante sempre que for possível.
void f4 (my_class*) {}
void f5 (const my_class*) {}
const my_class* const f6() { return 0; }
int main(int argc, char* argv[])
{
my_class obj5(1);
my_class* p1 = &obj5;
const my_class* p2 = &obj5;
f4(p1);
f4(p2); //erro
f5(p1);
f5(p2);
p1 = f6(); //erro
p2 = f6();
}
Uma função que recebe ponteiros
constantes é mais geral!
É melhor passar argumentos nas funções por referências constantes!
1) Para os objectos grandes é muito mais eficiente do que passar por
valor porque não é necessário chamar o construtor de cópia.
2) Passagem por referências constantes garante que a função não vai
alterar o estado do objecto  serviço para o utilizador.
3) Na utilização, a sintaxe é idêntica à passagem por valor  serviço
para o utilizador.
4) É possível passar objectos temporários.
Referências
int a = 47;
int* pa = &a;
*pa = 10;
int& ra = a;
ra = 13;
Uma referência (&) é um nome alternativo para um objecto.
A referência é parecida com um ponteiro constante que é dereferenciado
automaticamente.
•
•
•
A referência tem que ser inicializada no ponto da sua definição.
Uma vez inicializada, não se pode modificar a referência para que esta
referencia qualquer outro objecto.
Não se pode ter referências iguais a NULL.
Referências (cont.)
int* f (int* x)
{
(*x)++;
return x;
}
int& g (int& x)
{
x++;
return x;
}
int main()
{
int a = 0;
f(&a);
g(a);
}
As referências são normalmente
utilizadas em listas de argumentos de funções.
Quando uma referência é utilizada
como um argumento da função,
qualquer modificação da referência
dentro da função resulta na alteração
do objecto fora da função.
Referências (cont. 2)
Com a ajuda das referências pode-se transferir numa função (que aceita
referências constantes) um objecto temporário. Ao contrário disso, é
impossível transferir um objecto temporário numa função que aceita um
ponteiro.
void f (int&) {}
void g (const int&) {}
void p (int*) {}
int main()
{
// f (1);
g (1);
// p (1);
}
Optimização do retorno
const type type::f (const type& r)
{
type tmp (/*argumentos*/);
return tmp;
}
const type type::f (const type& r)
{
return (/*argumentos*/);
}
Constantes em classes
Uma constante definida numa classe representa um valor que depois de
inicializado não pode ser alterado. Contudo cada objecto da classe pode ter
o seu próprio valor da constante!
Portanto é impossível inicializar o valor da constante na definição da classe.
Este trabalho tem de ser feito na lista inicializadora do construtor.
class my_class
{
int i;
const int max;
public:
my_class (int ii, int m) : i(ii), max(m) {};
my_class inc () { if (i < max) i++; return *this; };
};
my_class obj6(1, 50);
my_class obj7(1, 10);
Funções-membros constantes
Uma função-membro declarada como constante pode ser chamada para
objectos constantes e para objectos não constantes.
Assuma-se que uma função membro não constante modifica os dados do
objecto para o qual foi chamada. Portanto tais funções não podem ser
chamadas para objectos constantes.
class my_class
{
int i;
const int max;
public:
my_class (int ii, int m) : i(ii), max(m) {};
my_class inc () { if (i < max) i++; return *this; };
void Display () const { cout << i << endl; };
};
my_class obj6(1, 50);
const my_class obj7(1, 10);
obj6.Display();
obj7.Display();
obj6.inc();
obj7.inc();
//erro
Cada função que não modifica o estado do objecto tem de ser
declarada como constante!!!
Os construtores e destrutores nunca podem ser constantes porque quase
sempre modificam o estado do objecto.
Quando uma função-membro constante é definida fora do corpo da classe,
deve levar o sufixo const porque faz parte do tipo da função:
void my_class::Display () const
{
cout << i << endl;
};
Nas funções-membros constantes o ponteiro this é do tipo:
const my_class *const this;
class string {
char str[25];
public:
void set_string(char* s) { strcpy(str,s); }
void display_string(void) const { cout << str << endl; }
char* return_string(void) {
return str; }
};
string str1;
const string str2;
str1.set_string(“Aveiro”);
str1.display_string();
str2.display_string();
str2.set_string(“erro”);
// objecto não constante
// objecto constante
// OK, objecto não constante pode
// invocar função não constante
// OK, objecto não constante pode
// invocar uma função constante
// OK objecto constante pode
// invocar uma função constante
// ERRO, objecto constante não pode
// invocar função não constante
A diferença entre uma função constante, e uma função não constante
mas com argumentos constantes, é a primeira tornar o ponteiro this
constante não podendo modificar o estado do objecto, enquanto que
a segunda apenas não modifica os argumentos.
O tipo do ponteiro this para uma função constante pertencente
à classe my_class é:
const my_class *const this;
Isto significa que nós não podemos alterar o valor do objecto
(da classe my_class) através de this sem cast explicito, por exemplo:
class my_class
{
int i;
public:
void implicit(void) const { i--;
void explicit(void) const
{
((my_class*)this)->i--;
};
}
// ERRO
}
// OK
Sumário da palavra chave this
1. this é uma nova palavra chave do C++;
2. Qualquer objecto novo pertencente a uma classe tem o seu
próprio ponteiro this.
3. A palavra this é definida dentro de um objecto como sendo um
ponteiro para o próprio objecto.
4. this aponta para o início da memória do computador onde está o
objecto.
5. O ponteiro this é uma variável local, que não pode ser acedida
fora do corpo da função.
6. O ponteiro this é apenas acessível em funções não static.
7. O ponteiro this não necessita de ser declarado porque é uma
palavra chave.
8. O ponteiro this pode ser explicitamente usado numa função
tal como this ou *this.
9. this é declarado como *const logo não pode ser alterado, mas o
objecto por ele apontado pode.
10. O ponteiro this é passado como um argumento escondido para
todas as funções, que não seja do tipo static, pertencentes à classe
dum determinado objecto.
Sumário da palavra chave friend
1. Funções friend são funções globais, logo não pertencem à
respectiva classe. No entanto têm acesso a todos os dados e funções
pertencentes à classe independentemente dos seus atributos.
2. A utilização de funções friend quebram o escudo protector que
uma classe tem por defeito. Por isso só se devem usar em funções
estritamente necessárias.
3. A utilização de funções friend permitem um certo acesso
aos dados pertencentes à classe.
4. Se funções friend não são membros de uma classe, estas são
chamadas como vulgares funções globais.
5. Se funções friend não são membros de uma classe, estas não têm
o ponteiro this.
6. Uma classe B que seja definida como friend de outra classe A, não
implica que A tenha acesso aos membros privados de B.
7. Duas propriedades de funções e classes amigas, é a de amizade
não poder ser herdada nem transitiva.
Sumário da palavra chave const
1. Constantes são identificadores que são associados a valores fixos.
2. As constantes têm de ser inicializadas na altura da sua declaração.
3. O valor de variáveis do tipo const não pode ser modificado após a
inicialização.
4. O uso de constantes aumenta a facilidade de leitura e compreensão
de um programa.
5. O uso de constantes facilita bastante a alteração de parâmetros
num programa.
6. Constantes do tipo macro-based são herdadas do C (a directiva do
compilador #define).
7. Constantes do tipo formal usam a directiva do compilador const.
8. Funções constantes não alteram o estado da classe à que pertencem.
9. Funções constantes implicam ponteiro this constante.
10. Funções não podem alterar argumentos constantes.
11. Funções constantes podem ser invocadas por objectos constantes e
não constantes.
12. Funções não constantes apenas podem ser invocadas por
objectos não constantes.
Agora vamos considerar alguns exemplos que vão demonstrar
algumas construções diferentes da linguagem C++
int b=10;
class my_class
{
int b;
int a;
public:
void display() { cout << a << '\t' << b << endl; }
my_class(int a) { my_class::a=a; b =::b; }
};
int main(int argc, char* argv[])
{
my_class mc=5;
mc.display();
// 5
return 0;
}
10
class my_class1
{
int a;
public:
void compare(my_class1&);
friend void compare(my_class1&, my_class2&);
my_class1(int A) : a(A) {}
virtual ~my_class1();
};
void my_class1::compare(my_class1 &mc1)
{ if (this != &mc1)
if(a==mc1.a) cout << "equal\n";
else cout << " not equal\n";
else cout << "the same class\n";
}
int main(int argc, char* argv[])
{
my_class1 mc1=10, mc1n=20; // mc1n=10 - equal
mc1.compare(mc1);
// the same class
mc1.compare(mc1n);
// not equal
return 0;
}
Download

ppt1 - Universidade de Aveiro › SWEET