Boas. Os construtores são funções. Podes ter vários definidos para o mesmo tipo, desde que difiram no numero e tipo de parametros como é o caso desses dois. Distinguem-se pois um não recebe parametros e o outro recebe uma string. Para que é que serve?
No caso de quereres iniciar uma instância de pessoa com um nome, podes passar como parametro ao construtor.
main:
Código:
std::string nome;
std::cout << "insira o nome:\n";
std::cin >> nome;
pessoa *p1 = new Pessoa(nome); //neste caso o construtor a ser evocado é o construtor que recebe uma string
pessoa *p2 = new Pessoa(); //é evocado o construtor sem parametros
Na 2a instância, o atributo nome de p1 ficará com "", pois segundo a implementação do construtor sem parametros que fizeste, caso nao se passem parametros, o nome corresponderá a uma string vazia. Não é que um construtor com parametros seja indispensável... podes sempre afectar os campos mais tarde, mas isto, obrigaria a que todos os campos fossem de acesso público ou houvessem funções membro para afectar todos os campos, desprezando o conceito de encapsulamento, levando a graves falhas de segurança.
Uma classe abstracta não pode ser instânciada. Pode-se dizer que é uma classe que existe apenas para servir métodos e campos ás suas derivadas. Que nunca necessirarás de instanciar. Utilizando o teu exercicio, supoe que não estavas a utilizar uma classe pessoa e cada uma das classes que estas a utilizar definiam os seus proprios campos:
Visitante:
-nome
-morada
-bi
Funcionario:
-nome
-morada
-numfuncionario
Director:
-nome
-morada
-numFuncionario
-departamento
Assim terias um programa muito mais complexo e de dificil actualização. Se pretendesses adicionar uma classe supervisor, lá terias de estar a criar os mesmo campos, havendo muita repetição de código.
Para resolver este problema, tens uma classe pessoa que contem todos os campos comuns a funcionario e director. Mas director difere de funcionario no campo departamento. Então Director define esse campo e deriva de funcionário, pois director é um funcionario da empresa e que por sua vez é uma pessoa. O visitante tambem é uma pessoa e só acrescenta o bi, deriva directamente de Pessoa.
Quanto ao polimorfismo, consiste numa classe derivada modificar o comportamento da base.
No teu exercicio, imaginando que pretendes armazenar os funcionarios, directores e visitantes em contentores.
Terias de ter um vector ou mapa de objectos para tipo funcionario, outro de directores e outro de visitantes. Teriam-se 3 vectores ou maps. :s Era uma chatice para manipular tudo isto. Criar um novo objecto implicaria verificar se era funcionario, visitante ou director, aceder ao respectivo contentor e posteriormente pesquisar, pois ja poderia existir e so depois adicionar.
Para resolver isto pensamos da seguinte forma:
Funcionarios, directores e visitantes são pessoas, não é ? Então criamos um contentor de apontadores para pessoas, ou seja, para a classe mais genérica e colocamos tudo num só. Utilizando o vector como exemplo:
Código:
std::vector<Pessoa*> pessoas;
Pessoa p1 = new Funcionario("Jose");
Pessoa p2 = new Director("Marta");
Pessoa p3 = new Visitante("Joana")
pessoas.push_back(p1);
pessoas.push_back(p2);
pessoas.push_back(p3);
// ou podes instanciar e inserir directamente no vector:
// pessoas.push_back(new Funcionario("Jose"));
Até aqui tudo bem, mas agora supoe que queres um metodo getId() que para funcionarios e directores retorne o numFuncionario e para visitantes retorne o numero do bi. Visto que este método será comum a todas as classes, o melhor é declarar na classe base. Mas isto trás outro problema. Pessoa não tem um campo bi, nem um numFuncionario. É aqui que entra o polimorfismo e os métodos virtuais puros. Declarando o método getId() como virtual puro na classe base, torna a classe abstracta (não pode ser instânciada) e permite realizares chamadas polimorficas sobre apontadores para essa classe.
Pessoa.h
Declarando como virtual puro, obriga a que redifinas nas classes derivadas, ou seja, só podes instanciar um visitante se este tiver definido um metodo getId(). O mesmo se passa com o funcionario/director.
visitante.cpp
Código:
int visitante::getId()
{
return bi;
}
funcionario.cpp
Código:
int funcionario::getId()
{
return numFuncionario;
}
director.cpp
Não precisa pois é comum a funcionário.
Assim já poderias evocar o método getId() sobre elementos do vector, que seriam chamados os métodos do tipo do objecto apontado. Continuando o exemplo, pontanto, o 1º elemento do vector é um funcionario, o 2º um director e o 3º é um visitante.
Então:
Código:
pessoas[0]->getId(); //evoca o getId() de funcionario
pessoas[1]->getId(); //evoca o getId() de funcionario
pessoas[2]->getId(); //evoca o getId() de visitante
Isto é, evoca o metodo não segundo o tipo do apontador (que é pessoa) mas segundo o tipo do objecto apontado.
Cumps