设为首页 加入收藏

TOP

8.1.2 使用struct
2013-10-07 15:58:21 来源: 作者: 【 】 浏览:51
Tags:8.1.2 使用 struct

8.1.2 使用struct

C++(www.cppentry.com)环境中,我们把C风格的struct叫做POD(Plain Old Data)对象,从字面上你也可以知道它仅包含一些数据成员,这些数据成员可以是基本数据类型变量、任何类型的指针或引用、任何类型的数组及其他构造类型的对象等,见示例8-2。

示例8-2

  1. struct Student   
  2. {  
  3. unsigned long ID ;  
  4. char firstName[15] ;  
  5. char lastName[15] ;  
  6. char email[20] ;   
  7. };  

【提示8-2】:

虽然把数组当作参数传递给函数的时候,数组将自动转换为指针,但是包装在struct/class中的数组,其内存空间则完全属于该struct/class的对象所有。如果把struct/class当作参数传递给函数时,默认为值传递,其中的数组将全部复制到函数堆栈中。例如:

  1. void func (Student s)   
  2. {  
  3.      cout << sizeof (s) << endl ;    // 56  
  4. }  
  5. Student  s0 ;  
  6. func (s0) ;  

因此,当你的UDT/ADT中包含数组成员的时候,最好使用指针或引用传递该类型的对象,并且一定要防止数组元素越界,否则它会覆盖后面的结构成员。

任何POD对象的初始化都可以使用memset()函数或者其他类似的内存初始化函数。假设s是Student的一个对象,用memset()初始化s的方法如下:

  1. memset (&s, 0x00, sizeof (Student)) ; 

C风格的构造类型对象也可以在定义的时候指定初始值。我们可以仅指定第一个成员的初值来初始化POD对象,后面的成员将全部自动初始化为0,就像数组的初始化一样。例如:
  1. Student s = { 0 }; 

结构可以嵌套定义,也就是在一个结构的定义体内定义另一个结构,见示例8-3。

示例8-3

  1. struct Student   
  2. {  
  3. struct _Name   
  4. {  
  5. char firstName[15] ;  
  6. char lastName[15] ;  
  7. };  
  8. unsigned long ID ;      
  9. _Name name ;            
  10. char email[20] ;  
  11. };  

【提示8-3】: 构造类型虽然可以嵌套定义,但是对于嵌套定义的类型,其对象不一定存在包含关系,存在包含关系的对象类型也不一定是嵌套定义的。例如,上例中的_Name类型完全可以挪到Student定义的外面某处,而它们的对象之间的包含关系不会改变。当一个类型A只会在另一个类型B中被使用的时候,就可以把A定义在B的定义体内,这样可以减少暴露在外面的用户自定义类型的个数。

所谓对象之间的包含是指一个类型的对象充当了另一个类型定义的数据成员,从而也就充当了它的对象的成员,即两个对象之间存在has-a关系。但是要注意:一个对象不能自包含,无论是直接的还是间接的,因为编译器无法为它计算sizeof值,也就不知道该给这样的对象分配多少存储空间,见示例8-4。

示例8-4

struct  A < xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

{

int  i ;

B   b ;

};

struct  B

{

char  ch ;

A    a ;

};

假设A定义在B的前面,于是计算A的大小就需要知道B的大小,而计算B的大小又需要A的大小,……,于是陷入了"鸡生蛋还是蛋生鸡"的怪圈!这样的代码在编译的时候肯定通不过。

虽然对象不能自包含,但可以自引用,而且两个类型可以交叉引用,这种关系称为holds-a关系。因为任何类型的指针的大小都一样,给指针分配存储空间的时候不需要知道它指向的对象的类型细节,见示例8-5。

示例8-5

struct  A

{

int   count ;

char *pName;         // A holds-a string

B   *pb ;          // A holds-a B

};

struct  B

{

char  ch ;

A    *pa;         // B holds-a A

B    *pNext ;      // B自引用

};

上面的两个结构可以组成一个链表,A是链表头的类型,B是链表节点的类型。通过链表头节点可以遍历整个链表,每个链表节点还可以指向另一个链表,……,这样就形成了一个庞大的链式结构。

利用对象之间的引用关系,我们就可以实现链表、树、队列等复杂的数据结构,或者实现一些复杂的对象管理,比如对象之间的索引和定位。

【提示8-4】: C++(www.cppentry.com)和C都支持相同类型对象之间的直接赋值操作(默认的"operator=语义",就是对象按成员拷贝语义),但是不能直接比较大小和判断是否相等。

这是因为,相同类型对象的各数据成员在内存中的布局是一致的,编译器执行默认的位拷贝也是符合赋值操作语义的。而出于对齐(将大小调整到机器字的整数倍)的考虑,每个对象的存储空间中可能会存在填补字节,这些字节单元不会初始化而是具有上次使用留下的"脏值"(随机值)。显然每个对象填补字节的内容是不会相同的。这就是说,如果编译器支持使用逐位比较的默认方法来比较同类型对象,结果肯定是不对的,而有意义的大小关系是与具体应用相关的,显然编译器并不对应用领域的东西做任何假设。例如:

  1. Student  a,  b;  
  2. cout << ((a.ID > b.ID)   "a larger than b" : "a less than b") << endl

所以,当默认的赋值语义不能满足我们的要求的时候,就需要定义自己的赋值语义。在C语言中只有定义一些函数来完成这样的功能,而C++(www.cppentry.com)则提供了运算符重载机制可以解决赋值和比较等问题。(本质上仍然是函数调用,只是形式不同而已!)
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇8.1.1 关键字struct与class的困惑 下一篇PYPL: 2012美国年度语言

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·MySQL 安装及连接-腾 (2025-12-25 06:20:28)
·MySQL的下载、安装、 (2025-12-25 06:20:26)
·MySQL 中文网:探索 (2025-12-25 06:20:23)
·Shell脚本:Linux Sh (2025-12-25 05:50:11)
·VMware虚拟机安装Lin (2025-12-25 05:50:08)