动态绑定还是开关/案例?

像这样的场景:
我有不同的对象执行与各自func()实现类似的操作。
func_manager()有两种解决方案可以根据不同的对象调用func()

解决方案1 :使用c ++中指定的虚函数字符。 func_manager以不同的方式对不同的对象点传入进行工作。

class Object{ virtual void func() = 0; } class Object_A : public Object{ void func() {}; } class Object_B : public Object{ void func() {}; } void func_manager(Object* a) { a->func(); } 

解决方案2 :使用普通开关/shell。 func_manager的工作原理与不同类型的传入不同

 typedef enum _type_t { TYPE_A, TYPE_B }type_t; void func_by_a() { // do as func() in Object_A } void func_by_b() { // do as func() in Object_A } void func_manager(type_t type) { switch(type){ case TYPE_A: func_by_a(); break; case TYPE_B: func_by_b(); default: break; } } 

我的问题是2:
1.在设计模式的观点,哪一个更好?
2.在RUNTIME EFFCIENCE的观点上,哪一个更好? 特别是当对象的种类增加时,总共可能达到10-15,哪一个的开销超过另一个? 我不知道switch / case如何实现内部,只是一堆if / else?

非常感谢!

从设计模式的角度来看哪一个更好?

使用多态(解决方案1)更好。
只有一个数据点:想象一下,你有一个围绕两者中的任何一个构建的庞大系统,然后突然需要添加另一种类型 。 使用解决方案一,您可以添加一个派生类 ,确保它在需要的地方实例化,然后您就完成了。 使用解决方案2,您可以在整个系统中覆盖数以千计的switch语句 ,并且或多或少无法保证找到所有必须为新类型修改它们的位置。

从RUNTIME EFFCIENCE的观点来看,哪个更好? 特别是对象的种类

这很难说。
我记得Stanley Lippmann的Inside the C ++ Object Model中的一个脚注,他说研究表明,虚拟函数对于切换类型可能有一个小优势。 然而,我会很难引用一章和一节,并且,IIRC,优势似乎不足以使决定依赖于它。

第一种解决方案更好,只因为它的代码更短。 它也更容易维护和编译更快:如果你想添加类型,你只需要添加新类型作为头和编译单元,而无需更改和重新编译负责类型映射的代码。 实际上,这段代码是由编译器生成的,并且可能比您自己编写的任何代码都高效或高效。

最低级别的虚函数的成本不超过表(数组)中的额外取消引用。 但是没关系,在这个微观数字上,这种性能挑剔真的无关紧要。 您的代码更简单,这才是最重要的。 C ++为您提供的运行时调度是有原因的。 用它。

  1. 我会说第一个更好。 在解决方案2中, func_manager必须知道所有类型,并在每次添加新类型时进行更新。 如果你使用解决方案1,你可以稍后添加一个新类型, func_manager将正常工作。

  2. 在这个简单的例子中,我实际上猜测解决方案1会更快,因为它可以直接查找vtable中的函数地址。 如果你有15种不同的类型,那么switch语句可能不会以跳转表的forms结束,但基本上就像你说一个巨大的if / else语句一样。

从设计的角度来看,第一个肯定是更好的,因为inheritance的目的是使不同的对象表现得一致。

从效率的角度来看,在两种选择中,您或多或少都有相同的生成代码,某些地方必须有选择制作代码。 区别在于inheritance在第一个中自动为您处理它,而您在第二个中手动处理它。

使用“ 动态调度 ”或“虚拟调度”(即调用虚函数)在设计(保持一个地方的变化,可扩展性)和运行时效率方面都更好(简单的解引用与跳转的简单解引用)使用了table或者if … else梯子,它真的很慢)。 稍微说一下,“动态绑定”并不意味着你的想法……“动态绑定”是指根据最近的声明解析变量的值,而不是“静态绑定”或“词法绑定”,指的是通过声明它的当前最内部范围来解析变量。

设计
如果另一个程序员出现谁无法访问您的源代码并且想要创建Object的实现,则该程序员被卡住…扩展function的唯一方法是在非常长的switch语句中添加另一个案例。 对于虚函数,程序员只需要从您的接口inheritance并提供虚拟方法的定义,而walla,它的工作原理。 此外,那些switch语句最终到处都是,因此添加新实现几乎总是需要在任何地方修改许多switch语句,而inheritance将更改本地化为一个类。

效率
动态调度只是在对象的虚拟表中查找函数,然后跳转到该位置。 它非常快。 如果switch语句使用跳转表,则速度大致相同; 但是,如果实现很少,一些程序员会试图使用if … else梯形图而不是switch语句,它通常不能利用跳转表,因此速度较慢。

为什么没人建议function对象? 我认为kingkai有兴趣解决这个问题,而不仅仅是那两个解决方案。
我对他们没有经验,但他们的工作是:

 struct Helloer{ std::string operator() (void){ return std::string("Hello world!"); } }; struct Byer{ std::string operator() (void){ return std::string("Good bye world!"); } }; template< class T > void say( T speaker){ std::cout << speaker() << std::endl; } int main() { say( Helloer() ); say( Byer() ); } 

编辑:在我看来,这是比单一方法(不是函数调用操作符)的类更“正确”的方法。 实际上,我认为这个重载被添加到C ++中以避免这样的类。
此外,即使您不想要模板,函数对象也更方便使用 - 就像通常的函数一样。
最后考虑STL - 它在任何地方使用func对象,看起来很自然。 我甚至没有提到Boost