Une raison de préférer static_cast à une chaîne de conversions implicites?

Supposons que j’ai une classe implémentant plusieurs interfaces

class CMyClass : public IInterface1, public IInterface2 { }; 

et dans une fonction membre de cette classe, je dois obtenir un pointeur void* sur l’une de ces interfaces (situation typique dans IUnknown::QueryInterface() .

La solution typique consiste à utiliser un static_cast pour effectuer le réglage du pointeur:

 void* pointer = static_cast( this ); 

et c’est sans danger dans ce cas s’il n’y a pas de classe connue héritée de CMyClass . Mais si une telle classe existe:

 class CDerivedClass : public CUnrelatedClass, public CMyClass {}; 

et je fais accidentellement

 void* pointer = static_cast( this ); 

et this s’agit en fait d’un pointeur sur CMyClass instance de CMyClass le compilateur ne m’attrapera pas et que le programme risque de static_cast ultérieurement un comportement indéfini – static_cast devient non sécurisé.

La solution suggérée consiste à utiliser la conversion implicite:

 IInterface2* interfacePointer = this; void* pointer = interfacePointer; 

On dirait que cela résoudra les deux problèmes – ajustement du pointeur et risque d’invalidité négative.

Y a-t-il des problèmes dans la deuxième solution? Quelles pourraient être les raisons de préférer le premier?

Vous pouvez utiliser ce modèle:

 template T* up_cast(U* p) { return p; } 

usage:

 struct B {}; struct C : B {}; int main() { C c; void* b = up_cast(&c); } 

Notez que le ‘*’ est implicite. Si vous préférez up_cast , ajustez le modèle en conséquence.

L’atsortingbution de void * est toujours dangereuse. Quelle que soit la façon dont vous l’écrivez, vous pouvez vous tromper – en supposant que l’utilisateur essaie de QI pour Interface1, aucun des éléments suivants ne sera un avertissement ou une erreur:

 Interface2* interfacePointer = this; void* pointer = interfacePointer; 

ou

 void* pointer = static_cast( this ); 

Étant donné le risque minime d’utilisation accidentelle d’un static_cast dans un fichier, dans un fichier qui n’aura probablement même pas access à la définition de la classe dérivée, je constate beaucoup d’efforts supplémentaires pour très peu de sécurité réelle.

Je ne vois aucune raison de ne pas utiliser cette dernière solution, mis à part le fait que, si quelqu’un d’autre lit votre code, il ne communiquera pas immédiatement pourquoi vous utilisez une déclaration aussi compliquée (“pourquoi n’utilise-t-il pas simplement une static_cast?!? “), il serait donc préférable de le commenter ou de clarifier l’intention.

Votre parsing me semble saine. Les raisons pour ne pas utiliser votre approche implicite ne sont pas convaincantes:

  • légèrement plus verbeux
  • laisse les variables traîner
  • static_cast <> est sans doute plus commun, donc plus susceptible d’être évident pour les autres développeurs, recherché etc.
  • dans de nombreux cas, même les déclarations de classes dérivées n’apparaissent pas avant la définition des fonctions de classe de base, il n’y a donc pas de risque potentiel de ce type d’erreur

Si vous avez peur de faire quelque chose par accident avec static_cast je vous suggère de static_cast le pointeur casting / interface dans une fonction de modèle, par exemple, comme ceci:

 template  void set_if_pointer (void * & p, SourceClass * c) { Interface * ifptr = c; p = ifptr; } 

Vous pouvez également utiliser dynamic_cast et rechercher la NULL pointeur NULL .

 template  void set_if_pointer (void * & p, SourceClass * c) { p = dynamic_cast(c); }