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),
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.
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;}
No, this is undefined behaviour, for two reasons:
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.
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);
}
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];
};
Yes. This class is layout-compatible standard-layout, because:
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.