【问题标题】:Command pattern without virtual functions (C++)没有虚函数的命令模式 (C++)
【发布时间】:2010-11-01 23:59:37
【问题描述】:

出于性能原因,我使用Curiously Reoccuring Template Pattern 来避免虚函数。我有很多执行数百万次的小命令。我正在尝试将其融入命令模式。我想将大量命令添加到队列中,然后逐个遍历它们执行。每个命令都使用 CRTP 来避免虚拟功能。我遇到的问题是命令模式通常是使用指针向量实现的。但是当 Command 类被模板化时,就很难传递通用的 Command 指针。我不是 C++ 专家,所以也许有一种明显的方法来存储模板化命令对象的向量?我一直在尝试使用类似的东西:

boost:ptr_vector commands;
AddCommand(Command* command) {
  commands.push_back(command);
}

问题是Command 不是类型,所以Command* command 给出了编译错误。我需要使用Command<CommandType>,但这不起作用,因为我需要队列来保存不同类型的命令。

有解决方案的想法吗?还是虚函数是我唯一的选择?

添加:命令对象是蒙特卡罗模拟算法的一部分。所以你可能有,Command 是来自正态分布的随机数,其中正态分布的参数是类的一部分。所以命令模式非常适合。我有很多按特定顺序调用需要维护状态的函数。

【问题讨论】:

  • 您的命令示例会有所帮助。如果您有一堆小的命令模板类,那么在您的示例中发布 1 或 2 个作为示例可能会帮助人们提出替代方案。至少,它将提高未来读者对问题的理解。
  • 您能否提供更多关于您使用命令模式解决的特定场景的信息?另外,解释为什么你认为虚函数太慢了。有没有用虚函数实现过,觉得太慢了?

标签: c++ performance design-patterns templates virtual-functions


【解决方案1】:

CRTP 通过在编译时解析对象的运行时类型来发挥其魔力,以便编译器可以内联函数调用。如果你有一个指向泛型类型的指针向量,编译器无法确定具体的具体类型,也无法进行编译时解析。

仅从您在问题中获得的信息来看,我认为虚函数是您的最佳选择。然而,虚函数并没有那么慢。当然,它们比内联函数慢,但在许多情况下它们足够快!特别是如果您的进程受限于 I/O 时间而不是处理时间。

One of the answersthis question 对此问题进行了更深入的讨论。总而言之,虚函数调用的开销可能以纳秒为单位。它比这更复杂,但关键是你不应该害怕虚函数,除非你的函数正在做一些非常微不足道的事情,比如单个赋值。您说您的命令很小,所以也许是这样。我会尝试用虚函数做一个快速原型,看看是否能提供可接受的性能。

【讨论】:

  • 我没有对它进行基准测试,所以也许你是对的。在简单的方面,命令可能只是对随机数生成器的调用。这些在一个非常紧密的循环中被称为数百万次。你能猜出虚函数开销是多少吗?
  • 如果你的伪随机数生成器是一个简单的线性同余生成器,这可能很重要。一些像 Mersenne Twister 这样更复杂的指令可能会有足够的指令,因此虚拟调用开销是微不足道的。
  • 除非你在命令方法和它调用的所有方法中做一些极其微不足道的事情,否则我建议使用虚拟方法。如果您在所有/大多数命令方法中都在做一些微不足道的事情,那么 void* 可能是您的朋友。
  • 直到它咬掉你的手 ;-)
【解决方案2】:

除非您在编译期间构建命令队列,否则您想要的都是不可能的。

【讨论】:

    【解决方案3】:

    我无法判断您的命令队列是经常更改还是很少更改。

    如果它很少更改,与执行频率相比,在我看来这可能是代码生成的工作。

    只需打印出一个程序来执行您需要的操作,即时编译和链接一个 dll,然后加载它。这应该需要大约一秒钟。没有类、对象或调度。如果您单步执行,您几乎不会看到对您的答案没有实质性贡献的周期。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-08
      • 2012-04-18
      • 1970-01-01
      • 2011-10-31
      • 2011-06-15
      • 2015-01-13
      • 2013-01-27
      • 2011-06-09
      相关资源
      最近更新 更多