2. Membros estáticos
2.1. Dados estáticos
2.2. Funções estáticas
2.3. Objectos estáticos
Quando um dado de uma classe é declarado como static, significa
que vai existir apenas uma cópia desse dado. No caso de membros
não estáticos, a cada objecto é atribuído uma cópia desse dado.
2.1. Dados estáticos.
Ao declarar um dado de uma classe como estático, vai existir
apenas uma cópia em memória desse dado. Desde modo, todos
os objectos terão exactamente a mesma informação no que toca
a essa variável.
As questões de permissão de acesso são exactamente as mesmas
que no caso de variáveis não estáticas.
Os dados estáticos são acedidos da mesma forma que os dados
não estáticos ou usando o seu nome completo, ou seja com o
operador :: e o nome da classe, por exemplo:
obj.static_var
pobj->static_var
my_class::static_var
Em memória, existe apenas uma instância de um membro estático
de uma classe independentemente do número de objectos desta
classe.
Esta instância deve ser obrigatoriamente definida (não basta apenas
declará-la).
A definição deve ocorrer fora da classe e só pode existir uma única
definição. Esta definição normalmente faz-se no ficheiro cpp:
class my_class
{
static int s_n;
//…
};
int my_class::s_n = 10;
o tipo do objecto deve ser
repetido no ficheiro cpp!
class CStatic
{
static int s_nInstances;
static const int s_nMax;
public:
CStatic();
virtual ~CStatic();
int CStatic::s_nInstances = 0;
const int CStatic::s_nMax = 5;
const char CStatic::s_Nome [] = "CStatic";
CStatic::CStatic()
{
cout << s_Nome << ": " <<
++s_nInstances << endl;
const static char s_Nome [];
if (s_nInstances >= s_nMax)
cout << "criados " << s_nMax <<
"objectos" << endl;
};
}
int main(int argc, char* argv[])
{
cout << CStatic::s_Nome << endl;
CStatic s1, s2, s3, s4, s5, s6, s7;
return 0;
}
CStatic::~CStatic()
{
cout << "~" <<s_Nome << ": " <<
--s_nInstances << endl;
}
int main(int argc, char* argv[])
{
cout << CStatic::s_Nome << endl;
CStatic s1, s2, s3, s4, s5, s6, s7;
return 0;
}
CStatic
CStatic: 1
CStatic: 2
CStatic: 3
CStatic: 4
CStatic: 5
criados 5 objectos
CStatic: 6
CStatic: 7
~CStatic: 6
~CStatic: 5
~CStatic: 4
~CStatic: 3
~CStatic: 2
~CStatic: 1
~CStatic: 0
Funções-membro estáticas
class CStatic
{
static int s_nInstances;
static const int s_nMax;
int m_nTeste;
public:
CStatic();
virtual ~CStatic();
static int GetMax();
int GetMax2();
const static char s_Nome [];
};
int CStatic::GetMax()
{
int a = m_nTeste; // erro
GetMax2(); // erro
return s_nMax;
}
int CStatic::GetMax2()
{
m_nTeste = GetMax();
return s_nMax;
}
cout << CStatic::GetMax() << endl;
CStatic s1, s2, s3, s4, s5, s6, s7;
cout << s1.GetMax() << endl;
Uma função estática só tem acesso aos membros estáticos da classe!
Uma função estática não tem ponteiro this!
Funções não estáticas têm acesso a todos os dados estáticos.
Os membros estáticos, sejam de dados ou funções, tem
obrigatoriamente de ser definidos antes da sua primeira
utilização. A definição deve ser feita antes da função main.
class my_class
{
public:
private:
static ff() { return a; };
static f(my_class* p) { return p->b; }
my_class::my_class(int B) : b(B) {}
virtual ~my_class() {}
int b; static a;
};
int my_class::a=10;
int main(int argc, char* argv[])
{
cout << my_class::ff() << endl; // O resultado: 10
my_class M1(100);
cout << M1.f(&M1) << endl; // O resultado: 100
return 0;
}
Mesmo no caso da variável ser privada é necessário defini-la antes
de ser usada.
int main(int argc, char* argv[])
{
cout << my_class::ff() << endl;
my_class M1(100);
my_class M2(200);
cout << M2.ff() << endl;
cout << M1.ff() << endl;
cout << M1.f(&M1) << endl;
return 0;
}
// O resultado: 10
// O resultado: 10
// O resultado: 10
// O resultado: 100
2.1. Funções estáticas.
Naturalmente, as funções membro estáticas também vão ter apenas
uma cópia em memória, partilhada por todos os objectos da classe,
por isso, os membros dizem-se, por vezes, propriedades da classe.
A acessibilidade a uma função estática é igual à de uma não estática.
Acresce que também nos podemos referir a uma função estática
usando o seu nome completo:
nome_da_classe::nome_da_função_estática(...)
As funções membros estáticas e não-estáticas numa mesma classe
não podem ter o mesmo nome.
A declaração de uma função membro ordinária supõe 3 coisas:
1. A função pode aceder a partes privadas da classe.
2. A função está confinada ao alcance (scope) da classe.
3. A função deve ser invocada sobre um objecto (tem um
ponteiro this).
Se a função membro for declarada como estática então, apenas
terá as primeiras duas propriedades.
Uma função membro estática só pode aceder directamente a dados
estáticos ou a outras funções membro estáticas, porque estas não
têm o ponteiro this, fundamental para determinar a que objecto
corresponde a chamada.
As funções membro estáticas não podem ser virtuais.
As vantagens de declarar uma função como estática em lugar de a
declarar como função global, reside no seguinte:
1. Não polui o espaço dos nomes. De notar que o nome da
função estática inclui o nome da respectiva classe.
2. Torna possível controlar o seu acesso, declarando-o como
público ou como privado ou como protegido na sua classe.
class X
{public:
int main(int argc, char* argv[])
static int get_count()
{ X array[9];
{ return counter; }
cout << X::get_count() << endl;
X()
{ ++counter; }
// O resultado: 9
virtual ~X() {}
X new_array[6]; 9+6=15
private:
cout << X::get_count() << endl;
static counter;
};
// O resultado: 15
return 0;
int X::counter = 0;
}
Singleton
class CPresidente
{ static CPresidente single;
char* m_sNome;
CPresidente(char* n)
{
m_sNome = new char [strlen(n) + 1];
strcpy (m_sNome, n);
};
CPresidente (const CPresidente& );
public:
~CPresidente() { delete [] m_sNome; } ;
vai existir apenas
um objecto da classe
static CPresidente& GetInstance () { return single; }
char* GetName() { return m_sNome; }
};
CPresidente CPresidente::single ("Santos");
int main()
{
//CPresidente presidente = CPresidente::GetInstance();
cout << CPresidente::GetInstance().GetName() << endl;
...
Memória reservada dinamicamente para objectos-membros estáticos
class A
{
int m_nInt;
public:
A(int n = 4) : m_nInt(n) {};
~A() {};
};
int main(int argc, char* argv[])
{
atexit (CStatic::DeleteStatic);
return 0;
}
class CStatic
{
static A* m_aTeste;
public:
CStatic();
~CStatic();
static void DeleteStatic();
};
A* CStatic::m_aTeste = new A(44);
void CStatic::DeleteStatic()
{
delete CStatic::m_aTeste;
}
2.3. Objectos estáticos.
Além dos membros de uma classe, também é possível utilizarmos
os próprios objectos como estáticos. Se os objectos são globais, então
os construtores destes objectos são chamados pela ordem com que
aparecem as declarações dos objectos. Naturalmente os destrutores
são chamados em ordem inversa. No caso dos objectos estáticos
locais, os construtores são chamados apenas na primeira vez em
que ocorre a definição do objecto e o objecto é destruído quando o
programa termina, tal como qualquer outra variável estática.
Sumário da palavra chave static
1. Os membros de uma classe ao serem declarados como estáticos
são considerados propriedades dessa classe e são partilhados entre
todos os objectos da classe.
2. Só existe uma cópia dos membros estáticos, em memória.
3. A utilização de membros estáticos passa sempre pelos 2 passos:
declaração e definição.
4. A definição de um membro estático é única e obrigatória mesmo
para objectos privados.
5. Todos os membros estáticos (dados ou funções) têm de ser
definidos antes da função main.
6. As variáveis membros estáticas não podem ser inicializadas na
sua declaração.
7. Ao ser reservada memória para um membro de dados estático
só quando o programa terminar é que vai ser de novo libertada.
8. Os membros estáticos são reconhecidos pelo seu nome completo,
usando:
nome_da_classe::variável_estática.
9. Também se pode aceder a um objecto estático do mesmo modo
que os objectos não estáticos:
objecto_da_classe.variável_estática
ponteiro_para_ objecto->variável_estática
10. Dentro da própria classe ou em classes derivadas pode-se
simplificar o nome do membro omitindo o nome_da_classe::
11. Não é possível ter membros estáticos e não estáticos com o
mesmo nome dentro da mesma classe.
12. As funções membro estáticas não podem aceder implicitamente
a membros não estáticos, porque não têm o ponteiro this.
13. Para aceder a membros não estáticos a partir de uma função
membro estática, é preciso fazê-lo de forma explicita, obrigando
o programador a indicar, caso a caso, a que objecto se quer referir.
14. As funções membro estáticos não podem ser virtuais,
principalmente por causa de não terem o ponteiro this.
15. Os construtores de objectos estáticos globais são sempre
chamados na ordem em que aparecem as declarações desses
objectos.
16. Os destrutores de objectos estáticos globais, são chamados na
ordem inversa da anterior.
17. Os construtores dos objectos estáticos locais, são chamados
uma única vez.
18. Os construtores dos objectos estáticos locais são chamados na
primeira vez em que esses objectos são definidos.
19. Qualquer objecto estático é construído apenas uma vez, e é
sempre destruído ao terminar o programa.
Porquê utilizar membros estáticos?
As razões que nos podem levar a optar por usar membros
estáticos em classes são, entre outras:
1. Reduzir o número de variáveis globais.
2. Tornar evidente quais os objectos estáticos que pertencem,
do ponto de vista lógico, a uma determinada classe. As variáveis
globais não permitem esse discernimento por exemplo.
3. Possibilidade de controlar a acessibilidade a partes
da classe.
O que é importante:
1. Perceber o que são dados estáticos.
2. Perceber o que são funções estáticas.
3. Perceber o que são objectos estáticos.
4. Perceber qual é a diferença entre as duas seguintes notações:
“reservação de memória estática” e “com visibilidade restrita”.
5. Perceber as respectivas regras da linguagem C++.
6. Estar apto a construir programas que utilizem
dados estáticos, funções estáticas e objectos estáticos.
Download

ppt1