C++11:(十)C++11非受限联合体(union)

在 C/C++ 中,联合体(Union)是一种构造数据类型。

在一个联合体内,我们可以定义多个不同类型的成员,这些成员将会共享同一块内存空间。

老版本的 C++ 为了和C语言保持兼容,对联合体的 数据成员的类型 进行了很大程度的限制,联合体成员必须是POD类型,因此 C++11 取消了这些限制。

1、C++11 允许非 POD 类型

POD 类型一般具有以下几种特征(包括 class、union 和 struct等):

    1. 没有用户自定义的构造函数、析构函数、拷贝构造函数和移动构造函数。
    1. 不能包含虚函数和虚基类。
    1. 非静态成员必须声明为 public。
    1. 类中的第一个非静态成员的类型与其基类不同,例如:
    class B1{};
    class B2:B1{B1 b;}; //class B2 的第一个非静态成员 b 是基类类型,所以它不是 POD 类型。
    
    1. 在类或者结构体继承时,满足以下两种情况之一:
    • 派生类中有非静态成员,且只有一个仅包含静态成员的基类;
    • 基类有非静态成员,而派生类没有非静态成员。
    1. 所有非静态数据成员均和其基类也符合上述规则(递归定义),也就是说 POD 类型不能包含非 POD 类型的数据。
  • 此外,所有兼容C语言的数据类型都是 POD 类型(struct、union 等不能违背上述规则)。

C++11 标准规定,任何非引用类型都可以成为联合体的数据成员,这种联合体也被称为非受限联合体

2. C++11 允许联合体有静态成员

union U {
    static int func() {
        int n = 3;
        return n;
    }
}; // 需要注意的是,静态成员变量只能在联合体内定义,却不能在联合体外使用,这使得该规则很没用。

非受限联合体的赋值注意事项

如果非受限联合体内有一个非 POD 的成员,而该成员拥有自定义的构造函数,那么这个非受限联合体的 默认构造函数将被编译器删除

#include <string>
using namespace std;
union U {
    string s; //string类有自定义的构造函数,所以U的构造函数会被删除。  则 U u; 无法定义成功
    int n;
};
int main() {
    U u;   // 构造失败,因为 U 的构造函数被删除
    return 0;
}

解决方案:使用placement new显式调用string类的构造函数。

#include <string>
using namespace std;
union U {
    string s;
    int n;
public:
    U() { new(&s) string; }  //采用 placement new 将 s 构造在其地址 &s 上
    ~U() { s.~string(); } //在析构时还需要调用 string 类的析构函数
};
int main() {
    U u;
    return 0;
}

placement new是什么?

  • placement new 是 new 关键字的一种进阶用法,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象。

  • 相对应地,我们把常见的 new 的用法称为 operator new,它只能在 heap 上生成对象。

语法格式如下:

new(address) ClassConstruct(...) ; 
// address : 表示已有的内存地址,该地址可以在栈上,也可以在堆上
// ClassConstruct(...) : 表示调用类的构造函数,如果构造函数没有参数,也可以省略括号。

placement new 利用已经申请好的内存来生成对象,它不再为对象分配新的内存,而是将对象数据放在 address 指定的内存中。在本例中,placement new 使用的是 s 的内存空间。

非受限联合体的匿名声明和“枚举式类”

什么是匿名联合体?

union U{
    union{int x; } //此联合体为匿名联合体
}

当非受限的匿名联合体运用于类的声明时,这样的类被称为“枚举式类”。

#include<cstring>
using namespace std;

class Student{
public:
    Student(bool g, int a): gender(g), age(a){}
    bool gender;
    int age;
};
class Singer { // 枚举式类
public:
    enum Type { STUDENT, NATIVE, FOREIGENR };
    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 = FOREIGENR;
    }
    ~Singer(){}
private:
    Type t;
    union { //匿名联合体
        Student s;
        int id;
        char name[10];
    };
};

  • 匿名非受限联合体作为类 Singer 的“变长成员”来使用,这样的变长成员给类的编写带来了更大的灵活性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值