2011-09-13 2 views
3

Voici mon exemple de test:comment utiliser reinterpret_cast pour lancer un pointeur de classe dérivée en C++

struct base { 
    virtual ~base(){} 
    int x; 
}; 

struct derived: public virtual base { 
    base * clone() { 
     return new derived; 
    } 
    derived(): s("a") {} 
    std::string s; 
}; 

int main() { 
    derived d; 
    base * b = d.clone(); 
    derived * t = reinterpret_cast<derived*>(b); 
    std::cout << t->s << std::endl; 
    return 0; 
} 

Il tombe en panne sur la ligne où imprimer s. Puisque "b" est un pointeur vers la classe dérivée, reinterpret_cast devrait simplement fonctionner. Je me demande pourquoi il se bloque. En même temps, si je remplace reinterpret_cast par dynamic_cast, alors cela fonctionne.

Répondre

8

Même si b est ici dynamiquement de type derived, vous devez utiliser dynamic_cast. C'est ce que dynamic_cast est pour convertir dynamiquement un pointeur d'une classe de base en une classe dérivée au moment de l'exécution.

reinterpret_cast prend le pointeur brut et le considère comme étant du type dérivé. Toutefois, en raison de l'héritage virtual, un léger ajustement doit être effectué sur le pointeur pour pointer vers la table d'acheminement de méthode correcte, et c'est précisément ce que fera dynamic_cast.

+0

N'avait pas pour l'héritage virtuel, 'static_cast' aurait travaillé. Il n'est pas nécessaire d'aller en cours d'exécution quand vous savez à coup sûr que votre base est dérivée. –

+0

Oui, vous avez raison, si je supprime _virtual_ et le rend juste structure dérivée: base publique, puis reinterpret_cast fonctionne aussi (au moins, il n'y a pas de plantages) – Andrei

+0

K-ballo: vous savez, les règles laides de l'héritage lors de la construction des bibliothèques (vous ne savez pas si dans le futur une classe sera héritée via deux chemins) vous forcer à abuser parfois de l'héritage virtuel juste au cas où. –

1

Ne pas reinterpret_cast, il va causer des problèmes avec l'héritage multiple ou virtuel, comme dans votre cas. Wont un simplement static_cast faire le travail ici? Pour savoir pourquoi, recherchez des implémentations d'héritage virtuel. Un point commun est de stocker un pointeur sur la classe de base dans l'objet, de sorte que la base virtuelle ne partage pas la même adresse que ses classes dérivées. Il existe un cas similaire lorsque l'héritage multiple est utilisé.

En résumé, reinterpret_cast ne peut pas faire beaucoup plus que de lancer des pointeurs sur ints et back (s'il y a assez de taille dans l'int pour contenir un pointeur).

+0

Dans ce cas, le compilateur dit que erreur: ne peut pas convertir de la base 'base' au type dérivé 'dérivé' via la base virtuelle 'base' – Andrei

+1

Vous ne pouvez pas lancer de façon statique à partir d'une base virtuelle. Vous devez donc 'dynamic_cast' ici (ce qui implique des pénalités d'exécution). En plus de cela, je suis d'accord que 'reinterpret_cast' est ** mauvais **! – bitmask

+0

Ensuite, vous devriez aller avec 'dynamic_cast' à la place. –

0

Comme les autres réponses ici proposées, vous ne pouvez pas utiliser reinterpret_cast de cette façon parce que la valeur du pointeur-base diffère réellement de la valeur du pointeur-derived. Le pointeur valide est déduit à l'exécution, c'est pourquoi vous devez utiliser dynamic_cast. Comme vous ne savez pas au moment de la conception par quel type intermédiaire la classe la plus dérivée (celle que vous voulez utiliser) a été dérivée du type pour lequel vous avez un pointeur.

La vraie question ici devrait être: Je sais au moment du design, comment calculer le pointeur derived à partir du pointeur base. Comment peut-on éviter la pénalité d'exécution (de dynamic_cast)?

Franchement, je ne vois pas une très bonne option, mais une option possible est de stocker le pointeur sur le type le plus dérivé d'un pointeur constant à l'intérieur de la classe racine, comme ceci:

struct base { 
    void* const self; 
    virtual ~base() {} 
    protected: 
    base(void* self) : self(self) {} 
}; 
struct derived : public virtual base { 
    derived() : base(this) {} 
} 

Ceci est moche et dangereux, car il sacrifie la sécurité de type pour la performance (si vous êtes vraiment chanceux, vous obtenez une légère performance d'exécution à partir de lui). Mais vous serez en mesure de reinterpret_cast votre pointeur base (le membre self de type void*) dans un pointeur derived.

Questions connexes