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:
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); }