简体   繁体   English

C ++非虚拟类成员变量的内存布局?

[英]C++ non-virtual class member variables memory layout?

I have a non-virtual class template A as below and I do the following 我有一个非虚拟的类模板A ,如下所示

#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? 那么,是否可以保证如果我对对象A或成员变量ax使用原始点,我将正确获得其他变量?

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. 通过索引访问成员的“在理论上正确”的方法是创建一个丑陋的operator []并在其中进行switch

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); 在您的特定情况下,将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. 将您的模板类更改为实际上是一个数组,然后将x,y,z变量转换为访问函数并转换为数组的元素。

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. 如果您也将数组的长度(即表示的向量的维数)作为模板参数,则可以在每个访问函数中放置一个“ STATIC_ASSERT”以确保成员的实际存在。

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. 例如,如果T是int8_t,则在指定1字节打包时才有效

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. 一种更有效的方法是将向量(或点)坐标放置在数组中,并使用x(),y(),z()成员函数来访问它们。 Then you example would work in all cases, provided you implement a T* operator in your class. 然后,如果您在类中实现了T *运算符,那么您的示例将在所有情况下均适用。

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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