跳转至

67 C++的联合体

1. 联合体

Union (联合体)有点像 class 类型或者 struct 类型,只不过它一次只能占用一个成员的内存。 通常如果我们有一个结构体,我们在里面声明 4 个浮点数,就可以有 4x4 个字节在这个结构体中,总共是 16 个字节。 但一个联合体只能有一个成员,所以如果我要声明 4 个浮点数,比如 abcd,联合体的大小仍然是 4 个字节,当我尝试去处理它们,比如将 a 设为 5,它们的内存是一样的,d 的值也会是 5,这就是联合体的工作方式。

你可以像使用结构体或类一样使用它们,也可以给它添加静态函数或者普通函数、方法等。然而你不能使用虚方法,还有一些其它限制,但通常人们用联合体来做的事,是和类型双关紧密相关的。当你想给同一个变量取两个不同的名字时,它真的很好用。

通常union是匿名使用的,但匿名 union 不能含有成员函数。

1. 使用案例

我们这里有两种不同的方法来处理相同的内存:

a. 解引用指针

struct Union
{
    union
    {
        float a;
        int b;
    };
};

Union u;
u.a = 2.0f;
std::cout << u.a << "," << u.b << std::endl;

得到 2 和一串数,其实这个 107...是浮点数形式的 2 的字节表示,就好像我们取了组成浮点数的内存,然后把它解释成一个整型,这样就是类型双关了。

看一个更有用的例子:

struct Vector2
{
    float x, y;
};

struct Vector4
{
    float x, y, z, w;
};

void PrintVector(const Vector2& vector)
{
    std::cout << vector.x << "," << vector.y << std::endl;
}

可以发现 Vector4 实际上是两个 Vector2,我们可以把 x 的内存地址转换为 Vector2 再解引用:

struct Vector4
{
    float x, y, z, w;

    Vector2& GetA()
    {
        return *(Vector2*)&x;
    }
};

int main()
{
    Vector4 e={1,2,3,4};
    std::cout << e.GetA().y << std::endl;  // 2
    std::cin.get();
}

b. union

还有另外一种方法,就是使用union

struct Vector4
{
    union
    {
        struct
        {
            float x, y, z, w;
        };
    };
};
// 这里还可以正常访问Vector4.x,因为我们没有给结构体起名,它是匿名的,只是一种数据结构

再加入一个结构体成员:

struct Vector4
{
    union
    {
        struct
        {
            float x, y, z, w;
        };

        struct
        {
            Vector2 a, b;
        };
    };
};

这里 a 和 x,y 的内存是一样的,b 和 z,w 的内存相同,这里有两种方法可以读取:

int main()
{
    Vector4 vector = { 1.0f,2.0f,3.0f,4.0f };
    PrintVector(vector.a);

    PrintVector(vector.b);
    vector.z = 500.f;
    PrintVector(vector.b);

    std::cin.get();
}

这里并没有设置 b.x=500,而是设置的 vecor.z,这个 z 变量对应于 b.x,因为它占用了相同的内存,所以 z 对应 Vector2 的 x。

3. 总结

当你想做这样的事情时,union真的很有用:当你想用多种方法来处理相同的数据时。 你也可以用类型双关或者其它方法,但是通常union的可读性更强。