为什么说不要使用 dynamic_cast,需要运行时确定类型信息,说明设计有缺陷?

为什么说不要使用 dynamic_cast,需要运行时确定类型信息,说明设计有缺陷?

前几天刚好回答了一个和这个类似的问题,关于此问题我还是想再补充几点:

1、《Google C++ 代码规范》对于dynamic_cast的规定「在google的使用场景下」没有问题。

2、「从设计模式的角度」,如果需要使用dynamic_cast说明设计有缺陷。这句话也没问题。

3、但是google不需要dynamic_cast,不代表别的地方也不需要。

4、dynamic_cast有办法用自定义的方法加速,性能可以提高。

现在我一条一条地解释:

1、《Google C++ 代码规范》对于dynamic_cast的规定「在google的使用场景下」没有问题。

我为什么这么说呢?因为google主要是做服务器。我也是做服务器,所以我对这点非常清楚。服务器采一般都采用分布式的构架,将功能拆分成N个进程,因为功能已经被拆成很多进程,所以在代码的组织和构架的设计的难度上从一开始就已经降低了至少一个数量级,所以完全可以做到不需要dynamic_cast。

也就是说,这个编码规范对于服务器开发来说完全可行。下面是祥光的编码规范的详细内容,可见也它也没有100%禁止。从我自己的角度来说,根据我多年的经验,我在服务器端是可以完全做到不实用dynamic_cast的。

2、「从设计模式的角度」,如果需要使用dynamic_cast说明设计有缺陷。

这里的设计模式指的是GOF的设计模式,这套理论在90年代横空出世然后影响力一直持续到现在,相关的设计模式被写成了一本书,被认为是面向对象设计理论的圣经。里面的例子几乎都用到了虚函数,但是没有任何一个是需要dynamic_cast的,因为它展示的就是理想化的设计。

我举个例子,设计模式中的Observer模式,中间有类似这样的代码:

Observer是抽象类,在Subject中保存在一个集合中,notify函数中对这些observer进行遍历,遍历的时候根本不用关心它们到底是啥具体类型,代码的通用性就很好。

如果在这个遍历的过程中,需要针对每个observer的子类进行向下转换然后再调用子类特有的方法,其实说明抽象程度不够。

总结下来就是:只要抽象的程度足够,就不需要dynamic_cast。如果抽象程度不够,当然是设计的缺陷。

3、但是google不需要dynamic_cast,不代表别的地方也不需要。

我在第一条里面说了,google做的主要是服务器,服务器因为分布式构架的功能拆分已经将设计难度降低了一个数量级,所以容易做到理想化的设计。

那么与服务器对应的是啥呢?当然就是客户端了。客户端没那么容易使用多进程,所有的功能集中在一个进程中,复杂度当然就高了一个数量级。于是客户端想要做到100分的理想化设计就比较难了。

在Qt、Unreal Engine写的代码中,使用dynamic_cast(这两货其实是自定义的dynamic_cast,我后面会说,但是功能上是等同的)就无法避免了。

鉴于篇幅,我这里不举例了。

即使从设计模式的角度来说,dynamic_cast不是100分,但并不是所有的场景都需要做到100分的设计,有时候70分、80分、90分都算完美,Qt/UE中的用法都是具体的例证。

4、dynamic_cast有办法用自定义的方法加速,性能可以提高。

与dynamic_cast效果等同的cast也可以自己实现,Qt和UE里面都是自己实现的。Qt提供了qobject_cast,UE提供了Cast,性能都更高。

最后总结如下:

分布式构架的服务器很容易做到完全不用dynamic_cast,所以可以把这个加到编码规范中去。但是此规则不适用于功能复杂的场景,例如某些不能将功能拆分成多个进程的客户端。