C++中类变量声明的查询

发布于 2024-11-14 12:12:21 字数 538 浏览 3 评论 0原文

我有一个类来表示浮点数的 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 是否浮动?

I have a class to represent a 3D vector of floats:

class Vector3D
{
    public:

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

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

My question is: are x, y, and z going to be allocated sequentially in memory such that I can assign the address of x to data and later use the subscript operator on data to access the vector components as an array?

For example, sometimes I may want to access the vector components directly:

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

And sometimes I may want to access them by offset:

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

Will the second example have the same effect as the first one, or do I run the risk of overwriting memory other than the x, y, and z floats?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

浅紫色的梦幻 2024-11-21 12:12:22

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

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

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

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,您可以使用 std::vector (并将初始化程序更改为 data(3)),尽管这是一个更重量级的解决方案,它的大小可以从外界修改,如果是的话,那么结果将是UB。

No, this is undefined behaviour, for two reasons:

  • Firstly for the padding issues that everyone else has mentioned.
  • Secondly, even if things are padded correctly, it is not valid to dereference a pointer with an offset that would take it beyond the bounds of what it's pointing to. The compiler is free to assume this, and make optimisations that would lead to undefined behaviour if you violate it.

However, the following would be valid:

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 is new to C++0x, and is basically equivalent to boost::array. If you don't want C++0x or Boost, you could use a std::vector (and change the initializer to data(3)), although that's a much more heavyweight solution, its size could be modified from the outside world, and if it is, then the result would result be UB.

痴情 2024-11-21 12:12:22

是的。这个类是布局兼容 标准布局,因为:

  • 你没有虚函数。
  • 所有数据成员都位于单个访问说明符块(public:)中

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

Yes. This class is layout-compatible standard-layout, because:

  • You have no virtual functions.
  • All data members are in a single access specifier block (the public:)

Because of this, it's guaranteed to be laid out sequentially just like a C structure. This is what allows you to read and write file headers as structures.

影子的影子 2024-11-21 12:12:22

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

float operator[](int idx)
{
 switch (idx)
{
case 0:
  return x;
case 1:
  return y;
case 2:
 return z;
}
assert (false);
}

or you can have an operator[] overload

float operator[](int idx)
{
 switch (idx)
{
case 0:
  return x;
case 1:
  return y;
case 2:
 return z;
}
assert (false);
}
夜无邪 2024-11-21 12:12:22

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

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

The compiler has some flexibility in how it lays out the memory within a struct. The struct will never overlap another data structure, but it can inject unused space between elements. In the struct you give, some compilers might choose to add 4 bytes of extra space between z and data so that the data pointer can be aligned. Most compilers provide a way of packing everything tightly.

EDIT: There's no guarantee that the compiler will choose to pack x, y, and z tightly, but in practice they will be packed well because they are the first elements of the struct and because they're a power of two in size.

呆头 2024-11-21 12:12:22

您的解决方案无效,但如果您可以确保(或知道)您的编译器将“做正确的事情”(特别是通过控制 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];
};

Your solution is not valid, but if you can ensure (or know) that your compiler will "do the right thing" (in particular by controlling padding between the x, y and z elements) you will be ok. In this case though I'd remove the data member altogether and use operator[].

I've seen something like this used on occasion. It runs into exactly the same issues, but does save you storing that data pointer, and allows for a nicer v[0] syntax rather than 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) {}
}

EDIT: Prompted by ildjam heres a compliant version using accessors rather than members, that is similar.

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];
};
终难遇 2024-11-21 12:12:22

做这样的事情:

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

    Vector3D() : x(data[0]), y (data[1]), z(data[2]) { data [0] = data [1] = data [2] = 0;}

Do something like this:

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

    Vector3D() : x(data[0]), y (data[1]), z(data[2]) { data [0] = data [1] = data [2] = 0;}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文