C++ 如何创建多态容器

C++ How to create polymorphism container?

本文关键字:多态 创建 何创建 C++      更新时间:2023-10-16

以下问题:我们有大网格。在每个点中,我们有两个具有不同结果数组的字段。

第一个数组是 3D。这意味着有三个坐标(i,j,k(,每个点都有五个物理数据(压力,密度,速度[x y z](。它看起来像:

Array3D < XXX::LocalFlow < double > > resultarray1;

LocalFlow 是一个自制的模板类,其中包含五个物理变量。

第二个数组是 4D。它被添加频率(i,j,k,f(。有很多频率。每个频率都有与平均流(第一个数组(相同的数据,但数据类型在这里complex <double>

Array4D < XXX::LocalFlow < std::complex < double > > > resultarray2;

我想合并这两个字段。

第一个

想法:将第一个融入第二个。但这很糟糕,因为我需要数百万点的双倍内存空间(complex<double>而不是double(。

第二个想法:创建一个多态容器,其中包含具有不同数据类型的 LocalFlow 对象(第一个是双精度,其余所有复杂对象(。

我读了很多关于boost::any的信息。我的问题是:boost::any 是否仅适用于简单的类型,如 int、float、double 或自己的类型?

还有其他方法可以解决我的问题吗?

我会看看 boost 变体,因为您想要存储的类型没有太大不同 (2(。特别是对于访问者模式,这使得读取/写入/更改数据变得容易。

另一个解决方案是,据我所知,创建一个三维网格,其中元素如下所示:

struct type
{
    XXX::LocalFlow < double > mean_element;
    XXX::LocalFlow < std::complex<double> > * f_values_array;
}
对我来说,使用 boost::any 似乎有点

矫枉过正,因为从本质上讲,boost::any 是使用通用类型的 void* 的"更安全"方式。

正如@ThomasFannes所建议的,boost::variant 是一个选项,因为它建议以一种优雅、非常易于阅读的方式执行静态多态性。

但是,为了多样化,我建议另一种选择。

首先,让我们定义一个GenericLocalFlow结构,该结构将承载LocalFlow<double>LocalFlow<std::complex<double>>,例如,使用 boost::optional 类。此泛型类存储的实际类型在构造时确定。此类的实现可以读取

class GenericLocalFlow {
  // Here, you could define constructors that fit your needs...
  // ...
  boost::optional<LocalFlow<double>>& asScalar();
  boost::optional<LocalFlow<std::complex<double>>>& asComplex();
};

请注意,成员函数不const用于读/写目的。此外,在调用任一成员函数时,您可以查询结构的标量版本或复杂版本是否已初始化。

然后,我建议您将完整的数据集存储在

Array3D<std::vector<GenericLocalFlow>> results;

std::矢量占第四维,即频率。

我直言,最好的两种可能性是:

    创建自定义向量,该向量
  1. 将分别保存每种类型,并在引擎盖下有两个单独的向量。
  2. 为两者创建基类,并通过虚拟类使用(运行时(多态性。然后,您当然不需要直接存储对象,而是指向它们的指针(但是,每个值都需要额外的两个指针,这可能很容易大于额外的双精度的大小,并且实际上还需要这两个指针对于复杂<>类型(。

boost::any 与解决方案 2 基本相同。 - 它在后台使用类型擦除,因此每个实例也有两个额外的指针(指向类型擦除持有者的指针和指向其虚拟表的指针(。有了"奖励",您将需要将所有内容来回键入正确的类型。

使用 boost::

variant 你没有额外的虚拟指针,但不幸的是,boost::variant 存储必须(显然(与它可以容纳的最大类型一样大(所以直接合并类型没有改进,关于内存使用情况(。

请参阅我对类似问题的回答:https://stackoverflow.com/a/35003245/1274747

上面的选项 1. 还有一个优点,那就是您可以分别迭代不同的类型,这对于 CPU 缓存行为更好(对于具有虚拟方法的多态类尤其如此,这就是为什么游戏程序员有时会根据实际类型对多态游戏对象的集合进行排序,以迭代块中的各种类型,从而不会丢弃指令缓存 - 所以即使在情况 2.可能希望根据不同的类型对它们进行排序(。

不要这样做。 只需存储复数数组。

如果 f 大小中等(例如 50-100(,则在较小维度上将 1 个数组的大小剃掉一倍可节省 0.5% 的内存。

元素上的任何类型的安全变体都将使用比这更多的方法。 我们谈论的是 20%-100% 的内存开销和可能严重的运行时成本。

一个三维double, array<complex<double>>数组,其中第一个是"f=0",数组是频率数据确实减少了0.5%(好吧,频率计数/200(,代价是代码复杂性和访问速度较慢(由于分支(。

除非频率数量很少,或者您更频繁地访问 f=0 数据数量级(因此缓存命中率使用紧凑数据更好(,或者您除了微小的优化之外没有什么更好的事情要做,否则您的计划看起来不是一个好主意。