C++的类成员对齐

        这是个小语法点,之前我们的对齐方式都是使用#pragma pack,这个方式实际是依赖编译器,且粒度粗(如果#pragma pack(1)之后没有#pragma pack(),那就作用整个进程了)。在C++11之后引入关键字alignas,以此来实现对齐更加便利,代码如下:

#pragma pack(1)
	struct TMeterDecrease{
		BYTE bFlag;
		DWORD dwEnergyBefor;
		DWORD dwEnergyAfter;
		DWORD dwEnergyCurrent;
		BYTE bSocCurrent;
	}tMeterDec;
#pragma pack()

//后续对于类对齐的方式,请大家修改为aligns,如上修改为:
struct alignas(1) TMeterDecrease{
	BYTE bFlag;
	DWORD dwEnergyBefor;
	DWORD dwEnergyAfter;
	DWORD dwEnergyCurrent;
	BYTE bSocCurrent;
}tMeterDec;

//当然,也可以对单个成员对齐
struct TMeterDecrease{
	alignas(1) BYTE bFlag;
	alignas(4) DWORD dwEnergyBefor;
	DWORD dwEnergyAfter;
	DWORD dwEnergyCurrent;
	BYTE bSocCurrent;
}tMeterDec;

alignas和#pragma pack都可以用来控制数据结构的对齐方式,但它们有一些重要的差异:

1:语法:

   `alignas`是C++11引入的关键字,用于在源代码中显式指定对齐方式。它是一种类型特性,可以应用于变量、数据成员、数组元素等。
   `#pragma pack`是一个编译器指令,通常在源代码中使用`#pragma`指令来设置对齐方式。它通常在头文件中使用,并且可以在一段代码中设置,影响此指令之后的数据结构的对齐方式。

2. 跨平台:
   alignas是C++标准的一部分,因此在标准兼容的C++编译器上应该是可移植的。
   #pragma pack是编译器特定的指令,不是标准C++的一部分,因此在不同编译器上的行为可能会有所不同。

3. 粒度:
   alignas可以应用于单个变量、数据成员或数组元素,因此你可以精确地控制每个数据元素的对齐方式。
   #pragma pack通常应用于一段代码块,它会影响此指令之后的所有数据结构,因此它的粒度较粗。

总之,如果你需要在C++中精确地控制数据元素的对齐方式,并且希望具有较好的跨平台性,那么`alignas`通常是更好的选择。
但如果你需要在特定编译器上进行精细的控制,或者你在使用C语言编写代码,那么`#pragma pack`可能更适合。

后续有发现了一个需要特殊说明的情况:

针对如上的对齐,有一种情况是例外的,测试代码如下:

#include 
#include 
#include 
#include 

using namespace std;

class CPoint{
public:
    int x;
    int y;
};

struct Point{
    int x;
    int y;
    int z;
};

 union PointU{
     CPoint cp;
     Point pt;
 };

using PointX= std::variant ;

void process(void* p)
{
    PointX x1;
    PointX x2=x1;
    x1=x2;
}


class Rectangle1
{
    Point leftUp;//值语义
    int width;
    int height;
};

class Rectangle3
{
    Point& leftUp;//引用
    int width;
    int height;
};

class alignas(4) shape{
	Rectangle1 r1;	//20---24
	Rectangle3 r2;	//16
	int *p;			//8
};

struct alignas(8) shape8{
	Rectangle1 r1;	//12
	Rectangle3 r2;	//16
	int *p;			//8
};

int main(){

    Rectangle1 r1;
    Rectangle2 r2(10,20,100,200);

    cout<<"Rectangle1 " << sizeof(Rectangle1)<

如上代码执行结果如下:

Rectangle1 20
Rectangle3 16
PointU 12
PointX 16
shape 48
shape8 48

如上,即使按照4byte对齐了,shape的大小就是48,按照shape成员,应该是20+16+8=44,而事实却是即使按照4字节对齐了,大小还是48字节,对比Rectangle1,point是值语义,所以大小就是按照4byte对齐,大小为20,而如果是引用语义,实际就是按照8字节对齐的,alignas无意义。

为了对比分析,可以增加一个值语义:

#include 
#include 
#include 
#include 

using namespace std;

class CPoint{
public:
    int x;
    int y;
};

struct Point{
    int x;
    int y;
    int z;
};

class Rectangle1
{
    Point leftUp;//值语义
    int width;
    int height;
};

class Rectangle3
{
    Point& leftUp;//引用
    int width;
    int height;
};

class alignas(4) shape{
	Rectangle1 r1;	//20---24
	Rectangle3 r2;	//16
	int *p;			//8
};

struct alignas(8) shape8{
	Rectangle1 r1;	//12
	Rectangle3 r2;	//16
	int *p;			//8
};

class Rectangle4
{
    int width;
    int height;
	int length;
};

struct shape9{
	Rectangle1 r1;	//20
	Rectangle4 r4;	//12
};

struct shape10{
	Rectangle1 r1;	//20
	Rectangle4 r4;	//12
	int *p;			//8
};

int main(){
    cout<<"Rectangle1 " << sizeof(Rectangle1)<

如上代码,输出结果:

Rectangle1 20
Rectangle3 16
Rectangle4 12
shape 48
shape8 48
shape9 32
shape10 40

如上看到,如果类中带了指针或引用,那在这个指针或引用之前比如需要按照8byte对齐,无论是否设置alignas,而如果是值语义,如shape9,对齐就是按照字节对齐(默认4byte,也可以alignas设置)。

你可能感兴趣的:(c++,开发语言)