Domanda

Ho visto un libro su C ++ ricordare che la navigazione gerarchie di ereditarietà usando getto statico è più efficiente rispetto all'utilizzo di fusione dinamica.

Esempio:

#include <iostream>
#include <typeinfo>

using namespace std;

class Shape { public: virtual ~Shape() {}; };
class Circle : public Shape {};
class Square : public Shape {};
class Other {};

int main() {
    Circle c;

    Shape* s = &c; // Upcast: normal and OK

    // More explicit but unnecessary:
    s = static_cast<Shape*>(&c);
    // (Since upcasting is such a safe and common
    // operation, the cast becomes cluttering)

    Circle* cp = 0;
    Square* sp = 0;

    // Static Navigation of class hierarchies
    // requires extra type information:
    if(typeid(s) == typeid(cp)) // C++ RTTI
        cp = static_cast<Circle*>(s);
    if(typeid(s) == typeid(sp))
        sp = static_cast<Square*>(s);
    if(cp != 0)
        cout << "It's a circle!" << endl;
    if(sp != 0)
        cout << "It's a square!" << endl;

    // Static navigation is ONLY an efficiency hack;
    // dynamic_cast is always safer. However:
    // Other* op = static_cast<Other*>(s);
    // Conveniently gives an error message, while
    Other* op2 = (Other*)s;
    // does not
} ///:~

Tuttavia, sia dinamico cast e il cast statico (come attuato sopra) devono RTTI abilitati per tale la navigazione al lavoro. E 'solo che dinamico cast richiede la gerarchia di classe di essere polimorfico (cioè classe di base con almeno una funzione virtuale).
Da dove viene questo guadagno di efficienza per il getto statico viene? Il libro fa menzione che dinamico cast è il modo migliore per fare type-safe downcasting.

È stato utile?

Soluzione

static_cast per se non ha bisogno RTTI - typeid non (come fa dynamic_cast), ma questa è una questione completamente diversa. La maggior parte dei calchi sono solo dicendo che il compilatore "fidati di me, io so quello che sto facendo" - dynamic_cast è l'eccezione, si chiede al compilatore di verificare in fase di esecuzione ed eventualmente fallire. Questa è la grande differenza di prestazioni proprio lì!

Altri suggerimenti

E ' molto meglio evitare di accendere i tipi a tutti, se possibile. Questo è solitamente fatto spostando il codice corrispondente a un metodo virtuale che è implementata in modo diverso per i diversi sottotipi:

class Shape {
public:
    virtual ~Shape() {};
    virtual void announce() = 0;  // And likewise redeclare in Circle and Square.
};

void Circle::announce() {
    cout << "It's a circle!" << endl;
}

void Square::announce() {
    cout << "It's a square!" << endl;
}

// Later...
s->announce();

Se si sta lavorando con un pre-esistente gerarchia di ereditarietà che non si può cambiare, indagare il Visitor Reticolo per un più estensibile alternativa-tipo switching.

Più informazioni: static_cast non richiede RTTI, ma un abbattuta utilizzo può essere pericoloso, che porta a un comportamento indefinito (ad esempio crash). dynamic_cast è sicuro ma lento, perché controlla (e quindi richiede) Info RTTI. Il vecchio cast di tipo C è ancora più pericoloso di quanto static_cast perché sarà tranquillamente gettato tra i tipi completamente indipendenti, quando static_cast sarebbe oggetto con un errore di compilazione.

Con il cast statico (e controllo typeid) non si può abbattuto per un tipo intermedio (bambino deriva dal padre deriva dal nonno, non è possibile bassi dal nonno al padre) l'utilizzo è un po 'più limitata . static_cast senza il controllo typeid sta sacrificando la correttezza per la perfomance, e poi si sa come si dice:

Chi sacrifica correttezza per le prestazioni merita né

Poi, naturalmente, ci sono situazioni in cui si è in disperato bisogno di un paio di istruzioni della CPU e non c'è nessun altro posto dove cercare i miglioramenti e si sono in realtà sicuro su cosa si sta facendo e si sono meassured (giusto?) Che la unico posto per ottenere prestazioni sta usando static_cast invece di dynamic_cast ... poi si sa è necessario rielaborare il vostro disegno, o gli algoritmi o ottenere una migliore hardware.

Le restrizioni si impongono utilizzando RTTI + static_cast è che non sarà in grado di estendere il codice con le nuove classi derivate in un secondo momento, senza rielaborare tutti i luoghi in cui si è utilizzato questo trucco per guadagnare pochi istruzioni della CPU. Che rielaborazione stesso vorrà probabilmente più tempo (tempo di ingegneria che è più costoso) rispetto al tempo di CPU che avete ottenuto. Se, in ogni caso, il tempo dedicato al downcasts è evidente, quindi rielaborare il vostro disegno come j_random_hacker suggerisce, che migliorerà sia nel design e prestazioni.

dynamic_cast sarebbe tornato NULL se non avesse fatto l'assegno typeid e il cast non poteva avere successo. static_cast avrebbe avuto successo (e portare a un comportamento indefinito, come ad esempio un incidente eventuale). Questa è probabilmente la differenza di velocità.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top