c ++的最佳变通方法

时间:2014-08-11 21:05:50

标签: c++ virtual instanceof dynamic-cast

我听说dynamic_cast&的选项rtti(但成本很高)或虚拟功能,但我不确定哪个是最好的!

假设我有以下示例

我有一个包含2个子类HumanMan的父类Woman

Human ofc提供了一些标准方法。 但是,我们可以说WomanMan有不同的方法。

为什么我需要知道对象的实例? 让我们说我的功能只允许人类作为参数。 所以参数可以是男人或女人。 但是对于进一步的操作,我需要知道Human对象实际上是Man还是Woman,然后将其转换为其中一个。

我认为在这种情况下使用虚函数不是一个好主意,因为子类有不同的方法数量?!

4 个答案:

答案 0 :(得分:2)

你是对的 - 如果你发现自己需要施法或rtti,这是一个好兆头,也许你需要重新考虑你的设计。

但是“继承”的整个想法是你可以用专门的方法和数据来定义一个“人”类,这些方法和数据只与“男性”有关,一个不同的类“女人”有她自己的专业数据和方法......和这些类的父类“人”是他们共有的方法。

此外,点击通常应该使用“人”类(通常不应该关心实例恰好是“男人”还是“女人”。客户应该知道 little 关于类实例的可能性 - 足以完成它的工作;不再需要。

最后,如果“男人”和“女人”做同样的操作,但需要不同 ......这就是虚拟方法的用武之地。

这些链接可能有所帮助:

答案 1 :(得分:1)

你这样做:

switch (human->gender) {
   case GenderMan: 
      doTheManlyThing((Man*)human);
      break;
   case GenderWoman: 
      doTheWomanlyThing((Woman*)human);
      break;
   default:
      abort("Unknown gender, stop the world");
}

故意使用符合C语言的语法。如果你要像往常一样回去做什么,为什么不去寻找完整的蒙太奇呢?

如果您使用typeid(*human)代替整数类型指示符,或者if (dynamic_cast<whatewer*>) ...就像今天的孩子一样,那么概念上也是如此,只是稍微(或很多)慢。

替代方案当然是

human->doTheHumanThing();   // a call to  a virtual function
                            // possibly with an empty body
                            // in case one of the genders 
                            // "has more actions" than the other(s)

但真正的程序员不使用虚函数。他们打开了类型ID,他们喜欢它

另一种选择是访客模式,它又是一堆虚拟功能,只能水平放置而不是垂直放置,可以这么说。我不希望这个答案成为访客的第11521273条描述,所以我只会推荐你the universal font of wisdom

答案 2 :(得分:0)

如果您发现自己因分析而陷入瘫痪,请记住过早优化的规则(即不要这样做)。继续吧。您可以在以后的路上选择其他路径(只是不要等待太久)。 :)

  

在每篇文章中,我都读到了dynamic_cast带来的成本。我没有   测试了它 -

这种担心有一个简单的解决方案,它是编程的基本技能:自己测试。

关于您对RTTI / dynamic_cast性能表现的担忧(在评论中):同时应用过早优化的规则。除非您发现问题,否则虚拟函数调用相对较快,您不应该关心。除非您有性能问题的证据,否则实际上是否存在性能问题? Bjarne Stroustrup在他的一本C ++书中提到了人们对虚拟调度性能的怀疑。如今,测试显示50,000,000个虚拟调度调用只需不到一秒钟。在我的分析中,虚拟呼叫的性能几乎与非虚拟呼叫相同。开销是统计噪音(除了最罕见的情况之外)。

利用当今的处理器和缓存架构,结合智能C ++编译器,我们应该不再担心虚拟调度或RTTI的开销。在我的PC上,我可以使用dynamic_cast&lt;&gt;来测试50,000,000个对象的向量。在一秒钟内,包括if语句和每个分支路径的另一条指令。

   // populate vector
   std::vector<A*> avec;
   for (int i = 0; i < 50000000; i++) {
      if (i %  2)
         avec.push_back(new A());
      else
         avec.push_back(new B());
   }

   clock_t begin = clock();

   int j = 0;
   for (int i = 0; i < 50000000; i++) {
      if (dynamic_cast<B*>(avec[i])) {
         j += 1;
      }
      else
         j -= 1;
   }

   clock_t end = clock();
   double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
   cout << "dynamic_cast: " << elapsed_secs << std::endl;
   cout << "j=" << j << std::endl;

PS:如果您尝试使用此代码,我建议使用较小的矢量并重复多次重复。我实际上编译并运行为x64项目,但关键是不要分配一个5000万项目向量,重点是显示跨越一堆随机对象指针的虚拟调度。对于32位,它可能会产生内存不足错误。

如果C ++虚拟调度对于您的目的而言太慢,那么您可能不太关心优雅的面向对象设计,而是更多关于调整每个最后一个周期。在这种情况下,删除虚函数,广泛使用内联函数,或者使用非面向对象的C语言或汇编语言编写。

答案 3 :(得分:0)

我实际上找到了另一种解决方案。 我正在使用的框架实际上为此提供了解决方案。 如果(对象 - &GT;的getclass() - &GT; IsChildOf(APlayerCharacter :: StaticClass()))

这实际上对我很有帮助!