Le differenze tra funzioni virtuali e pure virtuali in C++
Questo articolo descriverà le differenze tra funzioni virtuali e pure virtuali in C++.
Funzioni virtuali e loro caratteristiche in C++
Le funzioni virtuali sono strettamente associate al concetto di polimorfismo. In C++, possiamo organizzare diverse classi in una gerarchia collegata in cui potrebbero condividere alcuni membri di dati e avere le stesse funzioni membro esposte come interfaccia.
Generalmente, le classi che ereditano parte del codice da altre classi sono chiamate classi derivate, mentre quelle ereditate da sono le classi base. Nota che a volte questi termini possono essere usati in modo intercambiabile con nomi genitore-figlio o superclasse-sottoclasse. Le funzioni che possono essere sovrascritte nelle classi derivate sono chiamate funzioni virtuali e sono dichiarate con la parola chiave virtual
. Le funzioni virtuali hanno lo stesso nome all’interno della data gerarchia di classi e ogni classe derivata può implementare la propria definizione della funzione.
Se la funzione non viene sovrascritta, l’oggetto classe derivata richiama la funzione definita nella classe base. Il seguente programma di esempio mostra l’utilizzo di base delle funzioni virtuali definendo la funzione print
in entrambe le classi Engineer
e Employee
. Quindi possiamo implementare una funzione arbitraria Func
che accetta il riferimento a Employee
e chiama la funzione print
all’interno del corpo.
Ora, l’implementazione Func
può cambiare più volte, ma chiamerà sempre la funzione print
corrispondente in base all’oggetto che viene passato come parametro. Possiamo aggiungere più classi derivate nella gerarchia delle classi Employee
. Ciascuno di essi può/non può implementare la funzione print
, ma la funzione Func
accetterà le loro istanze e invocherà la funzione virtuale corretta.
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
class Employee {
public:
Employee(string fn, string ln)
: first_name(std::move(fn)), last_name(std::move(ln)) {}
virtual void print() {
cout << "name: " << first_name << "\n"
<< "last name: " << last_name << "\n";
};
protected:
string first_name, last_name;
};
class Engineer : public Employee {
public:
Engineer(string fn, string ln, string sp)
: Employee(std::move(fn), std::move(ln)), specialization(std::move(sp)) {}
void print() override {
Employee::print();
cout << "specialization: " << specialization << "\n";
}
private:
string specialization;
};
void Func(Employee &em) { em.print(); }
int main() {
Employee em1("Jim", "Jiao");
Engineer eng1("Jin", "Baker", "Aerospace Engineering");
Func(em1);
cout << "\n";
Func(eng1);
return EXIT_SUCCESS;
}
Produzione:
name : Jim last name : Jiao
name : Jin last name : Baker specialization
: Aerospace Engineering
Funzioni virtuali pure e tipi astratti in C++
Abbiamo invece il concetto di funzioni virtuali pure, che sono dichiarate simili alle normali funzioni virtuali e includono la notazione = 0;
alla fine della dichiarazione. Queste funzioni essenzialmente non hanno una definizione nella classe base, dove vengono dichiarate per prime. Pertanto, saranno molto probabilmente definiti nelle classi derivate.
Le classi che contengono funzioni virtuali pure sono chiamate classi astratte e sono solitamente utilizzate per specificare l’interfaccia per le classi derivate. Nota che le classi astratte non possono essere istanziate direttamente.
Il prossimo frammento di codice implementa le classi Triangle
e Rectangle
con la classe base astratta Shape
. In questo caso, abbiamo definito la funzione virtuale pura printArea
in entrambe le classi derivate. A volte, una classe derivata potrebbe non definire una funzione virtuale pura ereditata, rendendola un’altra classe astratta nella gerarchia di classi data. Una classe derivata può ereditare più funzioni virtuali pure. Se non ne definisce nemmeno uno, alla classe viene applicata la classificazione astratta.
#include <iostream>
#include <string>
#include <vector>
using std::cout;
using std::endl;
using std::string;
using std::vector;
class Shape {
public:
virtual void printArea() = 0;
};
class Triangle : public Shape {
public:
Triangle(double b, double h) : base(b), height(h) {}
void printArea() override { cout << (base * height) / 2.0; }
private:
double base;
double height;
};
class Rectangle : public Shape {
public:
Rectangle(double i1, double i2) : edges({i1, i2}) {}
void printArea() override { cout << edges[0] * edges[1]; }
private:
vector<double> edges;
};
int main() {
Triangle t1(3, 5);
t1.printArea();
cout << "\n";
Rectangle r1(3, 5);
r1.printArea();
cout << "\n";
return EXIT_SUCCESS;
}
Produzione:
7.5 15
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn Facebook