c ++中深度继承树的性能影响

时间:2011-11-05 06:38:53

标签: c++ performance inheritance

是否存在与深度继承树(在c ++中)相关的任何效率缺点,即,一大组类A,B,C等,使得B扩展A,C扩展B,等等。我能想到的一个效率含义是,当我们实例化最底层的类时,比如C,那么也会调用B和A的构造函数,这将对性能产生影响。

3 个答案:

答案 0 :(得分:12)

让我们列举一下我们应该考虑的操作:

建筑/销毁

每个构造函数/析构函数都将调用其基类等价物。然而,正如James McNellis指出的那样,无论如何你显然都会做这项工作。你没有从A派生,因为它就在那里。所以这项工作将以某种方式完成。

是的,它将涉及更多的函数调用。但是,与实际工作相比,函数调用开销将无关紧要,任何深层次的层次结构都必须实际执行。如果你正处于函数调用开销对性能实际上很重要的地步,我强烈建议调用构造函数可能不是你想在那段代码中做的。

对象大小

通常,派生类的开销是空的。虚拟成员的开销是指针或虚拟继承。

成员函数调用,静态

通过这个,我的意思是调用非虚方法成员函数,或者使用类名称调用虚拟成员函数(ClassName :: FunctionName语法)。这两个都允许编译器在编译时知道要调用哪个函数。

这种性能与层次结构的大小不变,因为它是编译时决定的。

成员函数调用,动态

这是在完全和完全期望运行时调用的情况下调用虚函数。

在大多数理智的C ++实现中,这与对象层次结构的大小不变。大多数实现为每个类使用v表。每个对象都有一个v表指针作为成员。对于任何特定的动态调用,编译器访问v表指针,选择方法并调用它。由于v-table对于每个类都是相同的,因此对于具有深层次结构的类而言,对于具有浅层的类而言,它不会更慢。

虚拟继承与此有关。

Pointer Casts,Static

这是指static_cast或任何等效操作。这意味着从派生类到基类的隐式转换,显式使用static_cast或C风格的转换等等。

请注意,这在技术上包括参考投射。

类之间(向上或向下)的静态强制转换的性能与层次结构的大小不变。任何指针偏移都将是编译时生成的。这应该适用于虚拟继承以及非虚拟继承,但我不是百分之百确定。

Pointer Casts,Dynamic

这显然是指明确使用dynamic_cast。这通常在从基类转换为派生类时使用。

对于大型层次结构,dynamic_cast的性能可能会发生变化。但是,理智的实现应该只检查当前类和所请求的类之间的类。所以它在两者之间的类数中只是线性的,而不是层次结构中类的数量的线性。

TYPEOF

这意味着使用typeof运算符来获取与对象关联的std::type_info对象。

这种性能将与层次结构的大小不变。如果类是虚拟类(具有虚函数或虚基类),那么它只是将它从vtable中拉出来。如果它不是虚拟的,那么它是编译时定义的。

结论

简而言之,大多数操作与层次结构的大小不变。但即使在有影响的情况下,也不是问题。

我更关心一些设计伦理,你觉得有必要建立这样的层次结构。根据我的经验,这样的等级制度来自两个设计线。

  1. Java / C#理想的所有内容都来自公共基类。这在C ++中是一个可怕的想法,永远不应该使用。每个对象应该来自需要的内容,而且只有那个。 C ++建立在“为你使用的东西付费”原则的基础上,并从一个共同的基础推导出来。一般来说,你可以用这种公共基类做的任何事情都是你不应该做的事情,或者可以用函数重载完成的事情(例如,使用operator<<转换为字符串)。 / p>

  2. 滥用继承权。在应该使用包含时使用继承。继承在对象之间创建“是一种”关系。通常情况下,“有一个”关系(一个对象有另一个对象作为成员)更有用和灵活。它们可以更容易地隐藏数据,并且您不允许用户假装一个类是另一个类。

  3. 确保您的设计不会违反其中一项原则。

答案 1 :(得分:2)

会有程序员性能影响但不会那么糟糕。

答案 2 :(得分:0)

正如@Nicol指出的那样,它可能会做很多事情。 如果这些是您需要完成的事情,无论设计如何,因为它们都是在尽可能少的周期内将程序从call main升级到exit的必要步骤,那么您的设计就是编码清晰度的问题(或者可能缺乏编码)。

根据我的经验,性能调优as in this example,我经常认为浪费时间的巨大来源是数据(即类)结构的过度设计。 很奇怪,数据结构的理由经常是(猜猜是什么?) - 性能!

根据我的经验,与数据结构有关的事情是让它尽可能简单并尽可能标准化。如果它被完全标准化,那么对它的任何单一更改都不会使它不一致。您无法始终达到完全正常,在这种情况下,您必须处理数据可能暂时不一致的可能性。 这就是人们编写通知处理程序的原因,这在OOP中是值得鼓励的。 我们的想法是,如果您在一个地方更改某些内容,则可以触发“自动”将更改传播到其他位置的通知,从而尝试保持一致性。

通知问题是他们可以逃跑。简单地将一些布尔属性从true更改为false可能导致通过数据结构的通知的火灾风暴,没有任何程序员理解,更新数据库,绘制窗口,压缩文件等。我经常发现这是大多数时钟周期的发生地。

我认为暂时容忍不一致性并且通过某种扫描过程定期修复它更简单,效率更高。

数据结构伴随着巨大的低效率的另一种方式是,如果某些过程有效地解释数据以产生一些输出。 这在图形中很常见。 如果数据以非常慢的速率变化,那么“编译”它而不是“解释”它可能是有意义的。 换句话说,将其转换为更简单的指令集,或“动态”编译的源代码,然后可以更快地执行以产生所需的输出。