O Problema
- Voltando ao exemplo do semáforo:
class Semaforo {
public:
void setEstado(int est);
int getEstado() const;
void mudaEstado();
private:
int estado; // estado atual
};
Por que precisamos do método setEstado ?
Resposta e nova pergunta: qual é o estado inicial do atributo estado ?
Resposta: indefinido
Atributos Indefinidos
- Porém, é possível garantir um estado inicial consistente se utilizarmos
o método setEstado:
int main() {
Semaforo sem;
int est;
string estados[] = { "verde", "amarelo", "vermelho" };
cout << "Digite o estado inicial (0-verde, 1-amarelo, 2-vermelho): ";
cin >> est;
sem.setEstado(est);
...
}
Então, qual é o problema ?
Simples: temos que nos lembrar de fazer isso!
O papel do Construtor
- Construtor é um método especial, responsável pela inicialização de um objeto.
- É caracterizado pela ausência de tipo de retorno, e cujo nome é o mesmo da classe:
class Semaforo {
public:
Semaforo(void);
void setEstado(int est);
int getEstado() const;
void mudaEstado();
private:
int estado; // estado atual
};
Semaforo::Semaforo(void) {
setEstado(0);
}
...
Construtor default
- Agora, ao declararmos um objeto Semaforo no programa principal,
podemos automaticamente inicializá-lo com estado = 0:
int main() {
Semaforo sem;
int est;
string estados[] = { "verde", "amarelo", "vermelho" };
// Neste ponto, "sem" já está inicializado com 0 (verde)
...
}
Esse tipo de construtor é denominado de default, pois não tem parâmetros e é sempre
chamado quando um objeto é instanciado.
Se não for declarado pelo usuário, é criado automaticamente por C++.
Construtor com Parâmetros
- Também é possível declararmos um construtor com parâmetros:
class Semaforo {
public:
Semaforo(void);
Semaforo(int est);
void setEstado(int est);
int getEstado() const;
void mudaEstado();
private:
int estado; // estado atual
};
Semaforo::Semaforo(int est) {
setEstado(est);
}
...
Construtor com Parâmetros
- Agora é possível inicializar um objeto Semaforo com um estado definido:
int main() {
int est;
string estados[] = { "verde", "amarelo", "vermelho" };
cout << "Digite o estado inicial (0-verde, 1-amarelo, 2-vermelho): ";
cin >> est;
Semaforo sem(est); // equivalente a sem.setEstado(est)
...
}
Se houver algum construtor com parâmetros, torna-se obrigatória a definição do construtor default!
Construtor com Parâmetros Default
- Alternativa ao uso de dois construtores: cria-se um único construtor, com um parâmetro definido como opcional (default):
class Semaforo {
public:
Semaforo(int est = 0);
void setEstado(int est);
int getEstado() const;
void mudaEstado();
private:
int estado; // estado atual
};
Semaforo::Semaforo(int est) {
setEstado(est);
}
Construtor com Parâmetros Default (2)
- O construtor pode ser chamado de duas formas:
int main() {
...
Semaforo sem; // equivalente a sem.setEstado(0)
// ou
Semaforo sem(est); // equivalente a sem.setEstado(est)
...
}
Exercícios
- Altere a classe Lampada para incluir um ou mais construtores adequados. Verifique que o código agora é mais consistente, e podemos inclusive criar um vetor de objetos sem nos preocuparmos com atributos não inicializados.
- Altere a classe Fracao para incluir um ou mais construtores apropriados. Imagine os valores que o numerador e denominador de uma fração deveriam ter, caso não sejam informados. Experimente depois fazer operações com frações inicializadas explicitamente e outras não inicializadas: verifique se os resultados fazem sentido.
- Crie uma classe Ponto para representar um ponto no plano (x,y). Inclua métodos para setar e obter as coordenadas do ponto, construtores necessários e um método para calcular a distância entre este ponto e outro ponto informado como parâmetro (dica: você precisa aplicar o Teorema de Pitágoras).