Domanda

Ho tre classi:

class A {};

class B : virtual public A {};
class C : virtual public A {};

class D: public B, public C {};

Il tentativo di un cast statica da A a B * * ottengo l'errore di seguito:

cannot convert from base A to derived type B via virtual base A
È stato utile?

Soluzione

Al fine di comprendere il sistema delle caste è necessario tuffo nel modello a oggetti.

La rappresentazione classica di un semplice modello di gerarchia è di contenimento:. Che se deriva da B A allora l'oggetto B dovrà infatti contenere un oggetto secondario A a fianco i propri attributi

Con questo modello, downcasting è una semplice manipolazione dei puntatori, da un offset noto al momento della compilazione che dipende dal layout di memoria B.

Questo è quello static_cast do:. Un getto statico è doppiato statico perché il calcolo di quanto necessario per il getto viene fatto al momento della compilazione, sia esso puntatori o conversioni (*)

Tuttavia, quando calci eredità virtual nelle cose tendono a diventare un po 'più difficile. Il problema principale è che con virtual eredità tutte le sottoclassi condividono una stessa istanza del sotto-oggetto. Per fare questo, B avrà un puntatore a una A, invece di un A corretta, e l'oggetto classe base A sarà istanziata fuori di B.

Pertanto, è impossibile al momento della compilazione per essere in grado di dedurre la necessaria aritmetica dei puntatori. Dipende dal tipo runtime dell'oggetto

Ogni volta che c'è un tipo di runtime dipendenza, è necessario RTTI (Runtime Informazioni Tipo), e facendo uso di RTTI per calchi è il lavoro di dynamic_cast .

In sintesi:

  • in fase di compilazione bassi: static_cast
  • run-time bassi: dynamic_cast

Gli altri due sono anche calchi in fase di compilazione, ma sono così specifici che è facile da ricordare quello che sono per ... e sono maleodoranti, quindi non meglio usare a tutti comunque.

(*) Come notato da @curiousguy nei commenti, questo vale solo per downcasting. Un static_cast permette upcasting indipendentemente eredità virtuale o semplice, anche se poi il cast è anche inutile.

Altri suggerimenti

Per quanto ne so, è necessario utilizzare dynamic_cast perché l'eredità è virtual e sei downcasting.

Non è possibile utilizzare static_cast in questa situazione perché il compilatore non conosce l'offset di B rispetto ad A in fase di compilazione. L'offset deve essere calcolato in runtime in base al tipo esatto dell'oggetto più derivata. Pertanto è necessario utilizzare dynamic_cast.

Si, è necessario utilizzare un dynamic_cast, ma dovrete fare la A polimorfica classe di base, ad esempio, con l'aggiunta di un dtor virtuale.

Secondo documenti standard,

Sezione 5.2.9 - 9 , Static Fusioni ,

  

Un rvalue di tipo “puntatore a CV1 B”, dove B è un tipo di classe, può essere convertito in un rvalue di tipo “puntatore a cv2   D “, dove D è una classe derivata (punto 10) da B, se una conversione standard valida dal‘puntatore a D’a‘puntatore B’   esiste (4.10), CV2 è la stessa cv-qualifica, o maggiore cv-qualificazione che, CV1 e B è né una classe base virtuale   D né una classe di base di una classe base virtuale di D.

Quindi, non è possibile e si dovrebbe usare dynamic_cast ...

  

$ 5.2.9 / 2 "Un'espressione e può essere   esplicitamente convertito in un tipo T usando   uno static_cast della forma   static_cast (e) se la dichiarazione   “T t (e);” è ben formata, per alcuni   inventato t temporanea variabile (8,5) ".

Nel codice che si sta tentando static_cast con 'T = B *' e 'E = A *'

Ora 'B * t (A *)' non è ben formata in C ++ (ma 'A * t (B *)' perché 'A' è una base virtuale univoco e accessibile di 'B'. Pertanto la il codice dà l'errore.

Non so se questo è "sicuro", ma.

Supponendo

B derivata da A (e A puro virtuale)

Poiché so che un puntatore a B rimane ancora un puntatore a B.

    class A
    {
            virtual void doSomething(const void* p) const =0;
    };

    class B
    {
    public:
            int value;
            virtual void doSomething(const void*p)const
            {
            const B * other = reinterpret_cast<const B*>(p);
            cout<<"hello!"<< other->value <<endl;
            }
    };

    int main()
    {
            B  foo(1),bar(2);
            A * p = &foo, q=&bar;
            p->doSomething(q);
            return 0;
    }

Questo programma esegue correttamente e restituiscono la stampa "ciao!" e il valore dell'altro oggetto (in questo caso "2").

A proposito, quello che sto facendo è altamente pericoloso (personalmente dare un ID diverso per ogni classe e affermo dopo cr fusione che la corrente ID è uguale a altro ID per essere sicuri che stiamo facendo qualcosa con 2 classi uguali ) e come vedete mi sono limitato a metodi "const". Così questo funzionerà con metodi "non-const", ma se fai qualcosa di sbagliato cattura il bug sarà quasi Unpossible. E anche con l'affermazione c'è un out 1 possibilità su 4 miliardi di riuscire affermazione anche quando si suppone a fallire (Assert (ID == altri-> ID);)

A proposito .. Un buon design OO non dovrebbe richiedere questo genere di cose, ma nel mio caso ho cercato di refactoring / ri-progettare il codice senza essere in grado di abbandonare l'uso di cr casting. in generale si può evitare questo tipo di cose.

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