Seguindo o livro Treinamento em Linguagem C++, nossa turma do curso de Ciência da Computação deparou-se com uma característica em C++ que não funcionava - o uso de membros estáticos. Inicialmente resolvemos acreditar que o nosso compilador Borland Turbo C++ (MS-DOS) possuía algum defeito de fábrica, e isto explica o título deste documento. O nosso curso seguiu em frente deixando esta importante funcionalidade da linguagem para trás até alguém descobrir uma solução. E após muitas frustrantes semanas visitando sites na Internet e lendo alguns outros bons livros para complementar nossos estudos, a solução surge do acaso. Nosso problema não era com a Borland, e sim com a especificação ANSI dos puristas da programação orientada ao objeto.
De fato, existe uma forma correta de utilizar membros estáticos que não é abordada em muitos livros. Esta mesma abordagem, conforme meus testes, é necessária também nos compiladores Borland C++ Builder (Windows) e EGCS-C++ (que adiciona suporte C++ ao compilador C da GNU para Unix).
Inicialmente veremos a utilidade dos membros estáticos e logo em seguida sua implementação, incluindo o nosso código defeituoso que nos fez acreditar que poderia existir um bug em nosso compiladores. Aproveitarei também para ministrar outros conceitos desta característica da linguagem, pois agora nada nos impedirá de terminar a lição. Bom, ao menos desta vez os exemplos irão compilar sem mensagens de erro...
Variáveis Estáticas Em Uma Classe
Vamos supor que eu tenho uma classe de corrida de carros em um programa escrito em C++. A posição do carro e sua velocidade são dados instanciáveis pelo fato de estes se aplicarem para cada carro individualmente. Entretanto, informações como o tempo total decorrido desde o início da corrida, o tempo total máximo de corrida e a distância total da pista não são específicos para cada carro, mas são relacionados para a classe inteira. Todos os carros da classe devem acessar a mesma informação para determinar se já chegaram ou não ao final da corrida; estas informações não são duplicadas para cada carro.
Eu poderia ter usado variáveis externas para estas informações, mas estas não estaríam associadas corretamente com a classe e poderiam gerar alguns problemas futuramente com o acréscimo de código ao programa. Eu quero usar variáveis da classe, mas não quero que elas sejam duplicadas para cada objeto.
Variáveis-membro estáticas solucionam este problema.
Criando Variáveis Estáticas Em Uma Classe (Modo Incorreto)
No livro Treinamento em Linguagem C++, foi apresentado o seguinte programa exemplo que não funcionou em nossos compiladores Borland Turbo C++ 3.0.
//STATDATA.CPP
//Mostra variáveis static em classes
//Exemplo do livro Treinamento em Linguagem C++ - Capítulo 8 - Página 26
#include <iostream.h>
class REC
{
private:
static int n; //dado STATIC
public:
REC() {n++;}
int getrec() const {return n;}
};
void main()
{
REC r1, r2, r3;
cout << "\nNumero de objetos:" << r1.getrec();
cout << "\nNumero de objetos:" << r2.getrec();
cout << "\nNumero de objetos:" << r3.getrec();
}
Este programa exemplo não funciona! O linkador gera o seguinte erro:
Linking STATDATA.EXE: Linker Error: Undefined symbol REC::n in module STATDATA.CPP
Criando Variáveis Estáticas em Uma Classe (Modo Correto)
A sintaxe correta para criar variáveis estáticas parece inicialmente estranha. Você deverá ter declarações separadas - uma para declarar uma variável dentro da classe e outra para definir ela fora da classe. Veja o exemplo abaixo:
class umaclasse
{
private:
static int statusvar; // declaração
...
};
int umaclasse::statusvar = 77; // definição
Ou seja, o programa anterior não funcionava pelo simples fato de o linkador requisitar uma definição após a declaração da variável static. A declaração usa a palavra-chave static. A definição usa o nome da classe conectada com o nome da variável usando um operador de resolução de escopo (::). Este procedimento para criar uma variável estática justifica-se assumindo que as variáveis definidas dentro da especificação da classe serão sempre valores instanciáveis, isto é, duplicados para cada objeto. Para definir um valor único que existe para a classe inteira, você define ela fora da classe, embora você a declare dentro da classe para ela ficar visível para os membros desta classe.
A variável-membro estática pode ser inicializada no momento que ela é definida, como foi mostrado acima. Se esta não for explicitamente inicializada, ela será automaticamente iniciada com o valor 0. Uma vez definida, as variáveis estáticas da classe podem ser acessadas somente por funções-membro da classe.
Para colocar em funcionamento o programa anterior, basta incluir apenas uma linha a mais: a definição da variável. Veja abaixo o nosso código problemático corrigido:
//STATDAT2.CPP
//Mostra variáveis static em classes
//Exemplo CORRIGIDO do livro Treinamento em Linguagem C++ - Capítulo 8 - Página 26
#include <iostream.h>
class REC
{
private:
static int n; //DECLARAÇÃO do dado STATIC
public:
REC() {n++;}
int getrec() const {return n;}
};
int REC::n = 0; //DEFINIÇÃO do dado STATIC
void main()
{
REC r1, r2, r3;
cout << "\nNumero de objetos:" << r1.getrec();
cout << "\nNumero de objetos:" << r2.getrec();
cout << "\nNumero de objetos:" << r3.getrec();
}
Acessando Variáveis Estáticas
Você pode acessar membros estáticos com o uso de uma função da classe. Entretanto, faz mais sentido usar uma forma especial de função que, eqüivalentes aos dados estáticos, aplica-se para a classe inteira ao invés de um objeto em particular. Isto é chamado de função estática .
Uma função-membro estática é definida usando a palavra-chave static e se parece muito com as outras funções-membro. Entretanto, uma chamada a uma função-membro estáticas é feita sem uma referência com um objeto em particular. Ao invés disso, a função é chamada conectando ela e o nome da classe com o operador de resolução de escopo. Veja abaixo:
class umaclasse
{
private:
...
public:
static int funcao_estatica() // definição da função estática
{
// esta função poderá acessar somente variáveis estáticas
}
};
void main()
{
...
umaclasse::funcao_estatica(); // chamada à função
...
}
Uma função-membro estática não pode referenciar nenhuma variável da classe que não seja estática. Por quê não? Porque funções estáticas não conhecem nada sobre os objetos de uma classe. Elas podem acessar somente variáveis estáticas, ou seja, dados específicos da classe. Você pode chamar uma função estática mesmo antes de você criar objetos desta classe.
Algumas vezes os objetos de uma classe, ou até as funções que não pertencem a esta classe como a função main() , precisam saber quantos objetos de uma certa classe existem em um certo momento. Em uma corrida de carros, por exemplo, poderíamos ter a necessidade de saber quantos carros ainda estão competindo na corrida após várias colisões e defeitos em motores.
O exemplo abaixo cria uma classe de abelhas, onde cada abelha pode possuir um nome. A classe usa uma variável estática para contar o número de abelhas criadas durante a execução do programa. Uma função estática é usada na função main() para acessar este número total de abelhas existentes. Isto nos permite saber o total de abelhas antes de criar a primeira e após ter criado algumas delas.
// static.cpp
// demonstra dados e funcoes estaticas
#include <iostream.h>
class abelha
{
private:
char* abelha_nome; //nome da abelha
static int total_abelhas; // total de abelhas criadas
// NOTA: 'declaracao' da variavel static
public:
abelha() // Constructor
{
abelha_nome = "[nome desconhecido]";
total_abelhas++;
}
abelha(char * nome); //Construtor com 1 argumento
char * nome()
// retorna o nome da abelha corrente
{
return abelha_nome;
}
static int total_de_abelhas()
// retorna o numero total de abelhas existentes
{
return total_abelhas;
}
};
abelha::abelha(char * nome)
{
abelha_nome = nome;
total_abelhas++;
}
int abelha::total_abelhas = 0; // NOTA: 'definicao' da variavel static
void main()
{
cout << "Total de abelhas = " << abelha::total_de_abelhas() << endl;
abelha ab1("Mara"), ab2, ab3("Vilma"); // cria abelhas
cout << "Abelha ab1= " << ab1.nome() << endl;
cout << "Abelha ab2= " << ab2.nome() << endl;
cout << "Abelha ab3= " << ab3.nome() << endl;
cout << "Total de abelhas = " << abelha::total_de_abelhas() << endl;
}
Note que a função construtora consegue acessar a variável total_abelhas (ela a incrementa), mas a função-membro estática total_de_abelhas() não consegue , pois funções estáticas não podem acessar variáveis não-estáticas (instanciáveis).
Aqui está a saída do programa:
Total de abelhas = 0 Abelha ab1= Mara Abelha ab2= [nome desconhecido] Abelha ab3= Vilma Total de abelhas = 3
Referências Bibliográficas
André Sandri
andresandri@hotmail.com
http://www.sandri.cjb.net/
Curso de Programação III - Linguagem C++
Professor Werner Haetinger
UNISC - Universidade de Santa Cruz do
Sul - RS
Santa Cruz do Sul, 28 de outubro de 1999.