gpt4 book ai didi

c++ - 使用非虚拟接口(interface)沿模板类向下转换

转载 作者:太空宇宙 更新时间:2023-11-04 13:46:32 26 4
gpt4 key购买 nike

我正在实现一个有限元代码。

问题描述

在有限元方法中,我们需要一个积分器和一个内插器。积分器是对几何对象(例如,四边形,三角形等)执行数值积分的对象。积分器将多个积分点或横坐标放置在几何对象内部,然后使用插值器近似计算的值。这些集成点的功能。

例如,四边形对象可以使用使用4个积分点的积分器。

* ------------- *
| |
| @ @ |
| |
| |
| @ @ |
| |
* ------------- *


其中@代表积分点的位置。内插器使用*表示的角节点处的值来近似这些积分点处的函数值。您可以将其视为@的每个值都是所有*值的平均值。

多态性概述

下表为方便起见显示了此问题中使用的不同类之间的连接:

               Interpolator
|
| is a base for:
v
Interpolator_Template
|
| is a base for:
|
------------------------------------------
| | |
| | More shapes
V V
Interpolator_Quad Interpolator_Tria
| | |
| ... More QUADs More TRIAs
v
Interpolator_Quad_04


Integrator
|
| has member variable
v
vector<Abscissa*> abscissa
|
| Abscissa is a base class for:
|
--------------------------------
| | |
| More shapes |
V V
Abscissa_Quad Abscissar_Tria


每个几何形状都有不同的坐标系,因此我的积分器和横坐标如下所示:

积分器

class Integrator {

public:

void
integrate();

private:

/**
* An interpolator class.
*/
Interpolator * _interpolator;

/**
* List of abscissae (for the quadrilateral shown above, _abscissae.size() == 4).
*/
std::vector< Abscissa * > _abscissae;
};


所有自然坐标横坐标的基类。

横坐标

class Abscissa {

};


四边形横坐标作用于ξ和η自然坐标。

Abscissa_Quad.hpp

class Abscissa_Quad final : public Abscissa {

public:

const double xi;

const double eta;
};


三角形横坐标作用于ζ1,ζ2和ζ3自然坐标。

横坐标_Tria.hpp

class Abscissa_Tria final : public Abscissa {

public:

const double zeta_1;

const double zeta_2;

const double zeta_3;
};


集成器的实现将如下所示进行集成:

积分器

void
Integrator::integrate()
{
for ( Abscissa * abscissa : _abscissae ) {
_intepolator->eval_det_J( abscissa );
}
}


到目前为止,一切都很好。让我向您展示我的内插器类。

插值器

class Interpolator {

public:

/**
* Evaluate the determinant of the Jacobian (important for numerical integration).
*
* @note Common interface for all abscissa types.
*
* @param[in] abscissa Integration abscissa.
* @return Shape functions.
*/
virtual double
eval_det_J(
const Abscissa * abscissa ) const = 0;
};


从插值器中,我导出所有几何形状的类。
您可能会注意到,我使用Interpolator_Template类作为基础。现在忽略它,我将在稍后解释细节。
此类包含所有四边形共有的函数。

Interpolator_Quad.hpp

class Interpolator_Quad : public Interpolator_Template< Abscissa_Quad > {

public:

// ... More functions common to all quadrilaterals.
};


此派生类对应于此问题开始处绘制的四边形。
得出它的原因是因为可能存在带有更多插值节点的四边形。
此类实现了QUAD_04元素(具有4个插值节点的四边形),但是在有限元素中,我们还具有QUAD_08,QUAD_09等。

Interpolator_Quad_04.hpp

class Interpolator_Quad_04 final : public Interpolator_Quad {

public:

double
eval_det_J(
const Abscissa_Quad * abscissa ) const;
};


Interpolator_Quad_04.cpp

double
Interpolator_Quad_04::eval_det_J(
const Abscissa_Quad * abscissa ) const
{
// Important! Get coordinates from an Abscissa_Quad object.
const double xi = abscissa.xi;
const double eta = abscissa.eta;

double det_J = ...
// ... Perform some computations and return the determinant of the Jacobian.

return det_J;
}


让我回过头来我以前没有解释过的Interpolator_Template类。在我的代码中的某个时刻,我执行从一个Abscissa *到一个Abscissa_Quad *对象的向下转换。我通过将模板类与非虚拟接口模式结合使用来实现此目的。

Interpolator_Template.hpp

template< class Abscissa_Derived >
class Interpolator_Template : public Interpolator {

public:

/**
* Implements Interpolator::eval_det_J.
*/
double
eval_det_J(
const Abscissa * abscissa ) const;

protected:

/**
* Implemented by Interpolator_Quad_04 in this example.
*/
virtual double
eval_det_J(
const Abscissa_Derived * abscissa ) const = 0;

private:

Abscissa_Derived *
eval_abscissa(
const Abscissa * abscissa ) const;
};


Interpolator_Template.cpp

