简体   繁体   中英

C++ non-virtual class member variables memory layout?

I have a non-virtual class template A as below and I do the following

#include <iostream>
// my class template
template<typename T>
class A
{
    public:
    T x;
    T y;
    T z;
    // bunch of other non-virtual member functions including constructors, etc
    // and obviously no user-defined destructor
    // ...
};

int main()
{
    //now I do the following
    A<double> a;
    a.x = 1.0; // not important this
    a.y = 2.0;
    a.z = 3.0;

    // now the concerned thing 
    double* ap = (double*)&a;
    double* xp = &(a.x);

    // can I correctly and meaningfully do the following?     
    double new_az = ap[2]; // guaranteed to be same as a.z (for any z) ? ** look here **
    double new_z = xp[2]; // guaranteed to be same as a.z (for any z) ? ** look here **

    std::cout<<new_az<<std::endl;
    std::cout<<new_z<<std::endl;
    return 0;
}

So, is it guaranteed that if I use a raw point to object A or to the member variable ax , I will correctly get the other variables?

As many users pointed out, there is no guarantee that the memory layout of your structure will be identical to the appropriate array. And "ideologically correct" way to access members by index would be creating some ugly operator [] with a switch inside it.

However, speaking practically, there is usually no problem with your approach, and the suggested solutions are inferior in terms of code generated and run-time performance.

I can suggest 2 other solutions.

  1. Keep your solution, but verify in compile-time that your structure layout corresponds to an array. In your specific case putting STATIC_ASSERT(sizeof(a) == sizeof(double)*3);
  2. Change your template class to be actually an array, and convert the x,y,z variables into the access functions into the elements of the array.

I mean:

#include <iostream>
// my class template
template<typename T>
class A
{
public:
    T m_Array[3];

    T& x() { return m_Array[0]; }
    const T& x() const { return m_Array[0]; }

    // repeat for y,z
    // ...
};

If you make the length of the array (ie dimension of the represented vector) a template parameter as well, you may put a 'STATIC_ASSERT' in each access function to ensure the actual existence of the member.

No, there is no guarantee, not the way you do it. If T is a int8_t, for example, it would work only if you specified 1-byte packing.

The easiest, and correct way to do this, would be to add an operator [] to your template class, something like:

T& operator[](size_t i)
{
  switch(i)
  {
  case 0: return x;
  case 1: return y;
  case 2: return z:
  }
  throw std::out_of_range(__FUNCTION__);
}

const T& operator[](size_t i) const
{
  return (*const_cast<A*>(this))[i];  // not everyone likes to do this.
}

But this is not really efficient. A more efficient way is to have your vector (or point) coordinates in a array, and x(), y(), z() member functions to access them. Then you example would work in all cases, provided you implement a T* operator in your class.

operator T*() { return &values[0]; }
operator const T*()const  { return &values[0]; }

If you really want to do such things:

template <typename T>
class FieldIteratable
{
  using Data = std::array<T, 5/*magic number*/>;
  Data data_;
  public:
  const Data & data() { return data_; }
  T& a1 = data_[0]; // or some macro
  char padding1[3]; // you can choose what field is iteratable
  T& a2 = data_[1];
  char padding2[3]; // class can contain other fields can be
  T& a3 = data_[2];
  char padding3[3];
  T& a4 = data_[3];
  char padding4[3];
  T& a5 = data_[4];


};



int main() {

  FieldIteratable<int> fi;

  int* a = &fi.a1;
  *a++ = 0;
  *a++ = 1;
  *a++ = 2;
  *a++ = 3;
  *a++ = 4;

  std::cout << fi.a1 << std::endl;
  std::cout << fi.a2 << std::endl;
  std::cout << fi.a3 << std::endl;
  std::cout << fi.a4 << std::endl;
  std::cout << fi.a5 << std::endl;

  for(auto i :fi.data())
    std::cout << i << std::endl;

  return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM