非受限联合体 介绍与用法

联合体是一种在C/C++中的构造类型的数据结构,联合体内可定义多种不同数据类型,它们会共享内存空间。
在C++98中,非POD类型成员的联合体无法通过编译,例如:

struct Student{ 
    //定义了构造函数,student成为非pod类型
    Student(bool g, int a): gender(g), age(a){} 
    bool gender; 
    int age; 
}; 
union T 
{ 
    Student s; // 编译失败,不是一个POD类型 
    int id; 
    char name[10];
};
 // 编译选项:g++ -std=c++98 3-7-1.cpp

不仅包含非pod类型的联合体无法通过编译,C++98标准也不允许联合体拥有静态或引用类型的成员。本意是为了与C尽可能兼容,但使联合体的使用受限。

非受限联合体:
实践证明C++98对联合体的限制没有什么意义,而且随C++发展,有些东西已经和C发生了本质上的区别,没必要再为C兼容联合体,因此新标准(C++11)规定,任何非引用类型(也就是不限制非POD和静态)都可成为联合体成员,这样的联合体就叫非受限联合体

C++98规定联合体自动对未在初始化成员列表中出现的成员赋默认初值,但由于联合体同一时间只有一个成员生效,那么初始化联合体时到底是初始化了第一个成员还是所有成员?
例子:

union T
{ 
    int x; 
    double d; 
    char b[sizeof(double)]; 
}; 

T t = {0}; // 到底是初始化第一个成员还是所有成员呢? 
// 编译选项:g++ -std=c++98-c 3-7-3.cpp

int是第一个成员,实际上C++98在对t赋值时,是将联合体大小的所有空间(即并非只对Int的4个字节赋值为0,而是整个结构体的8字节)都设置成了0,实际上我们并不都希望它这么干,也不理解它为什么要这么干,因此C++新标准中会删除一些非受限联合体的默认构造函数,减少这种情况的发生。比如当一个非受限联合体存在一个非Pod类型成员,该成员拥有一个非平凡的构造函数,那么这个非受限联合体的默认构造函数将被删除。类似地,默认拷贝构造函数、拷贝赋值操作符、以及析构函数等,也都遵循此规则
举例 :

#include <string> 
using namespace std; 
union T 
{ 
    string s; // 满足string是非POD类型并且string有非平凡的构造函数 ,T的默认构造函数被删除
    int n; 
}; 
int main() 
{ 
    T t; // 构造失败,因为T的构造函数被删除了 
} 
// 编译选项:g++ -std=c++11 3-7-4.cpp

出现上面这种情况时,需要手动给非受限联合体定义默认构造函数(其它特殊函数规则类似)
像这样:

#include <string> 
using namespace std; 
union T 
{ 
    string s; 
    int n; 
public: 
    // 自定义构造函数和析构函数 
    T()
    { 
        new (&s) string; 
    } 
    ~T() 
    { 
        s.~string(); 
    } 
}; 

int main() 
{ 
    T t; // 构造析构成功 
} 
// 编译选项:g++ -std=c++11 3-7-5.cpp

上面代码构造T时,采用placement new将s构造在其地址&s

[关于placement new 可参阅这篇文献:https://www.cnblogs.com/xzlq/p/9504851.html]

,这里placement new的唯一作用只是调用了一下string的构造函数;在析构时,Union T也必须是一个string对象,否则可能导致析构错误

匿名非受限联合体可以运用于类的声明中,这样的类也被称为**“枚举式的类”**(union-like class)
举例:

#include <cstring> 
using namespace std;

//有自己的构造,非POD
struct Student
{ 
    Student(bool g, int a): gender(g), age(a){} 
    bool gender; 
    int age; 
}; 

class Singer 
{ 
public: 
    enum Type 
    {
        STUDENT, 
        NATIVE, 
        FOREIGNER
    }; 

    Singer(bool g, int a): s(g, a) 
    { 
        t = STUDENT; 
    } 

    Singer(int i): id(i) 
    { 
        t = NATIVE; 
    } 

    Singer(const char* n, int s) 
    { 
        int size = (s > 9) ? 9 : s; 
        memcpy(name, n, size); 
        name[s] = '\0'; 
        t = FOREIGNER; 
    } 

~Singer() {} 

private: 
    Type t; 
    union 
    { 
        // 匿名的非受限联合体 
        Student s; 
        int id; 
        char name[10]; 
    }; 
}; 

int main()
{ 
    Singer(true, 13); 
    Singer(310217); 
    Singer("J Michael", 9); 
} 
// 编译选项:g++ -std=c++11 3-7-6.cpp

上面的匿名非受限联合体称为类Singer的“变长成员”(variant member)。变长成员给类的编写带来了更大的灵活性,这是C++98标准中无法达到的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值