Dessa vez, crie no projeto, um arquivo de cabeçalho (file). A classe será construída dentro dele, com o mesmo código acima, mas este terá algumas alterações.
Nele, terá o método construtor, que não tem declaração de tipo e tem o mesmo nome da classe. Esse método construirá automaticamente atributos ao objeto. Assim:
class Aviao {
public:
int velocidade;
int velMax;
std::string tipo; // Será necessário colocar std em strings, e incluir o iostream no cabeçalho.
Aviao(int tp);
};
Aviao::Aviao(int tp) {
if(tp == 1) {
this->velMax = 800;
this->tipo = "Jato";
}
else if(tp == 2) {
this->velMax = 350;
this->tipo = "Monomotor";
}
else if(tp == 3) {
this->velMax = 180;
this->tipo = "Planador";
}
this->velocidade = 0;
}
Esse código acima, sempre estará no arquivo de cabeçalho, entre o ifndef/define e o endif.
Para incluir na página principal, usamos o include entre aspas, no caso #include "Aviao.h"
. Dessa forma:
#include <iostream>
#include "Aviao.h"
using namespace std;
int main() {
Aviao *av[3];
av[0] = new Aviao(1);
av[1] = new Aviao(2);
av[2] = new Aviao(3);
delete *av;
return 0;
}
Essa é uma das vantagens da orientação a objetos, já que esse arquivo de cabeçalho pode ser utilizado para criar vários objetos, e também pode ser reaproveitado em outros programas C++.
Código completo do cabeçalho Aviao.h:
#ifndef AVIAO_H_INCLUDED
#define AVIAO_H_INCLUDED
#include <iostream> // Não esqueça do include
class Aviao {
public:
int velocidade;
int velMax;
std::string tipo;
Aviao(int tp);
void imprimir();
private:
};
Aviao::Aviao(int tp) {
if(tp == 1) {
this->velMax = 800;
this->tipo = "Jato";
}
else if(tp == 2) {
this->velMax = 350;
this->tipo = "Monomotor";
}
else if(tp == 3) {
this->velMax = 180;
this->tipo = "Planador";
}
this->velocidade = 0;
}
void Aviao::imprimir() {
std::cout << "Tipo: " << tipo << std::endl;
std::cout << "Velocidade Máxima: " << velMax << std::endl;
std::cout << "Velocidade atual: " << velocidade << std::endl;
std::cout << "-----------------------" << std::endl;
}
#endif // AVIAO_H_INCLUDED
PS: O std nos cout, nos endl e nas strings é porque não estamos informando o namespace no Aviao.h.
Código na função principal:
#include <iostream>
#include "Aviao.h"
using namespace std;
int main() {
Aviao *av[3];
av[0] = new Aviao(1);
av[1] = new Aviao(2);
av[2] = new Aviao(3);
av[0]->imprimir();
av[1]->imprimir();
av[2]->imprimir();
delete *av;
return 0;
}
PS: Podemos criar também classes diretamente no arquivo classes, nesse caso seria algo do tipo:
No arquivo h, colocamos apenas a parte da assinatura (interface) da classe:
#ifndef AVIAO_H_INCLUDED
#define AVIAO_H_INCLUDED
#include <iostream> // Não podemos esquecer do iostream aqui.
// Podemos colocar o using namespace std também aqui, pra evitar o uso de std.
class Aviao {
public:
int velocidade;
int velMax;
std::string tipo;
Aviao(int tp);
void imprimir();
private:
};
#endif // AVIAO_H_INCLUDED
No arquivo cpp, colocaremos a implementação (execução) dos métodos (incluindo os getters, setters e construtores), além dos atributos estáticos:
#include "Aviao.h"
// Não é necessário colocar iostream e outras bibliotecas aqui, caso estejam em h.
Aviao::Aviao(int tp) {
if(tp == 1) {
this->velMax = 800;
this->tipo = "Jato";
}
else if(tp == 2) {
this->velMax = 350;
this->tipo = "Monomotor";
}
else if(tp == 3) {
this->velMax = 180;
this->tipo = "Planador";
}
this->velocidade = 0;
}
void Aviao::imprimir() {
std::cout << "Tipo: " << tipo << std::endl;
std::cout << "Velocidade Máxima: " << velMax << std::endl;
std::cout << "Velocidade atual: " << velocidade << std::endl;
std::cout << "-----------------------" << std::endl;
}
E no método principal, basta incluirmos apenas o arquivo de cabeçalho h normalmente.
Caso seja necessário chamar um método de uma classe pai dentro de outro método em uma classe filha, chame usando uma sintaxe tipo ClassePai::metodoDela()
.
E também podemos fazer sobrecarga de mais de um construtor, colocando na mesma classe dois ou mais construtores, todos com o nome igual da classe e sem tipo, mas com parâmetros diferentes.
PS: Caso num construtor precise inicializar um atributo como nulo, use NULL
e inclua a bibioteca <cstddef>
. Lembrando que apenas ponteiros podem usar NULL.
Além dos construtores, também temos os destrutores em C++, declarados de forma quase igual aos construtores, usando o nome da mesma classe, diferenciando por um til, declarando na classe algo tipo virtual ~NomeDaClasse()
(se a classe não for pra extensão, pode retirar o virtual), e a implementação é feita dessa forma:
Aviao::~Aviao() {
std::cout << "Objeto Destruído" << std::endl; // A mensagem é opcional
}
E para destruir o objeto especificado, fazemos algo assim no main:
delete avi;
delete *arAv; // Se for destruir array de objetos, coloque o ponteiro pra destruir o array todo
PS: Objetos de contâiners como list e vector, só podem ser destruídos separadamente. Podemos fazer um for it pra isso, por exemplo:
for(auto a: av) { // Nome do objeto no lugar do av, o a é o que será iterado.
delete a;
}
Isso é usado para destruir um objeto no meio da execução do programa, pra não sobrecarregar a memória. Diferente de outras linguagens POO surgidas posteriormente, o C++ não faz a coleta de lixo, por isso é necessário usar o delete pra destruir objetos (objetos agregados em outros objetos são destruídos automaticamente, mas o principal não), na verdade, no C++ a POO é mais primitiva, por ter sido uma das primeiras linguagens a usar esse paradigma. Sempre mantenha o hábito de usar o delete em objetos em C++ por esse motivo.
Lembrando que, caso tenha agregação de objetos, é recomendado usar o delete this->nomeDoObjeto
desses objetos dentro do destrutor da classe, dessa forma:
Aeroporto::~Aeroporto() {
delete this->avi;
}
PS: Lembrando que o delete só pode ser usado em objetos criados com ponteiros, inicializados com new, por não haver coleta de lixo desse tipo de objeto. Objetos criados sem essa atribuição tem a coleta de lixo feita automaticamente, por isso não se usa delete.
Após o uso do delete no objeto, podemos atribuir ele novamente com outras características, por exemplo:
Aviao *av = new Aviao(1);
av->imprimir();
delete av;
av = new Aviao(2); // Na nova atribuição é sem ponteiro mesmo.
av->imprimir();
delete av;
PS: Não é necessário criar o destrutor para usar o delete, porque ele já cria um por padrão, mas pode ser útil pra exibir mensagens, por exemplo. Os construtores também são criados automaticamente, quando não são especificados no código. Destrutores em C++ não podem ser sobrepostos.
No C++ é possível criar métodos estáticos, que é quando podemos chamar um método de uma classe sem precisar criar um objeto, sem usar o new, apenas chamando o nome da classe seguido do método, na sintaxe NomeDaClasse::nomeDoMetodo()
.
No código, apenas coloque static
antes do tipo da função que deseja ser acessada de tal forma, dentro da classe. O static pode também ser usado em atributos (sempre inicializados da classe), e no código principal chamamos diretamente junto com o nome da classe, como por exemplo NomeDaClasse::nomeDoAtributo = conteudo
. (lembrando que nesse caso, mudaremos o conteúdo do atributo da classe e todos os objetos criados por ela serão alterados, por compartilhamento, independente da alteração ser dentro ou fora da classe). Nesse caso, dentro da classe não utilizamos this, só o nome do atributo ou método sozinho.
Veja um exemplo de uso de métodos e atributos estáticos numa classe denominada Lampada:
// Isso pode ir no arquivo .h:
class Lampada {
private:
static float preco;
static bool acesa;
public:
static void custo();
static void acender();
static void apagar();
};
// Daqui pra baixo iria no arquivo .cpp:
float Lampada::preco = 9.50;
bool Lampada::acesa = false;
void Lampada::custo() {
printf("A lâmpada custa R$ %.2f.\n", preco);
// Note que não usamos this para exibir atributos estáticos.
}
void Lampada::acender() {
cout << "A lâmpada está acesa!" << endl;
acesa = true;
}
void Lampada::apagar() {
cout << "A lâmpada está apagada!" << endl;
acesa = false;
}
E no código principal:
int main() {
Lampada::custo(); // Método estático.
Lampada::acender();
Lampada::apagar();
}
PS: Não é recomendado chamar atributos e métodos estáticos através de objetos (tanto que algumas linguagens nem permitem isso), pois são atributos e métodos da classe, por isso devem ser chamados diretamente através dela.
Também podemos fazer novas atribuições em atributos estáticos no código principal, desde que sejam públicos, e toda instância feita por ele também é alterada, por exemplo:
Lampada::preco = 7.25; // Atributo estático, deixe ele público
O static, teoricamente, significa que só uma alocação de memória é criada para esse atributo ou método, não tendo duas cópias na memória em simultâneo.
Em outras palavras, o static manipula os atributos e métodos na classe toda, não apenas em uma instância, e todo objeto criado com ela também terá essa alteração. São atributos e métodos globais.
PS: Métodos estáticos só podem trabalhar outros métodos e atributos quando estes também forem estáticos, e não podem ser sobrepostos. E atributos estáticos é recomendável eles serem inicializados.