在 C/C++ 中,联合体(Union)是一种构造数据类型。在一个联合体内,我们可以定义多个不同类型的成员,这些成员将会共享同一块内存空间。老版本的 C++ 为了和C语言保持兼容,对联合体的数据成员的类型进行了很大程度的限制,这些限制在今天看来并没有必要,因此 C++11 取消了这些限制。

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

class Student{

public:

Student(bool g, int a): gender(g), age(a) {}

private:

bool gender;

int age;

};

union T{

Student s; // 含有非POD类型的成员,gcc-5.1.0 版本报错

char name[10];

};

int main(){

return 0;

}

上面的代码中,因为 Student 类带有自定义的构造函数,所以是一个非 POD 类型的,这导致编译器报错。这种规定只是 C++ 为了兼容C语言而制定,然而在长期的编程实践中发现,这种规定是没有必要的。

关于 POD 类型稍后我们会讲解,大家先不要着急。

接下来,我们具体看一下 C++11 对 C++98 的改进。

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

C++98 不允许联合体的成员是非 POD 类型,但是 C++1 1 取消了这种限制。

POD 是 C++ 中一个比较重要的概念,在这里我们做一个简单介绍。POD 是英文 Plain Old Data 的缩写,用来描述一个类型的属性。

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

1) 没有用户自定义的构造函数、析构函数、拷贝构造函数和移动构造函数。

2) 不能包含虚函数和虚基类。

3) 非静态成员必须声明为 public。

4) 类中的第一个非静态成员的类型与其基类不同,例如:

class B1{};

class B2 : B1 { B1 b; };

class B2 的第一个非静态成员 b 是基类类型,所以它不是 POD 类型。

5) 在类或者结构体继承时,满足以下两种情况之一:

派生类中有非静态成员,且只有一个仅包含静态成员的基类;

基类有非静态成员,而派生类没有非静态成员。

我们来看具体的例子:

class B1 { static int n; };

class B2 : B1 { int n1; };

class B3 : B2 { static int n2; };

对于 B2,派生类 B2 中有非静态成员,且只有一个仅包含静态成员的基类 B1,所以它是 POD 类型。对于 B3,基类 B2 有非静态成员,而派生类 B3 没有非静态成员,所以它也是 POD 类型。

6) 所有非静态数据成员均和其基类也符合上述规则(递归定义),也就是说 POD 类型不能包含非 POD 类型的数据。

7) 此外,所有兼容C语言的数据类型都是 POD 类型(struct、union 等不能违背上述规则)。

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

C++11 删除了联合体不允许拥有静态成员的限制。例如:

union U {

static int func() {

int n = 3;

return n;

}

};

需要注意的是,静态成员变量只能在联合体内定义,却不能在联合体外使用,这使得该规则很没用。

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

C++11 规定,如果非受限联合体内有一个非 POD 的成员,而该成员拥有自定义的构造函数,那么这个非受限联合体的默认构造函数将被编译器删除;其他的特殊成员函数,例如默认拷贝构造函数、拷贝赋值操作符以及析构函数等,也将被删除。

这条规则可能导致对象构造失败,请看下面的例子:

#include

using namespace std;

union U {

string s;

int n;

};

int main() {

U u; // 构造失败,因为 U 的构造函数被删除

return 0;

}

在上面的例子中,因为 string 类拥有自定义的构造函数,所以 U 的构造函数被删除;定义 U 的类型变量 u 需要调用默认构造函数,所以 u 也就无法定义成功。

解决上面问题的一般需要用到 placement new(稍后会讲解这个概念),代码如下:

#include

using namespace std;

union U {

string s;

int n;

public:

U() { new(&s) string; }

~U() { s.~string(); }

};

int main() {

U u;

return 0;

}

构造时,采用 placement new 将 s 构造在其地址 &s 上,这里 placement new 的唯一作用只是调用了一下 string 类的构造函数。注意,在析构时还需要调用 string 类的析构函数。

placement new 是什么?

placement new 是 new 关键字的一种进阶用法,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象。相对应地,我们把常见的 new 的用法称为 operator new,它只能在 heap 上生成对象。

placement new 的语法格式如下:

new(address) ClassConstruct(...)

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

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

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

匿名联合体是指不具名的联合体(也即没有名字的联合体),一般定义如下:

union U{

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

};

可以看到,联合体 U 内定义了一个不具名的联合体,该联合体包含一个 int 类型的成员变量,我们称这个联合体为匿名联合体。

同样的,非受限联合体也可以匿名,而当非受限的匿名联合体运用于类的声明时,这样的类被称为“枚举式类”。示例如下:

#include

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];

};

};

int main() {

Singer(true, 13);

Singer(310217);

Singer("J Michael", 9);

return 0;

}

上面的代码中使用了一个匿名非受限联合体,它作为类 Singer 的“变长成员”来使用,这样的变长成员给类的编写带来了更大的灵活性,这是 C++98 标准中无法达到的(编译器会报member 'Student Singer::::s' with constructor not allowed in union错误)。

