0Pricing
C++ Academy · Lezione

Polimorfismo statico

Evitare l'overhead delle chiamate virtuali

Polimorfismo statico è una lezione C++ Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C++ Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C++ Academy include 4 lezioni in totale.

Due tipi di polimorfismo

C++ supporta due forme di polimorfismo.

  • Dinamico: funzioni virtuali risolte a runtime tramite vtable
  • Statico: template e CRTP risolti a tempo di compilazione

Il polimorfismo statico sacrifica la flessibilità in favore della velocità.

Il costo di virtual

Una chiamata virtuale richiede un salto indiretto attraverso la vtable. Di norma il compilatore non può eseguirne l'inlining, impedendo molte ottimizzazioni.

#include <iostream>

struct Shape {
    virtual double area() const = 0;
    virtual ~Shape() = default;
};

struct Square : Shape {
    double s;
    Square(double x) : s(x) {}
    double area() const override { return s * s; }
};

int main() {
    Shape* p = new Square(3);
    std::cout << p->area() << "\n";
    delete p;
    return 0;
}

Dispatch statico con CRTP

Con CRTP, la classe base chiama direttamente il metodo della classe derivata tramite una conversione a tempo di compilazione, consentendo al compilatore di eseguire l'inlining completo.

#include <iostream>

template <typename T>
struct Shape {
    double area() const { return static_cast<const T*>(this)->area(); }
};

struct Square : Shape<Square> {
    double s;
    Square(double x) : s(x) {}
    double area() const { return s * s; }
};

int main() {
    Square sq(3);
    std::cout << sq.area() << "\n";
    return 0;
}

Assegnare un nome al forwarder

Quando la classe base e quella derivata definiscono entrambe area, si assegna al forwarder della classe base un nome distinto, come compute, in modo che richiami senza ambiguità l'implementazione della classe derivata.

#include <iostream>

template <typename T>
struct Shape {
    double compute() const { return static_cast<const T*>(this)->area(); }
};

struct Circle : Shape<Circle> {
    double r;
    Circle(double x) : r(x) {}
    double area() const { return 3.14159 * r * r; }
};

int main() {
    Circle c(2);
    std::cout << c.compute() << "\n";
    return 0;
}

I template come polimorfismo statico

Anche i semplici template di funzione sono una forma di polimorfismo statico: lo stesso codice funziona per qualsiasi tipo che fornisca l'interfaccia richiesta (duck typing a tempo di compilazione).

#include <iostream>

struct Dog { void speak() const { std::cout << "Woof\n"; } };
struct Cat { void speak() const { std::cout << "Meow\n"; } };

template <typename Animal>
void make_speak(const Animal& a) { a.speak(); }

int main() {
    make_speak(Dog{});
    make_speak(Cat{});
    return 0;
}

Quando il tipo è noto

Il polimorfismo statico si applica solo quando il tipo concreto è noto in fase di compilazione. Se deve archiviare oggetti eterogenei in un unico contenitore e scegliere il comportamento in fase di esecuzione, avrà comunque bisogno delle funzioni virtuali.

Nessun contenitore eterogeneo

Non può inserire Shape<Square> e Shape<Circle> nello stesso std::vector come classe base comune, perché sono tipi non correlati. Questa è la limitazione principale del polimorfismo statico.

Vantaggio in termini di prestazioni

Eseguendo l'inline della chiamata dinamica, il compilatore può propagare le costanti ed eliminare completamente l'overhead della chiamata di funzione. Nei cicli stretti su un unico tipo noto, il polimorfismo statico può essere notevolmente più veloce.

#include <iostream>

template <typename T>
struct Op {
    int apply(int x) const { return static_cast<const T*>(this)->apply(x); }
};

struct Doubler : Op<Doubler> {
    int apply(int x) const { return x * 2; }
};

int main() {
    Doubler d;
    int sum = 0;
    for (int i = 0; i < 5; ++i) sum += d.apply(i);
    std::cout << sum << "\n";
    return 0;
}

Combinare entrambi

Può usare CRTP per il percorso più caldo e un sottile wrapper virtuale quando è necessario il type erasure. In questo modo ottiene velocità dove conta e flessibilità dove serve.

Controlli statici dell'interfaccia

Se una classe derivata dimentica di implementare il metodo richiesto, l'errore appare in fase di compilazione, quando viene istanziato il forwarder della classe base, e non in fase di esecuzione.

#include <iostream>

template <typename T>
struct Greeter {
    void greet() const { static_cast<const T*>(this)->hello(); }
};

struct English : Greeter<English> {
    void hello() const { std::cout << "Hello\n"; }
};

int main() {
    English{}.greet();
    return 0;
}

Scegliere lo strumento giusto

Usi il polimorfismo statico quando:

  • Il tipo è noto in fase di compilazione
  • Le prestazioni sono fondamentali
  • Non è necessaria una classe base comune in fase di esecuzione

Altrimenti, preferisca le funzioni virtuali per maggiore chiarezza e flessibilità.

Verifica rapida

Individui il principale compromesso del polimorfismo statico.

Riepilogo

Ha confrontato il polimorfismo statico e quello dinamico.

  • Virtual = dispatch in fase di esecuzione, vtable, contenitori eterogenei
  • CRTP/template = dispatch in fase di compilazione, possibilità di inline, maggiore velocità
  • Il polimorfismo statico richiede che il tipo sia noto in fase di compilazione
  • La scelta dipende dalle esigenze di flessibilità e prestazioni

Domande Frequenti

La lezione «Polimorfismo statico» è gratuita?

Sì — il testo completo di «Polimorfismo statico» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C++ Academy, passa a CoddyKit PRO. Il corso C++ Academy include 4 lezioni in totale.

Cosa imparerò in «Polimorfismo statico»?

Evitare l'overhead delle chiamate virtuali Eserciti C++ Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C++ Academy?

Non è richiesta alcuna esperienza precedente. C++ Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Polimorfismo statico»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C++ Academy?

Sì. Ogni lezione C++ Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. L'idioma CRTP
  2. Polimorfismo statico
  3. Mixin con CRTP
  4. Quando usare CRTP
← Torna a C++ Academy