English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Neste artigo, você vai entender as funções virtuais e seus locais de uso. Além disso, você também aprenderá sobre funções virtuais puras e classes abstratas.
As funções virtuais são funções membros da classe base, que você deseja redefinir nas classes derivadas.
Antes de详细介绍, vamos primeiro entender por que precisamos de funções virtuais.
Vamos supor que estamos desenvolvendo um jogo (por exemplo, itens: armas).
Criamos a classe Weapon e derivamos duas classes, Bomb e Gun, que carregam as funções de armas respectivas.
#include <iostream> using namespace std; class Weapon { public: void loadFeatures() { cout << "Carregar as características da arma.\n"; } }; class Bomb : public Weapon { public: void loadFeatures() { cout << "Carregar as características da faca.\n"; } }; class Gun : public Weapon { public: void loadFeatures() { cout << "Carregar as características da arma.\n"; } }; int main() { Weapon *w = new Weapon; Bomb *b = new Bomb; Gun *g = new Gun; w->loadFeatures(); b->loadFeatures(); g->loadFeatures(); return 0; }
Resultados de Saída
Carregar características das armas. Carregar características da faca. Carregar as características da arma.
Definimos três objetos de ponteiros para as classes Weapon, Bomb e Gun, respectivamente, w, b e g. E usamos os seguintes comandos para chamar a função membro loadFeatures() de cada objeto:
w->loadFeatures(); b->loadFeatures(); g->loadFeatures();
Obra perfeita!
Mas, nosso projeto de jogo começou a crescer cada vez mais. E decidimos criar uma classe Loader separada para carregar a função de armas.
Esta classe Loader carrega outras funcionalidades das armas com base na escolha do tipo de arma.
class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } };
loadFeatures() carrega as características específicas de uma arma.
#include <iostream> using namespace std; class Weapon { public: Weapon() { cout << "Carregar características das armas.\n"; } void features() { cout << "Carregar características das armas.\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "Carregar características da faca.\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "Carregar características da arma.\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
Resultados de Saída
Carregar características das armas. Carregar características das armas. Carregar características das armas. Carregar características das armas.
A nossa implementação parece correta. No entanto, as características das armas foram carregadas4vezes. Por quê?
Inicialmente, o objeto de arma w aponta para o objeto b da classe (Bomb). E tentamos passar o ponteiro para o objeto (ponteiro da classe Loader) para a função loadFeatures() para carregar as características do objeto Bomb.
Da mesma forma, tentamos carregar as características do objeto Gun.
No entanto, a função loadFeatures() da classe Loader recebe como parâmetro um ponteiro para um objeto da classe Weapon:
void loadFeatures(Weapon *weapon)
Isso é por que as características das armas foram carregadas4vezes. Para resolver esse problema, precisamos usar a palavra-chave virtual para implementar a função virtual da classe base (classe Weapon).
class Weapon { public: virtual void features() { cout << "Carregar características das armas.\n"; } };
#include <iostream> using namespace std; class Weapon { public: virtual void features() { cout << "Carregar características das armas.\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "Carregar características da faca.\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "Carregar características da arma.\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
Resultados de Saída
Carregar características das armas. Carregar características da faca. Carregar características das armas. Carregar características da arma.
Além disso, note que l-A função >loadFeatures(w) chama diferentes funções de classes com base no objeto apontado pelo objeto l.
O uso de funções virtuais torna nosso código não apenas mais claro, mas também mais flexível.
No programa acima, "Carregar características das armas." foi impresso duas vezes. Recomendamos que você adicione outro código ao programa acima para carregar as características das armas apenas uma vez.
Se quisermos adicionar outro tipo de arma (por exemplo, arco), podemos adicionar e carregar suas características com facilidade.como adicionar?
class Bow : public Weapon { public: void features() { this-<Weapon::features(); cout >> "Carregar as características da flecha.\n"; } };
E, adicione o seguinte código na função main().
Bow b; w = &b; l->loadFeatures(w);
É importante notar que não alteramos nenhum conteúdo na classe Loader para carregar as características da lâmina.
O objetivo da programação orientada a objetos é dividir um problema complexo em várias pequenas coleções. Isso ajuda a entender e lidar eficazmente com problemas.
Às vezes, é melhor usar herança apenas quando melhor visualizar o problema.
Em C ++Nela, você pode criar uma classe abstrata que não pode ser instanciada (você não pode criar um objeto dessa classe). Mas, você pode derivar uma classe dela e instanciar um objeto da classe derivada.
As classes abstratas são classes base que não podem ser instanciadas.
As classes que contêm funções virtuais puras são chamadas de classes abstratas.
Declarar funções virtuais terminadas em =0 é chamado de função virtual pura. Por exemplo,
class Weapon { public: virtual void features() = 0; };
Aqui, a função virtual pura é
virtual void features() = 0
E, a classe Weapon é uma classe abstrata.
#include <iostream> using namespace std; // classe abstrata (classe que não pode ser instanciada) class Shape { protected: float l; public: void getData() { cin >> l; } // função virtual virtual float calculateArea() = 0; }; class Square : public Shape { public: float calculateArea() { return l*l; } }; class Circle : public Shape { public: float calculateArea() { return 3.14*l*l; } }; int main() { Square s; Circle c; cout << "Insira o comprimento para calcular a área do quadrado: "; s.getData(); cout << "Área do quadrado: " << s.calculateArea(); cout << "\nInsira o raio para calcular a área do círculo: "; c.getData(); cout << "Área do círculo: " << c.calculateArea(); return 0; }
Resultados de Saída
Insira o comprimento para calcular a área do quadrado: 4 Área do quadrado: 16 Insira o raio para calcular a área do círculo: 5 Área do círculo: 78.5
Neste programa, a função virtual pura virtual float area() = 0; é definida na classe Shape.
Uma coisa a notar é que você deve sobrescrever a função virtual pura da classe base na classe derivada. Se a sobrescrita falhar, a classe derivada também se tornará uma classe abstrata.