matlab 联合体,C++11非受限联合体(union)相关推荐

  1. 非受限联合体--non-trivial union

    联合体中可以定义多个不同类型的成员,这些成员变量共享一块内存空间,C++11以前为了与C兼容,对于联合体内的数据类型进行了很多限制,C++11后取消了这些限制 在C++11之前是不允许联合体中包含非P ...

  2. matlab恒定值,基于MATLAB的明渠恒定非均匀渐变流水面线的数值计算

    基于MATLAB的明渠恒定非均匀渐变流水面线 的数值计算 胡岚平 刘华 覃文文 马丽琼 四川大学水利水电学院,成都610065 摘要:明渠恒定非均匀渐变流的微分方程难以求出其解析解,本文针对底宽渐变的 ...

  3. 企业联合体的形式_企业联合体

    企业联合体 企业联合体 企业联合体定义 企业联合体定义: : 属于经济联合体的一种联合体表现形式 (最大经济联合体是欧盟) , 完全区别于 联合公司和战略合作伙伴的联合体, 具体是集团公司或者是交叉参 ...

  4. MATLAB中求矩阵非零元的坐标

    MATLAB中求矩阵非零元的坐标: 方法1: index=find(a); [i,j]=ind2sub(size(a),index); disp([i,j]) 方法2: [i,j]=find(a> ...

  5. 【Matlab图像去噪】改进非局部均值红外图像混合噪声【含源码 1640期】

    一.代码运行视频(哔哩哔哩) [Matlab图像去噪]改进非局部均值红外图像混合噪声[含源码 1640期] 二.matlab版本及参考文献 1 matlab版本 2014a 2 参考文献 [1] 李方 ...

  6. matlab设计菜单教程,MATLAB程序设计教程(11)——MATLAB图形用户界面设计

    MATLAB程序设计教程(11)--MATLAB图形用户界面设计 第11章  MATLAB图形用户界面设计 11.1  菜单设计 11.2  对话框设计 11.3  图形用户界面设计工具 11.1菜单 ...

  7. 怎么用vc采集ni卡数据_利用labview、matlab和c实现非ni接口卡的数据采集及处理

    利用LabVIEW .MATLAB 和C 实现非NI 接口卡的数据采集及处理** 周旭欣1 董兵超1 向明礼2 李拥秋1 袁支润1? (1.四川大学生物力学研究所,成都:610065:2.四川大学化工 ...

  8. MATLAB代码:基于非合作博弈的多微网P2P交易策略研究

    MATLAB代码:基于非合作博弈的多微网P2P交易策略研究 关键词:微网 P2P交易 非合作博弈 交易电价 参考文档:<基于博弈论的多微电网系统交易模式研究>完全复现 仿真平台:MATLA ...

  9. matlab对话框设计,MATLAB程序设计教程(11)——MATLAB图形用户界面设计

    MATLAB程序设计教程(11)--MATLAB图形用户界面设计 第11章  MATLAB图形用户界面设计 11.1  菜单设计 11.2  对话框设计 11.3  图形用户界面设计工具 11.1菜单 ...

最新文章

  1. JS 数组A有数组B的数据就删除
  2. 2019年CV领域,值得一看的综述文章!
  3. 【linux 开发】定时器使用setitimer
  4. WorldWind Java 版学习:1、启动过程
  5. Java程序员应该了解的10个设计原则
  6. oppoR17手机计算机的隐藏功能,OPPO R17 Pro隐藏了很多你不知道的黑科技,让你高效使用手机...
  7. 设置第一个字母字体变大并且所有字母大小写 及下划线
  8. 音视频开发(30)---新一代视频编码标准:HEVC、AVS2和AV1性能对比报告
  9. Linux学习总结(25)——CentOS系统常识
  10. Tap titans乐趣
  11. Vivado初次使用教程
  12. html5使用mescroll
  13. ToLua 入门06_LuaCoroutine
  14. bandizip修改压缩文件内容_BandiZip解压添加压缩文件教程
  15. 根据判断PC浏览器类型和手机屏幕像素自动调用不同CSS
  16. python提供了几个基本的数值运算操作符_慢步学习,二级python,数字类型及其运算...
  17. java 1st 2nd 3rd 4th_1st怎么输入excel “st”在1的右上角 EXCEL excel 1st 2nd 3rd 4th 怎么设置上标 如何改为上标...
  18. LInux查看系统信息命令
  19. ElasticSearch的基本操作第三章
  20. ElasticSearch的_cat命令说明和其JavaAPI

热门文章

  1. 未能初始化social club错误代码0_智能电表错误代码大全 报警灯常亮原因
  2. asp.net捕获全局未处理异常的几种方法
  3. 《Spring攻略(第2版)》——1.5 指定Bean引用
  4. javaWeb RSA加密使用
  5. java base64编码的三种方式
  6. Xcode插件管理工具Alcatraz
  7. Quartz调用大全
  8. 蓄水池抽样(Reservoir Sampling)
  9. PHP自学--第一天
  10. Spring 创建对象的方式