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.