5

我有一个类来表示浮点的 3D 矢量:

class Vector3D
{
    public:

    float x, y, z;
    float * const data;

    Vector3D() : x(0.0), y(0.0), z(0.0), data(&x) {}
}

我的问题是:x、y 和 z 是否会在内存中按顺序分配,以便我可以将 x 的地址分配给数据,然后在数据上使用下标运算符将向量分量作为数组访问?

例如,有时我可能想直接访问向量组件:

Vector3D vec;
vec.x = 42.0;
vec.y = 42.0;
vec.z = 42.0;

有时我可能想通过偏移量访问它们:

Vector3D vec;
for (int i = 3; i--; )
    vec.data[i] = 42.0;

第二个示例是否与第一个示例具有相同的效果,或者我是否冒着覆盖除 x、y 和 z 浮点数之外的内存的风险?

4

6 回答 6

6

不,这是未定义的行为,原因有两个:

  • 首先是其他人提到的填充问题。
  • 其次,即使正确填充了内容,取消引用具有偏移量的指针也是无效的,该偏移量会使指针超出其指向的范围。编译器可以自由地假设这一点,并进行优化,如果您违反它会导致未定义的行为。

但是,以下内容将是有效的:

class Vector3D
{
public:
    std::array<float,3> data;
    float &x, &y, &z;

    Vector3D() : data(), x(data[0]), y(data[1]), z(data[2]) { }
    Vector3D& operator =(Vector3D const& rhs) { data = rhs.data; return *this; }
};

std::array是 C++0x 的新手,基本上等同于boost::array. 如果您不想要 C++0x 或 Boost,则可以使用 a std::vector(并将初始化程序更改为data(3)),虽然这是一个更重量级的解决方案,但它的大小可以从外部修改,如果是,那么结果将是UB。

于 2011-06-07T23:37:28.437 回答
2

是的。这个类是布局兼容 的 standard-layout,因为:

  • 您没有虚拟功能。
  • 所有数据成员都在一个访问说明符块中(public:

因此,它可以保证像 C 结构一样按顺序布局。这就是允许您将文件头作为结构读取和写入的原因。

于 2011-06-07T23:49:08.690 回答
1

编译器在如何布局结构中的内存方面具有一定的灵活性。该结构永远不会与另一个数据结构重叠,但它可以在元素之间注入未使用的空间。在您提供的结构中,一些编译器可能会选择在 z 和 data 之间添加 4 个字节的额外空间,以便可以对齐数据指针。大多数编译器都提供了一种将所有内容紧密打包的方法。

编辑:不能保证编译器会选择紧密地打包 x、y 和 z,但实际上它们会被很好地打包,因为它们是结构的第一个元素,而且它们的大小是 2 的幂。

于 2011-06-08T00:12:54.803 回答
1

或者你可以有一个 operator[] 重载

float operator[](int idx)
{
 switch (idx)
{
case 0:
  return x;
case 1:
  return y;
case 2:
 return z;
}
assert (false);
}
于 2011-06-08T00:19:13.683 回答
1

您的解决方案无效,但是如果您可以确保(或知道)您的编译器将“做正确的事情”(特别是通过控制 x、y 和 z 元素之间的填充),您就可以了。在这种情况下,虽然我会data完全删除该成员并使用operator[].

我见过这样的东西偶尔使用。它遇到了完全相同的问题,但确实节省了您存储该数据指针的时间,并允许使用更好的v[0]语法而不是v.data[0].

class Vector3D
{
    public:

    float x, y, z;
    float& operator[](int i) { return *(&x+i); }
    const float& operator[](int i) const { return *(&x+i); }

    Vector3D() : x(0.0), y(0.0), z(0.0) {}
}

编辑:ildjam 提示使用访问器而不是成员的兼容版本,这是相似的。

class Vector3D
{
    public:
      float& operator[](int i) { return v[i]; }
      const float& operator[](int i) const { return v[i]; }

      float& x() { return v[0]; }
      float  x() const { return v[0]; }
      float& y() { return v[1]; }
      float  y() const { return v[1]; }
      float& z() { return v[2]; }
      float  z() const { return v[2]; }

      Vector3D() : v() {}
    private:    
      float v[3];
};
于 2011-06-08T00:27:49.733 回答
-1

做这样的事情:

float data[3];
float& x, y, z;

    Vector3D() : x(data[0]), y (data[1]), z(data[2]) { data [0] = data [1] = data [2] = 0;}
于 2011-06-07T23:44:51.190 回答