深入了解C++ 结构体(struct)与共用体(union)

所属分类: 软件编程 / C 语言 阅读数: 140
收藏 0 赞 0 分享

编码运行环境:VS2017+Win32+Debug,Win32表示生成32bits的应用程序。

结构体(struct)与共用体(union)是C语言中就已经存在的数据类型,C++对他们进行了扩充,最大的变化是允许在结构和公用体中定义成员函数。下面将通过实例讲解二者的特性和用法。

1.struct

以下是一个使用了结构体的C++程序。

#include <iostream>
using namespace std;

struct Room
{
		int floor;
		int No;
};

struct Student
{
	int age;
	int score;
	Student(int a,int s){
		age=a;
		score=s;
	}
};

int main(int argc,char* argv[])
{

	Room r[3]={{1,101},{2,201},{3,301}};
	Student s(18,89);
	cout<<"the room are:";
	cout<<r[0].floor<<"-"<<r[0].No<<" ";
	cout<<r[1].floor<<"-"<<r[1].No<<" ";
	cout<<r[2].floor<<"-"<<r[2].No<<endl;
	cout<<"the student's age:"<<s.age<<" score:"<<s.score<<endl;
	getchar();
}

程序运行结果:

the room are:1-101 2-201 3-301
the student's age:18 score:89

阅读以上程序,在C++中使用结构体需要注意以下几点:

 (1)C++中,结构体是一种真正的数据类型,在利用结构定义变量时,不需要像在C中带上struct关键字,或先使用typedef struct structname structalias的方式进行申明。

(2)C++对C中的struct进行了扩充,允许在struct中定义成员函数。struct中的成员变量和成员函数也有访问权限,在class中,默认的访问权限是private,而在struct中默认访问权限是public,这是结构体和类的唯一区别。struct成员的默认访问权限设为public是C++保持与C语言兼容而采取的一项策略。

(3)如果struct中没有显示定义任何构造函数,那么结构变量可以像在C语言中那样用花括号顺序指明数据成员的值来进行初始化。但是一旦显示定义了任何一个构造函数,就不能用这种方式初始化了。如果在class中只有若干public型的数据成员,而没有显示定义任何构造函数,也可以使用花括号进行初始化。

(4)用sizeof运算符计算结构的大小时,要考虑结构体内部变量的对齐问题。

2.union

共用体(union),又名联合体,是一种特殊的类,从C语言章继承而来,其基本语义没有发生什么变化,只是具有了类的一些特性(允许定义成员函数)。在实际的编程实践中,使用频率没有struct高。与struct相比,最显著的区别是union的数据成员共享同一段内存,以达到节省空间的目的。

2.1union的基本性质

通过如下程序考察union变量的占用空间,成员赋值时的相互影响。

#include <iostream>
using namespace std;
union testunion
{
	char c;
	int i;
};

int main(int argc,char* argv[])
{
	cout<<sizeof(testunion)<<endl;
	testunion* pt=new testunion;
	char* p=reinterpret_cast<char*>(pt);
	for(int i=0;i<sizeof(*pt);i++)
		cout<<int(p[i])<<" ";
	cout<<endl;
	cout<<pt->i<<endl;
	pt->c='A';
	cout<<pt->c<<endl;
	for(int i=0;i<sizeof(*pt);i++)
		cout<<int(p[i])<<" ";
	cout<<endl;
	cout<<pt->i<<endl;
	delete pt;
}

程序运行结果:

4
-51 -51 -51 -51
-842150451
A
65 -51 -51 -51
-842150591

可以看出,union testunion变量的体积是4,它是由两个数据成员中体积较大的一个(int)类型来决定的。对其中一个数据成员的修改,一定会同时改变所有其他数据成员的值。不过对体积较小的数据成员的修改,只会影响到该成员应该占用的那些字节,对超出部分(高位字节)没有什么影响。

2.2union的高级特性

观察如下程序。

