虚拟方法开销是否很重要?

时间:2015-11-29 18:12:41

标签: c++ inheritance

我有一些复杂的继承结构,主要是为了避免代码复制和促进各种类的通用接口。它依赖于虚拟和非虚拟继承,看起来或多或少是这样的:

class AbstractItem
{
   //bunch of abstract methods
};

class AbstractNode : virtual public AbstractItem
{
    //some more virtual abstract methods
};

class AbstractEdge : virtual public AbstractItem
{
    //yet some different virtual abstract methods
};

然后是一些像这样的“真实”类

class Item : virtual public AbstractItem
{
    //implements AbstractItem
};

class Node : public Item, public AbstractNode
{
    //implements AbstractNode
};

class Edge : public Item, public AbstractEdge
{
    //implemetns AbstractEdge
};

并将其打包到图模型类中,以便:

class AbstractGraph
{
    virtual QList<AbstractNode*> nodes() const = 0;
    virtual QList<AbstractEdge*> edges() const = 0;
};

class GraphModel : public AbstractGraph
{
public:
    virtual QList<AbstractNode*> nodes() const override; //this converts m_Nodes to a list of AbstractNode*
    virtual QList<AbstractEdge*> edges() const override; //dtto

private:
    QList<Node*> m_Nodes;
    QList<Edge*> m_Edge;
};

这种错综复杂的结构的原因是有不同的类实现AbstractGraph,例如排序模型,过滤和那些有不同变体的类 - 有些存储它们的数据就像显示的模型一样,并且有自己的AbstractItem集合/ Node / Edge派生类,其他是动态的,并且依赖于底层图形/模型的数据而没有它们自己的数据。例如:

class FilterNode : public AbstractNode
{
    //access the data in the m_Item via AbstractItem interface and implements differently AbstractNode interface
private:
    AbstractItem *m_Item = nullptr; //this variable holds pointer to some real item with actual data such as the one from GraphModel
};

class GraphFilter : public AbstractGraph
{
    //implements the interface differently to the GraphModel
private:
    QList<FilterNode*> m_Nodes;
    AbstractGraph *m_Source = nullptr; //source graph...
};

我对此有第二个想法,因为它依赖于(虚拟)继承,依赖于通过base等调用的抽象方法。这样的开销是否显着?

另一种选择是:

a)复制 - 粘贴大量代码以避免虚拟方法和大部分继承,但这将是代码维护的噩梦。加上没有通用接口...

b)模板全部以某种方式......我有点不确定,我不知道它是否可能。我已经在这个地方的几个地方使用它们以避免代码重复。

那么它看起来是否合理或类似矫枉过正?我可以补充一点,在某些情况下,我将直接调用方法(在模型内)绕过虚拟调用,但在外部它几乎总是通过抽象基调用。

1 个答案:

答案 0 :(得分:3)

尝试使用动态多态与C ++实现通用图算法

  1. 不必要的努力。
  2. 不必要的慢。
  3. 虚函数开销越显着,功能越简单。在引用的界面中,您还可以从各种函数返回容器。即使这些是COW容器,也涉及一些工作,随便访问序列可能很容易取消共享(即复制)表示。

    在稍微遥远的过去(大约1990年至1996年),我已经尝试了基于动态多态的图算法的通用实现,并且正在努力解决各种问题以使其工作。当我第一次阅读有关STL的文章时,发现大多数问题可以通过类似的抽象来解决(尽管仍然缺少一个关键思想:属性映射;有关详细信息,请参阅下面对BGL的引用)。

    我发现在类似STL的抽象方面实现图算法更为可取。算法是根据特定的概念实现的功能模板,除了两个主要区别外,它们有点像基类:

    1. 抽象中不涉及虚​​函数调用,函数通常可以内联。
    2. 从函数返回的类型只需要建模一个合适的概念,而不是必须通过某种形式的继承与某个特定的接口兼容。
    3. 不可否认,我有偏见,因为我在这个主题上写了diploma thesis。对于这种方法的[独立开发]应用,请查看Boost Graph Library (BGL)

      对于比较不同函数调用方法的一些性能测量,请查看function call benchmarks。它们是在Performance TR的函数调用的性能测量之后建模的。