【问题标题】:C++ implementing vector fieldC++实现向量场
【发布时间】:2021-01-16 17:06:18
【问题描述】:

我正在尝试从 Lubos Brieda(等离子模拟示例)重新创建等离子模拟。为此,我需要实现一个 3D 场,其中填充了包含电场数据的物理矢量。 (我不想使用模板,我指的是数学/物理意义上的向量。)

本书展示了如何创建 Field 和 vec3 类,以创建具有三个组件的特定类型和数组(向量)的字段。

我的目标是找到实现矢量场的最简单方法,如上所述。由于书中提到 Field3(= Field_<vec3<double>>) 类型已经实现(但还没有实现),我认为有一个简单的选项可以做到这一点,而不必创建一个新类。我试过using Field3 = Field_<vec3<double>>,但总是导致以下错误:

Exception thrown: read access violation. **a** was nullptr. 

您可以在下面看到导致错误的代码,包括上面提到的类。 (我想我排除了所有不必要的内容。我留下了一些带有代码行的 cmets,您可以使用这些代码行来查看其余代码是否正常工作,只有 Field3 类型会引发错误。)

#include<iostream>
#include"World.h"
#include"Field_.h"
#include"vec3.h"

int main() {
    World world(21, 21, 21);

    return 0;
}
template <typename T>
struct vec3 {
    // assign data
    vec3(const T u, const T v, const T w) : d{ u,v,w } {}
    vec3(const T a[3]) : d{ a[0],a[1],a[2] } {}
    vec3() : d{ 0,0,0 } {}

//protected:
    T d[3];
};

using double3 = vec3<double>;   // three component vector with doubles
using int3 = vec3<int>;
#pragma once
#include"vec3.h"

template<typename T>
class Field_ {  // underscore for undefined type
public:
    // constructor
    Field_(int ni, int nj, int nk) : ni{ ni }, nj{ nj }, nk{ nk }{
        data = new T * *[ni];           // ni pointers to pointers of type T
        for (int i = 0; i < ni; i++) {
            data[i] = new T * [nj];     // allocte nj pointers to T
            for (int j = 0; j < nj; j++)
                data[i][j] = new T[nk]; // allocate nk objects of type T
        }
        operator=(0);   // call the overloaded operator= function (initialize with 0)
    }

    // destructor, frees memory in reverse order
    ~Field_() {
        if (data == nullptr) return;        // return if unallocated
        for (int i = 0; i < ni; i++) {      // release memory in reverse order
            for (int j = 0; j < nj; j++)
                delete data[i][j];
            delete data[i];
        }

        delete[] data;
        data = nullptr;                     // mark as free
    }

    // overload the assignment operator
    Field_<T>& operator= (const T s) {
        std::cout << "Field_ operator=" << std::endl;
        for (int i = 0; i < ni; i++)
            for (int j = 0; j < nj; j++)
                for (int k = 0; k < nk; k++)
                    data[i][j][k] = s;
        return *this;                           // return reference to self
    }

    const int ni, nj, nk;   // number of nodes

protected:
    T*** data;  // pointer of type T
};

using Field = Field_<double>;   // field of doubles (scalar field)
using Field3 = Field_<double3>; // field of double3s (vector field)
#pragma once
#include"vec3.h"
#include"Field_.h"

class World {
public:
    World(int ni, int nj, int nk);  //constructor

    const int ni, nj, nk;

    //Field phi;    //electric potential (scalar field)
    Field3 ef;  //electric field (vector field)
};

//World::World(int ni, int nj, int nk): ni{ ni }, nj{ nj }, nk{ nk }, phi(ni, nj, nk){}
//World::World(int ni, int nj, int nk) : ni{ ni }, nj{ nj }, nk{ nk }, phi(ni, nj, nk), ef(ni, nj, nk) {}
World::World(int ni, int nj, int nk) :
    ni{ ni }, nj{ nj }, nk{ nk }, ef(ni, nj, nk) {}

我希望你能帮我弄清楚该怎么做,如果我能在此期间提供任何帮助,请告诉我。

【问题讨论】:

  • 由于您似乎遇到了一个高度可重复的问题,是时候部署您的开发环境附带的调试器了。当程序崩溃时,调试器通常会停止,并允许您检查崩溃站点以寻找线索。然后,您使用收集到的线索来查看接下来应该检查的内容。使用断点将程序推进到感兴趣的位置,然后逐行执行程序以查找意外情况,程序通常存储错误的值或采取错误的路径。意外几乎总是一个错误。
  • 仔细检查operator=(0)。这几乎可以肯定是错误。 operator= 期望用 T 调用,它会尽一切努力将该 0 转换为 T,包括调用 vec3 构造函数并将 0 转换为空指针。
  • 一旦你将data成员的类型T***替换为std::vector&lt;T&gt;,然后计算3D该连续数组的子集索引。专业的密集张量/数组/场实现几乎总是连续的一维数组,而不是嵌套数组。
  • 旁注:delete data[i][j]; 应该是 delete[] data[i][j];delete data[i]; 应该是 delete[] data[i]; 因为你正在处理数组。
  • 感谢到目前为止的 cmets!我使用调试器工作,我相信问题出在operator=vec3 的构造函数上。您认为可以对标量值以及我的三分量向量使用相同的Field_ 类吗?还是有问题,因为一个需要比另一个更多的指向指针?

标签: c++ simulation


【解决方案1】:

我找到了避免该错误的方法,因为它仅在尝试使用 operator=(0) 初始化矢量场时出现。但是,如果我想用零初始化一个标量字段,则必须使用此命令。因此,对vec3 类型使用相同的命令会导致上述错误,因为覆盖的operator= 仅适用于标量值。幸运的是data[i][j] = new T[nk]; 已经用零初始化了vec3s(如果T 是某种vec3(就像我想使用的double3 = vec3&lt;double&gt;))。

我的解决方案是切换operator=(0)

if (!std::is_same<T, double3>::value) {operator=(0);}

因此,当T 的类型为double3 时,不使用标量初始化。就我而言,这应该足够了,因为所有矢量字段都应该是double 类型。虽然如果我想要另一种类型,我可以调整 if 语句。 (我认为也可以检查T 是否类似于vec3&lt;...&gt;,但我没有尝试过这架飞机,因为我似乎不需要任何其他类型的矢量场。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-03
    相关资源
    最近更新 更多