#include <iostream>
using namespace std;
struct Student
{
	int age;
	int score;
	Student(int a,int s)
	{
		age=a;
		score=s;
	}
};

union testunion
{
	char c;
	int i;
};

class someClass
{
	int num;
public:
	void show(){cout<<num<<endl;}
};

union A
{
	char c;
	int i;
	double d;
	someClass s;
};

union B
{
	char c;
	int i;
	double d;
	B(){d=8.9;}
};

union
{
	char c;
	int i;
	double d;
	void show(){cout<<c<<endl;}
}u={'U'};

int main(int argc,char* argv[])
{
	A a={'A'};
	B b;
	cout<<a.c<<endl;
	cout<<b.d<<endl;
	a.s.show();
	u.show();
	
	//匿名共用体
	union
	{
		int p;
		int q;
	};

	p=3;
	cout<<q<<endl;
}

程序运行结果:

A
8.9
65
U
3

阅读以上程序,需要注意以下几点:

 (1)union可以指定成员的访问权限,默认情况下,与struct具有一样的权限(public)。

 (2)union也可以定义成员函数,包括构造函数和析构函数。与struct不同的是,它不能作为基类被继承。

 (3)union不能拥有静态数据成员或引用成员,因为静态数据成员实际上并不是共用体的数据成员,它无法和共用体的其它数据成员共享空间。对于引用变量,引用本质上是一个指针常量,它的值一旦初始化就不允许修改。如果共用体有引用成员,那么共用体对象一创建初始化后就无法修改,只能作为一个普通的引用使用,这就失去了共用体存在的意义。

 (4)union允许其他类的对象成为自己的数据成员,但是要求该类对象所属类不能定义constructor,copy constructor,destructor,assignment operator,virtual function中的任意一个。因为:
  (4.1)union数据成员共享内存,union构造函数在执行的时候,不能调用数据成员为类对象的构造函数,否则就改变了其他数据成员的值。
  (4.2)同样,union的对象成员的析构函数也不能被调用,因为其他数据成员的值对于对象成员而言可能毫无意义。
  (4.3)union的对象成员的赋值应该维持其原始语义,不建议进行赋值运算符的重载,因为赋值运算符重载一般用于“深拷贝”等场合,而在对象空间与其它变量共享的情况下,“深拷贝”引入的内存资源,指向内存资源的指针往往会被其它共用体数据成员修改,导致内存资源无法寻址,造成内存泄漏。此外,因为union的对象成员没有自定义的析构函数,也会导致内存泄漏。
  (4.4)拥有虚函数的类对象,虚函数表指针可能会在共用体对象初始化时被覆盖,导致无法寻址虚函数表,所以也不能拥有虚函数。

 (5)如果union类型旨在定义该类的同时使用一次,以后不再使用了,那么也可以不给出union的名称。如上例中变量u就是这种情况,这种情况下,无法为该union定义构造函数。

 (6)匿名共用体(Anonymous Union),也就是给出一个不带名称的共用体的申明后,并不定义任何该union的变量,而是直接以分号结尾。严格来说,匿名共用体并不是一种数据结构,因为它不能用来定义共用体对象,它只是指明若干个变量共享一片内存单元。在上例中,对变量p的修改实际上修改了变量q。可以看出,尽管匿名共用体中的变量被定义在同一个共用体中,他们与同一个程序块的任何其他局部变量具有相同的作用域级别。这意味着匿名共用体内的成员的名称不能与同一个作用域内的其它标识符相冲突。另外,对匿名共用体还存在如下限制:
   (6.1)匿名共用体不允许有成员函数;
   (6.2)匿名共用体也不能包含私有或者保护成员;
   (6.3)全局匿名共用体中的成员必须是全局或静态变量。

以上就是深入了解C++ 结构体(struct)与共用体(union)的详细内容,更多关于C++ 结构体与共用体的资料请关注脚本之家其它相关文章!

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多