Caso deseje simular uma interface, crie uma classe apenas com os nomes dos métodos, colocando eles virtuais, sem colocar as implementações deles, apenas atribuindo com 0 (classe abstrata):
class Estoque {
public:
virtual void defineLado(int l) = 0;
virtual double volume() = 0;
virtual ~Estoque() = 0; // Toda classe abstrata deve ter um método destrutor virtual puro, mesmo que os outros métodos não os sejam
};
Estoque::~Estoque() { // Implementação Vazia, sempre necessária, no caso.
}
Um método virtual atribuído a 0 é como se ele fosse abstrato, ou seja, não terá implementação na interface, isso é uma função virtual pura, e toda classe que herdará, deverá implementar, sobreescrevendo ele.
Uma classe pode ter vários métodos implementados e ter uma ou mais funções virtuais puras. O método destrutor (sempre obrigatório ser um método virtual puro em classes abstratas) deverá ter uma implementação vazia, os outros métodos virtuais puros não tem implementação nenhuma.
PS: Uma classe com métodos abstratos não pode ser instanciada, mesmo se tiver apenas um método virtual puro. Na verdade, para criar uma classe abstrata, basta declarar um ou mais métodos virtual puros (atribuídos com 0). Quando a classe tem exclusivamente métodos virtuais puros, age de forma igual à uma interface (que existe em linguagens POO como Java, C# e PHP, mas não em C++).
Para criar uma classe com todos os métodos prontos como abstrata (para ser usada em heranças), basta criar o método destrutor dela como virtual puro (virtual ~NomeDaClasse() = 0
e uma implementação do mesmo (mesmo sem código algum entre as chaves) na mesma classe (nesse código acima seria o recomendado, pra encapsular também a classe), e as classes que a herdarem, não precisam de nenhuma alteração. Caso esse método virtual não possua implementação, aí sim as classes filhas dessa terão que implementar.
E na classe que a implementará, coloque como se fosse uma herança, e o sobreponha os métodos com override
:
include "Estoque.h"
class Caixa : public Estoque {
public:
void defineLado(int l) override;
double volume() override;
const double getLado();
void setLado(double lado);
private:
double lado;
};
void Caixa::defineLado(int l) {
this->lado = l;
}
double Caixa::volume() {
return lado * lado * lado;
}
const double Caixa::getLado() {
return lado;
}
void Caixa::setLado(double lado) {
this->lado = lado;
}
E a implementação no main:
Estoque *cx = new Caixa();
cx->defineLado(5);
cout << cx->volume() << endl;
delete cx;
E no método main podemos verificar se a classe implementa a classe abstrata usando um if e else:
Estoque *cx = new Caixa();
if(typeid(cx) == typeid(Estoque*)) { // inclua typeinfo
cx->defineLado(5);
cout << cx->volume() << endl;
}
else {
cerr << "Classe Estoque Não Implementada!" << endl;
}
delete cx;
Usando uma classe base no "tipo" do objeto, e ele sendo implementado com uma classe herdeira, permite maior polimorfismo, pois qualquer classe que herde o "tipo" da classe anterior, pode ser implementada.
Alternativamente, podemos fazer assim, de uma forma mais "gambiarrenta":
// Funções estruturadas:
void defLado(Estoque &caix, int l) {
caix.defineLado(l);
}
double verVol(Estoque &caix) {
return caix.volume();
}
int main() {
Caixa cx;
defLado(cx, 5);
cout << verVol(cx) << endl;
return 0;
}
Podemos criar também "tipos genéricos" em C++, que nos permite configurar os tipos, iguais aos usados num contâiner.
Primeiro, crie, num arquivo de classe, uma classe dessa forma:
template<class E> class Generico {
public:
const E getEntidade();
void setEntidade(E entidade);
private:
E entidade;
};
template<class E> const E Generico<E>::getEntidade() {
return this->entidade;
}
template<class E> void Generico<E>::setEntidade(E entidade) {
this->entidade = entidade;
}
PS: No caso de templates, coloque as implementações no arquivo h, no entanto, o arquivo cpp deverá estar presente, mesmo sem nada a não ser o include pro arquivo h.
Como visto acima, a letra E será substituída pelo tipo que passaremos ali.
E no programa principal, faça isso:
int main() {
Generico<string> texto;
texto.setEntidade("Exemplo de String!"); // Utilizamos ponto pelo objeto não ser criado com new.
cout << texto.getEntidade() << endl; // Idem
Generico<int> numero;
numero.setEntidade(50);
cout << numero.getEntidade() << endl;
return 0;
}
No entanto, o ideal é usarmos objetos criados com new, dessa forma:
int main() {
Generico<string> *texto = new Generico<string>(); // Não esqueça do ponteiro
texto->setEntidade("Exemplo de String!"); // Utilizamos seta pelo objeto ser criado com new.
cout << texto->getEntidade() << endl; // Idem
Generico<int> *numero = new Generico<int>();
numero->setEntidade(50);
cout << numero->getEntidade() << endl;
delete texto;
delete numero;
return 0;
}
Caso use mais de um tipo genérico, coloque dentro da mesma tag, separados por vírgulas.
PS: Caso use mais de um tipo genérico, coloque dentro da mesma tag, separados por vírgulas. Em heranças e implementações, caso a classe a ser herdada ou implementada tenha tipos genéricos, a classe herdeira ou implementadora deverá ter o mesmo tipo (por exemplo, se a classe abstrata for template <class E> class ClasseAbstrata
, a classe que a implementa deverá ter o tipo a ser recebido indicado, como por exemplo class ClasseImplementadora : public ClasseAbstrata<int>
) e deverá estar em arquivos separados (h e cpp) e os métodos declarados como classe comum, e nesse caso o objeto criado será ClasseAbstrata<int> *objeto = new ClasseImplementadora()
. Caso não declare o tipo genérico, coloque template <class E> class ClasseImplementadora : public ClasseAbstrata<E>
e as implementações no mesmo arquivo h (mas crie um cpp vazio de mesmo nome pra evitar erros de compilação), e nesse caso o objeto criado será ClasseAbstrata<TipoOuClasse> *objeto = new ClasseImplementadora<TipoOuClasse>()
. Esse é o conceito de polimorfismo paramétrico.