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

时间:2017-06-17 14:55:42

标签: c++ class data-structures memory-layout

我有一个非虚拟类模板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;
}

那么,如果我使用原始点来对象A或成员变量a.x,我会正确地获取其他变量吗?

3 个答案:

答案 0 :(得分:1)

正如许多用户所指出的那样,无法保证结构的内存布局与相应的数组相同。并且&#34;在意识形态上正确&#34;通过索引访问成员的方法是创建一些丑陋的operator [],其中包含switch

然而,实际上,您的方法通常没有问题,并且建议的解决方案在代码生成和运行时性能方面较差。

我可以建议其他两种解决方案。

  1. 保留您的解决方案,但在编译时中验证您的结构布局是否与数组相对应。在你的具体案例中,STATIC_ASSERT(sizeof(a) == sizeof(double)*3);
  2. 将模板类更改为实际上是一个数组,并将x,y,z变量转换为访问函数到数组的元素中。
  3. 我的意思是:

    #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
        // ...
    };
    

    如果你将数组的长度(即所表示的向量的维度)作为模板参数,你可以放置一个&#39; STATIC_ASSERT&#39;在每个访问函数中确保成员的实际存在。

答案 1 :(得分:0)

不,没有保证,不是你做的方式。例如,如果T是一个int8_t,如果你指定了1字节的打包,那么它只能

最简单,最正确的方法是在模板类中添加一个operator [],如:

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

但这并不是很有效率。更有效的方法是将矢量(或点)坐标放在数组中,使用x(),y(),z()成员函数来访问它们。如果您在类中实现了一个T *运算符,那么您的示例将适用于所有情况。

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

答案 2 :(得分:-1)

如果你真的想做这样的事情:

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