菜鸟资源网站全面焕新升级啦!全新的界面设计,简洁而不失雅致,让您一目了然,轻松上手。我们还对资源分类进行了细致梳理,确保各类资源条理清晰,便于您按需筛选与查找。 立即查看

第十一章:为何struct x1{struct x1 stX};无法通过编译,了解C++基本语言特性变量和类型

第十一章:为何struct x1{struct x1 stX};无法通过编译,了解C++基本语言特性变量和类型

  大家经常会碰到这样的问题:定义一个数据类型,而此数据类型又包含此数据类型的变量。通常做法应该是把数据类型中的变量定义为指针形式。例如定义一个二叉树的节点:   上述二叉树节点定义是正确的,可以编译通过。但如果定义为下述对象形式,则无法编...

当前版本

软件大小

软件语言

是否破解

  大家经常会碰到这样的问题:定义一个数据类型,而此数据类型又包含此数据类型的变量。通常做法应该是把数据类型中的变量定义为指针形式。例如定义一个二叉树的节点:

struct tagNode                     //二叉树节点
{
  ElemType data;                 //节点数据
  struct tagNode *pLeftNode;     //节点左孩子
  struct tagNode *pRightNode;    //节点右孩子
}

  上述二叉树节点定义是正确的,可以编译通过。但如果定义为下述对象形式,则无法编译通过:

struct tagNode                      //二叉树节点
{
  ElemType data;                  //节点数据
  struct tagNode LeftNode;        //节点左孩子
  struct tagNode RightNode;       //节点右孩子
} 

  然而上述对象形式的二叉树节点为何不能编译通过,而采用指针形式的二叉树节点可以编译通过呢?
  答案是 C/C++采用静态编译模型。程序运行时,结构大小会在编译后确定。程序编LeftNode.LeftNode.LeftNode..-.这类错误也称为类和结构的递归定义错误。要正确编译,编译器必须知道一个结构所占用的空间大小。除此之外还有一个逻辑方面的问题,在这种情况下,想想可以有LeftNode.LeftNode.LeftNode.LeftNode.
  如果采用指针形式,指针的大小与机器的字长有关,不管是什么类型,编译后指针的大小总是确定的,所以这种情况下不需要知道结构 struct tagNode 的确切定义。例如,在32 位字长的 CPU 中,指针的长度为 4 字节。所以,如果采用指针的形式,structtagNode的大小在其编译后即可确定,为sizeof(int)+4+4.对于对象形式的 struct tagNode定义,其长度在编译后是无法确定的。
  注意:
  (1) 在Visual studio 2010编译器中,错误C2460代表这种递归定义错误,编译提示:”identifier1″:使用正在定义的”identifier2″,将类或结构(identifier2)声明为其本身的成员(identifier1),不允许类和结构的递归定义。
  (2) 在编程过程中,禁止类和结构的递归定义,以防产生奇怪的编译错误。现在,我们继续讨论类递归定义的经典案例一两个类相互包含引用问题。这是所有 C++程序员都会碰到的经典递归定义案例。
案例:CA 类包含 CB类的实例,而 CB类也包含 CA 类的实例。代码实现如下:

//A.h实现CA类的定义
#include"B.h"
class CA
{
Public:
  intiData;        //定义int数据iData
  CB instanceB:    //定义CB 类实例instanceB
}
//B.h实现CB类的定义
#include"A.h"
class CB
{
public:
  int iData;      //定义int数据iData
  CAinstanceA;    //定义CA 类实例instanceA
}
int main()
{
  CAinstanceA;
  return 0;
}

  上述代码在VisualC 2010上无法编译通过,编译器会报出 C2460 错误。先分析一下代码存在的问题:

  ● CA类定义时使用CB类的定义,CB类定义时使用CA类的定义,递归定义。

  ● A.h包含了B.h,B.h 包含了A.h,也存在递归包含的问题。

  其实,无论是结构体的递归定义,还是类的递归定义,最后都归于一个问题C/C++采用静态编译模型。在程序运行时,结构或类大小都会在编译后确定。程序要正确编译,编译器必须知道一个结构或结构所占用的空间大小,否则编译器就会报出奇怪的编译错误,如C2460错误。

  最后,我们给出类或结构体递归定义的几个经典解法。这里以类为例,因为在C++中 struct也是class,class举例具有通用性。

  1.前向声明实现

//A.h实现CA类的定义

class CB;//前向声明CB类
class CA
{
public:
  Int iData;        //定义int数据iData
  CB*pinstanceB;  //定义 CB类的指针 pinstanceB
}