template< class Abscissa_Derived >
double
Interpolator_Template< Abscissa_Derived >::eval_det_J(
const Abscissa * abscissa ) const
{
Abscissa_Derived * abscissa_type = this->eval_abscissa( abscissa );

double det_J = this->eval_det_J( abscissa_type );

return det_J;
}

template< class Abscissa_Derived >
Abscissa_Derived *
Interpolator_Template< Abscissa_Derived >::eval_abscissa(
const Abscissa * abscissa ) const
{
// Dynamic cast occurs here.
// I will include some check later to check for nullptr.
return dynamic_cast< Abscissa_Derived * >( abscissa )
}


我确定这段代码包含错误,因为我只需要复制和粘贴我认为对理解和执行修改所必需的内容。但是,我希望我的想法正确理解。

我知道向下转换通常是代码的味道,因此在我开始对有限元素中的所有几何形状实施积分器和插值器之前,我想征询您的意见。

我以前的尝试

这是我实现的最后一个设计模式。我将在下面解释我尝试过的其他设计;但是,您可以跳过本节。


一种双调度设计模式(特别是访问者模式),其中积分器是派生的而不是插值器。例如,我有一个Integrator_Quad_04而不是Interpolator_Quad_04。由于不再衍生出横坐标,因此Integrator_Quad_04具有一个Abscissa_Quad作为成员变量。

class Integrator_Quad_04 final : public Integrator {

private:

std::vector< Abscissa_Quad * > _abscissae;

public:

double
eval_det_J(
const std::size_t & index,
const Interpolator * interpolator ) const
{
// The interpolator acts as the visitor.
interpolator->eval_det_J( _abscissa[index] );
}
}

/// Abscissa_Quad is no longer derived from Abscissa.

class Abscissa_Quad {

public:

const double xi;

const double eta;
};


然后,插值器将成为集成器类的访问者,并访问其_abscissae成员变量。我决定不进行这种设计,因为这样插值器将不得不基于操作而不是形状来实现。

class Interpolator {

// ...

};

class Eval_Det_J : public Interpolator {

double
eval_det_J(
const Abscissa_Quad * abscissa ) const;

double
eval_det_J(
const Abscissa_Tria * abscissa ) const;
};

我尝试使用多个分派进行操作,但是所有形状所需的功能数量增长很快。
双调度+模板的多种变体。
我在这里找到了当前使用的设计模式:

Object Oriented Design Problem, Liskov Substitution Principle


结论

正如您从代码中推断的那样,我正在使用C ++ 11进行编译。

您可能想知道为什么我不简单地将积分器和插值器组合为一个类,而答案是因为积分器可能在四边形的子域上运行。例如,我可以在四边形内部引入一个虚构的三角形,并将积分点放置在该三角形内部,但是我仍然会使用四边形插值来近似该三角形点内部的解。当然,我将需要在三角形和四边形坐标之间实现一些映射,但这又是一个问题。

我想知道您是否认为向下广播不是解决此问题的好方法,还是我错过了什么。也许我不了解解决此问题的设计模式,或者我的多态架构不正确。

任何反馈表示赞赏。谢谢!

最佳答案

我将其发布为答案,因为评论太多了,也许有帮助。

您可以将Integrator用作模板类:

template<class Shape>
class Integrator {
typedef typename Shape::AbscissaType AbscissaType;
public:
void integrate() const {
for (const AbscissaType& abscissa : _abscissae) {
_intepolator->eval_det_J(abscissa);
}
}

template<class OtherShape>
Integrator<OtherShape> convert(/* maybe pass range of abscissae indices */) const {
// Abscissae classes must have converting constructors like AbscissaTria(const AbscissaQuad&);
std::vector<typename OtherShape::AbscissaType> newAbscissae(_abscissae.begin(), _abscissae.end());
// initialize resulting integrator with newAbscissae
}

Interpolator_Template<AbscissaType> _interpolator;
std::vector<AbscissaType> _abscissae;
};


如果仍然需要,特定的集成商将从适当的基本模板继承而来:

class Integrator_Quad_04 : public Integrator<Quad> {
};


因此,您不需要基 Interpolator类,并且 eval_det_J是接受虚假横坐标类型的非虚函数:

template<class AbscissaType>
class Interpolator_Template {
public:
double eval_det_J(const AbscissaType& abscissa) const;
}


我在此处添加了 Shape以强调横坐标类型取决于形状,并且您可能还有其他一些东西取决于形状:

struct Tria {
typedef AbscissaTria AbscissaType;
static const size_t NUM_EDGES = 3; // just example
};

struct Quad {
typedef AbscissaQuad AbscissaType;
static const size_t NUM_EDGES = 4; // just example
};


我认为您的代码中已经有此类。

另请注意,您也不再需要基 Abscissa,所有横坐标类都将成为独立的。

编辑:如果需要将横坐标转换为其他类型,则可以在 Integrator类中实现以下功能:

template<class OtherShape>
Integrator<OtherShape> convert(/* maybe pass range of abscissae indices */) const {
}


编辑2:在 convert类中添加了 Integrator的示例实现。

关于c++ - 使用非虚拟接口(interface)沿模板类向下转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25633089/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com