我知道动态/静态多态性取决于应用程序的设计和要求。但是,如果可能的话,是否建议选择动态静态多态?特别是,我可以在我的应用程序中看到以下2个设计选择,两者似乎都被建议不要:
使用CRTP实现静态多态性:没有vtable查找开销,同时仍以模板基类的形式提供接口。但是,使用很多开关和static_cast来访问正确的类/方法,这是危险的
动态多态性:实现接口(纯虚拟类),将查询成本与访问者/变异器之类的简单函数相关联
我的应用程序非常关键,所以我赞成静态多态。但是需要知道使用过多的static_cast是否表明设计不佳,以及如何在不产生延迟的情况下避免这种情况。
编辑:感谢您的见解。针对具体案例,哪一种更好的方法?class IMessage_Type_1
{
virtual long getQuantity() =0;
...
}
class Message_Type_1_Impl: public IMessage_Type_1
{
long getQuantity() { return _qty;}
...
}
OR
template <class T>
class TMessage_Type_1
{
long getQuantity() { return static_cast<T*>(this)->getQuantity(); }
...
}
class Message_Type_1_Impl: public TMessage_Type_1<Message_Type_1_Impl>
{
long getQuantity() { return _qty; }
...
}
请注意,每个类中都有几个mutators / accessors,我需要在我的应用程序中指定一个接口。在静态多态性中,我只切换一次 - 获取消息类型。但是,在动态多态性中,我使用虚函数进行EACH方法调用。这不是一个使用静态聚合物的情况吗?我相信CRTP中的static_cast非常安全且没有性能损失(编译时限)?
答案 0 :(得分:14)
静态和动态多态性旨在解决不同的问题 问题,因此很少有两种情况都适合。在 这种情况下,动态多态将导致更灵活和 更容易管理设计。但大多数时候,选择将是 很明显,出于其他原因。
两者的粗略分类:虚拟功能允许不同 公共接口的实现;模板允许不同 通用实现的接口。
答案 1 :(得分:8)
一个开关只不过是一系列跳跃 - 在优化之后 - 变成跳转到由表查找的地址。完全像虚函数调用。
如果必须根据类型跳转,则必须先选择类型。如果在编译时无法完成选择(主要是因为它取决于输入),则必须始终执行两个操作:select&amp;跳。您用于选择的语法工具不会改变性能,因为优化相同。
事实上,你正在重新发明 v-table。
答案 2 :(得分:6)
您会看到与纯模板基于多态性相关的设计问题。虽然看起来虚拟基类可以让您非常了解派生类的预期,但在模板化设计中却会变得更加困难。通过在使用其中一个boost库时引入语法错误,可以很容易地证明这一点。
另一方面,您在使用虚拟功能时担心性能问题。证明这将是一个问题要困难得多。
恕我直言,这是一个非问题。坚持使用虚函数,直到另有说明。虚函数调用比大多数人想象的快得多(从动态链接库调用函数也增加了一层间接。似乎没有人想到这一点。)
我只考虑模板化设计,如果它使代码更容易阅读(通用算法),你使用已知的少数情况之一的虚拟函数(数字算法)或你已经将其识别为性能瓶颈
答案 3 :(得分:1)
如果被调用的方法可能被编译器内联,则静态多向性可能提供显着的优势。 例如,如果虚拟方法如下所示:
protected:
virtual bool is_my_class_fast_enough() override {return true;}
然后静态polimophism应该是首选方式(否则,该方法应该诚实并返回false :)。
“真实”虚拟呼叫(在大多数情况下)无法内联。
其他差异(例如vtable调用中的额外间接)是可忽略的
[编辑] 的
但是,如果确实需要运行时多态性 (如果调用者不应该知道方法的实现,因此,无法在调用者方面内联该方法) 不要重新发明vtable(如Emilio Garavaglia所提到的),只需使用它。