[英]C++ non-virtual class member variables 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
或成员变量ax
使用原始点,我将正确获得其他变量?
正如许多用户指出的那样,不能保证结构的内存布局将与适当的数组相同。 通过索引访问成员的“在理论上正确”的方法是创建一个丑陋的operator []
并在其中进行switch
。
但是,实际上,您的方法通常没有问题,建议的解决方案在代码生成和运行时性能方面也较差。
我可以建议另外两种解决方案。
STATIC_ASSERT(sizeof(a) == sizeof(double)*3);
x,y,z
变量转换为访问函数并转换为数组的元素。 我的意思是:
#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
// ...
};
如果您也将数组的长度(即表示的向量的维数)作为模板参数,则可以在每个访问函数中放置一个“ STATIC_ASSERT”以确保成员的实际存在。
不,没有保证,不是保证。 例如,如果T是int8_t,则仅在指定1字节打包时才有效 。
最简单,正确的方法是在模板类中添加一个运算符[],例如:
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]; }
如果您真的想做这样的事情:
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.