C.146: Use dynamic_cast where class hierarchy nigation is unoidableC.146:如果无法避免在继承层次中移动,使用dynamic_cast
Reason(原因)
在运行时检查dynamic_cast。
运行时的Dynamic_cast检查。
Example(示例)
struct B { //一个接口虚void f();虚void g();虚拟~ B();};struct D : B { //更宽的接口void f()重写;虚拟void h();};void user(B * Pb){ if(D * PD = dynamic _ cast & lt;D * & gt(pb)) { //…使用D & # 39s接口…} else { //…凑合着用B& # 39;s接口…} }使用其他强制转换可能会违反类型安全,并导致程序访问实际上属于X类型的变量,就好像它属于不相关的Z类型一样:
使用其他类型转换不能保证类型安全,并导致程序将实际上是X类型的变量作为z类型来访问。
void user 2(B * Pb)//bad { D * PD = static _ cast & lt;D * & gt(Pb);//我知道pb确实指向一个D;相信我//…使用D & # 39s接口…} void user 3(B * Pb)//unsafe { if(some _ condition){ D * PD = static _ cast & lt;D * & gt(Pb);//我知道pb确实指向一个D;相信我//…使用D & # 39s接口…} else { //…凑合着用B& # 39;s接口…} } void f(){ B B;用户(& b);//OK user 2(& b);//坏错误user 3(& b);//ok * if *程序员把some _ condition检查对了}
注意和其他强制转换一样,dynamic_cast被过度使用了。比起造型,更喜欢虚函数。在可能(不需要运行时解析)且相当方便的情况下,最好使用静态多态而不是层次导航。
像其他类型的转换一样,dynamic_cast已经被过度使用了。应该使用虚函数而不是类型转换。在继承系统中移动时,如果可能(没有执行时决策)且更方便的话,应该使用静态多态机制。
Note(注意)
有些人使用dynamic_cast,而使用typeid可能更合适;dynamic_cast是一个通用& # 34;是种& # 34;发现对象的最佳接口的操作,而typeid是一个& # 34;告诉我这个东西的确切类型& # 34;操作来发现对象的实际类型。后者本质上更简单,应该更快。如果需要的话,后者(typeid)很容易手工创建(例如,如果在一个由于某种原因禁止RTTI的系统上工作),前者(dynamic_cast)通常很难正确实现。
有些人在typeid更合适的时候使用dynamic _ castDyamic_cast只是一个判断,用来寻找一个对象的最优接口& # 34;是某种类型& # 34;的通常操作。typeid是“告诉我对象的实际类型& # 34;获取对象的类型。后者肯定会更简单,应该是更快的操作。如果有必要,后者(typeid)更容易自己实现(比如RTTI因为某种原因被工作系统禁止),而一般来说,前者(dynamic_cast)的正确实现要困难得多。
考虑(考虑):
struct B { const char * name { & # 34B&第34名;};//如果PB1-& gt;id()= = Pb2-& gt;id() *pb1与*pb2虚拟const char * id()const { return name;的类型相同;} // …};struct D:B { const char * name { & # 34;D & # 34};const char* id() const覆盖{ return name} // …};void use(){ B * PB1 = new B;B* pb2 =新D;cout & lt& ltPB1->;id();// "B&第34名;cout & lt& ltPb2->;id();// "D & # 34if(PB1->;id()= = & # 34;D & # 34){ //看起来很无辜D * pd = static _ cast & ltD * & gt(PB1);// …} // …} Pb2的结果-& gt;id()= = & # 34;D & # 34实际上是实现定义的。我们添加它是为了警告家庭酿造RTTI的危险。这些代码可能会像预期的那样工作几年,只是在新机器、新编译器或不统一字符文字的新链接器上失败。
Pb2->;id()= = & # 34;D & # 34结果实际上是实现了决策。我们添加它是为了警告自制RTTI的危险。这些代码可能会像预期的那样工作很多年,但当它遇到新的机器、新的编译器或新的连接器而没有统一的字符文字时,它就会失败。
如果您实现自己的RTTI,请小心。
如果你自己实现RTTI,要小心。
Exception(例外)
如果您的实现提供了非常慢的dynamic_cast,您可能需要使用一种变通方法。但是,所有无法静态解决的变通方法都涉及显式转换(通常是static_cast ),并且容易出错。你基本上是在制作你自己的特殊用途的动态造型。所以,首先要确保你的dynamic_cast真的像你想象的那样慢(有很多没有根据的传言),并且你对dynamic_cast的使用真的是性能关键。
如果您的实现提供了非常慢的dynamic_cast,那么您可能需要灵活一些。但是所有的修改都不能静态解决,容易出错,包括显示类型转换(通常是static_cast)。您只能为特殊目的设计dynamic_cast。所以,首先要确定你的dynamic_cast真的有你想象的那么慢(有一些关于它的未经证实的传言)并且你使用dynamic_cast的地方真的对性能那么敏感。
我们认为当前的dynamic_cast实现太慢了。例如,在适当的条件下,可以在快速常数时间内执行dynamic_cast。然而,兼容性使得改变变得困难,即使所有人都同意优化的努力是值得的。
我们认为dynamic_cast的当前实现有些不必要的慢。比如dynamic_cast在合适的条件下可以在很短的固定时间内完成。但是,兼容性使得它很难改变,即使所有人都同意优化是有价值的。
在极少数情况下,如果您已经测量到dynamic_cast开销很大,您有其他方法静态地保证向下转换会成功(例如,您正在小心地使用CRTP),并且不涉及虚拟继承,请考虑战术性地求助于static_cast,用突出的注释和免责声明来总结本段,并且在维护时需要人工注意,因为类型系统可以& # 39;t验证正确性。尽管如此,在我们的经验中这样& # 34;我知道我& # 39;我在干什么& # 34;情况仍然是一个已知的bug源。
极少数情况下。如果已经确定dynamic_cast的影响确实存在,可以用其他方法静态保证下转换成功(比如你谨慎使用CRTP),如果不涉及虚拟继承,战术上可以考虑采用带有明显注释的static_cast。但是因为类型系统无法验证正确性,所以需要对这段代码做一个免责声明,并进行提醒。根据我们的经验,即使在这个层次上,“我知道我在做什么& # 34;这种情况仍然是众所周知的错误来源。
例外(例外)
考虑(考虑下面的代码):
模板& lt键入名称B& gt;Dx类:B { //…};
Enforcement(标记所有用于市区的static_cast,包括执行static_cast的c样式转换)。指出使用static _ cast实现向下转换的情况,包括进行static _ cast的C风格转换。该规则是类型安全配置文件的一部分。该规则也是类型安全规则组的内容。
RTTI:运行时类型信息。
CRTP:静态分布。请参考:
https://Eli . the green place . net/2013/12/05/the-cost-of-dynamic-virtual-calls-vs-static-crtp-dispatch-in-c/
原始连接:
https://github . com/isocpp/cppcore guidelines/blob/master/cppcore guidelines . MD # c146-use-dynamic _ cast-where-class-hierarchy-nigation-is-oid
觉得这篇文章有帮助?请分享给更多的人。
更多精彩文章欢迎关注【面向对象思维】,轻松每天学习!
面向对象开发,面向对象思维!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。