C++:繁重的多重继承层次结构的设计和成本

C++: Design and cost for heavy multiple inheritance hierarchies

本文关键字:多重继承 C++ 层次结构      更新时间:2023-10-16

我有一个包含以下三个类的类层次结构:

template<int pdim >
class Function
{
   virtual double operator()( const Point<pdim>& x) const = 0;
};

这是 pdim 维空间中的一个函数,返回双精度。

template<int pdim, int ldim >
class NodeFunction
{
   virtual double operator()( const Node<pdim,ldim>& pnode, const Point<ldim>& xLoc) const = 0;
};

它是来自 pdim 维空间中节点的 ldim 维局部空间的函数。

template<int pdim, int ldim, int meshdim >
class PNodeFunction
{
   virtual double operator()( const PNode<pdim,ldim,meshdim>& pnode, const Point<ldim>& xLoc) const = 0;
};

此设计的原因 1:NodeFunction 比 Function 更通用。它始终可以将本地 ldim 点映射到 pdim 点。例如,边(ldim=1 的节点(将区间 [0,1] 映射到 pdim 维物理空间。这就是为什么每个函数都是一个节点函数。NodeFunction 更通用,因为允许 NodeFunction 查询节点的属性。

这种设计的原因2:PNodeFunction比NodeFunction更通用。每个 PNode 只有一个节点(反之亦然(。这就是为什么每个PNodeFunction都是一个NodeFunction。PNodeFunction 更通用,因为它还具有 PNode 的所有上下文,PNode 是网格的一部分(因此它知道它的所有父项、邻居......

摘要:每个Function<pdim>都是ldim任何参数的NodeFunction<pdim, ldim>。每个NodeFunction<pdim, ldim>都是任何参数meshdimNodeFunction<pdim, ldim, meshdim>

问题:用C++表达这一点的最佳方式是什么,这样我就可以用Function代替NodeFunction/PNodeFunction,这样代码就很快了(它是一个高性能的计算代码(,这样代码就可以

模板参数不是完全独立的,而是相互依赖的:- pdim=1,2,3(主要兴趣(,但如果它也适用于 PDIM 高达 7 的值,那就太好了。- 'ldim=0,1,...,pdim'- 'Meshdim=ldim,ldim+1,...,pdim'

要考虑性能,请注意,程序中创建了几个函数,但它们的 operator(( 被调用了很多次。

变种

我想到了几种实现它的方法(我目前实现了变体 1(。我把它写在这里,以便您可以告诉我这些方法的优缺点。

变式 1

通过帮助程序模板Arec<dim,dim2>实现上述继承A<dim> inherits from B<dim,dim2>。在伪代码中,这是

class A<dim> : public Arec<dim,dim>;
class Arec<dim,dim2> : public Arec<dim,dim2-1>, public B<dim,dim2>;
class Arec<dim,0> : public B<dim,dim2>;

这既适用于从 NodeFunction 继承函数,也用于从 PNodeFunction 继承 NodeFunction。由于 NodeFunction 从 PNodeFunction 继承了大约 O(pdim^2) 倍,因此如何扩展?这个巨大的虚拟表不好吗?

注意:实际上每个函数也应该继承自VerboseObject,这允许我打印有关函数的调试信息,例如 std::cout .我通过几乎继承VerboseObject PNodeFunction来做到这一点。这将如何影响性能?这应该会增加构造函数和打印调试信息的时间,但不会增加operator()的时间,对吧?

变式2

不要用C++表达继承,例如 A<dim> 继承自 B<dim,dim2> bur,而是有一个函数来转换两者

class AHolder<dim,dim2> : public B<dim, dim> {
}
std::shared_pointer< AHolder<dim,dim2> > interpretAasB( std::shared_pointer< AHolder<dim> >)
 [...]

这有一个缺点,我不能再用Function<dim>代替NodeFunction<dim>PNodeFunction<dim>

变式3

您的首选实现方式是什么?

我不太理解你的问题;这可能是因为我缺乏问题领域的特定知识。

无论如何,您似乎想要生成一个类的层次结构,其中函数(最派生的类(在底部,PNodeFunction在顶部(派生最少的类(。
为此,我只能推荐Alexandrescu的现代C++设计书,尤其是关于层次结构生成器的章节。
有一个开源库源于名为Loki的书。这是您可能感兴趣的部分。

采用通用元编程方式可能是最难的,但我认为与虚拟继承相比,一旦设置,它就会变得易于使用,并可能提高性能(这始终由分析器验证(。

在任何情况下,我强烈建议不要从 Verbose 对象继承日志记录,而是使用单独的单例日志记录类。
这样,您就不需要类层次结构中的额外空间来存储日志记录对象。
您只能从 Verbose 对象继承派生最少的类,但您的函数类不是记录对象;他们使用日志记录对象(我在这里可能有点迂腐(。
另一个问题是,如果从该基类继承多次,则最终将获得日志记录对象的多个副本,并且必须使用虚拟继承来解决它。