//B.h实现CB类的定义
#include "A.h"
class CB
{
public:
  Int iData;      //定义int数据iData
  CAinstanceA;  //定义 CA类实例instanceA
}
#include"B.h"
int main()
{
  CA instanceA;
  return 0;
}

  前向声明实现方式的主要实现原则:

  ●主函数只需要包含 B.h就可以,因为 B.h 中包含了A.h。

  ●A.h 中不需要包含 B.h,但要声明 class CB,在避免死循环的同时也成功引用了CB。

  ●包含class CB 声明,而没有包含头文件 B.h,这样只能声明 CB类型的指针,而不能实例化。

  2.friend声明实现

//A.h实现CA类的定义

class CA
{
public:
  friend class CB;    /友元类声明
  int iData;         //定义int数据iData
  CB *pinstanceB;  //定义CB类的指针 pinstanceB
}
//B.h实现CB类的定义
#include "A.h"
class CB
{
public:
  int iData;        //定义int数据iData
  CAinstanceA;    //定义 CA类实例 instanceA
}

#include"B.h"
int main()
{
  CA instanceA;
  return 0:
}

  friend友元声明实现说明:

  ● 主函数只需要包含B.h就可以,因为B.h中包含了A.h。

  ● A.h 中不需要包含B.h,但要声明class CB,在避免死循环的同时也成功引用了CB。

  ● class CA 包含class CB友元声明,而没有包含头文件 B.h,这样只能声明 CB类型的指针,而不能实例化。

  不论是前向声明实现还是friend友元实现,有一点是肯定的:即最多只能有一个类可以定义实例。同样头文件包含也是一件很麻烦的事情,再加上头文件中常常出现的宏定义,感觉各种宏定义的展开是非常耗时间的。类或结构体递归定义实现应遵循两个原则:

  (1) 如果可以不包含头文件,那就不要包含了。这时候前置声明可以解决问题。如果使用的仅仅是一个类的指针,没有使用这个类的具体对象(非指针),也没有访问到类的具体成员,那么前置声明就可以了。因为指针这一数据类型的大小是特定的,编译器可以获知。

  (2) 尽量在CPP文件中包含头文件,而非在头文件中。假设类A的一个成员是一个指向类B的指针,在类A的头文件中使用了类B的前置声明并编译成功,那么在A的实现中需要访问B的具体成员,因此需要包含头文件,那么我们应该在类A的实现部分(CPP文件)包含类 B的头文件而非声明部分。

  请谨记

  ● 类和结构体定义中禁止递归定义,以防产生奇怪的编译错误。如果两个类相互递归定义时,需考虑前向声明或friend友元实现。但无论通过何种方法实现,有一点是肯定的:即最多只能有一个类可以定义实例。

第十一章:为何struct x1{struct x1 stX};无法通过编译,了解C++基本语言特性变量和类型
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

常见问题

  • 如何安装和激活?

    每款软件会附带有安装教程,您打开安装包一目了然

  • 程序是否已损坏?文件损坏?还是其他错误?

    有错误问题,请您参考:https://www.xxx.com/error

  • 如何更新?正式激活后会发生什么情况?

    除非安装说明中另有说明,否则官方更新可能会导致无法激活。 要从本网站更新软件,您需要在此处下载此软件的新版本(如果可用),并将其安装在计算机上安装的版本之上(替换)。在这种情况下,您将保存此软件的激活和设置。

  • 如何下载?链接不起作用?

    我们使用百度网盘,132云盘和微软网盘,除了百度网盘,其他两款不限速,如果链接失效,请您联系客服处理

  • 已发布更新。我什么时候升级我的版本?

    所有软件如有更新,我们第一时间推送,视自己情况更新使用

  • 如何更改语言?

    打开“系统偏好设置”->“通用>语言和地区”->应用程序-“+”。 选择应用和语言。此方法适用于大多数应用程序。 Adobe 产品中的语言通常是在产品本身的安装阶段选择的。 游戏中的语言通常会在游戏本身的设置中发生变化。

  • 如何删除软件?

    有很多选择。最简单的方法是使用特殊的实用程序来卸载应用程序,例如App Cleaner Uninstaller 要删除 Adobe 产品,请使用 Creative Cloud Cleaner Tool

  • 需要远程帮助吗?

    网站已开通永久会员的可享受免费远程,如果非永久会员,远程安装另外收费

给TA打赏
共{{data.count}}人
人已打赏
技术教程

第十章:枚举和一组预处理的#define 有何不同,了解C++基本语言特性变量和类型

2024-2-19 10:34:00

技术教程

第十二章:实现可变数组 struct{int namelen; char namestr[1];};,了解C++基本语言特性变量和类型

2024-2-21 11:04:00

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