超前引用

所谓超前引用是指一个类型在定义之前就被用来定义变量和声明函数。
    一般情况下,C/C++要求所有的类型必须在使用前被定义,但是在一些特殊情况下,这种要求无法满足,例如,在类time_outDialog中保留了对象指针,该对象用于显示/修改一些信息。为了实现对话框"应用"按钮,把对话框做的修改立刻更新到time_outDialog界面上,为此,需要在对话框类中需要保存
view类的指针,这样定义关系就变成如下的代码:

time_outDialog.h中的代码:

#ifndef _TIME_OUTDIALOG_H_
#define _TIME_OUTDIALOG_H_

#include
<QDialog>

#include "../../mainwindow/mainwindow.h"

namespace
Ui{
        class
time_outDialog;
}

class
MainWindow;

class time_outDialog : public
QDialog
{
        Q_OBJECT
public:
        time_outDialog(QWidget
*parent =
0);
        ~time_outDialog();

private:
        Ui::time_outDialog
*ui;
        MainWindow
*mainwindow_ui;

private
slots:
        void
on_OKButton_clicked();
        void
on_closeButton_clicked();
};

#endif  // end of the time_outDialog.h

mainwindow.h文件中的代码:

#ifndef _MAINWINDOW_H_
#define _MAINWINDOW_H_

#include
"../Set/time_outDialog/time_outDialog.h"

#include
<QtGui/QMainWindow>
#include <QLabel>

namespace
Ui
{
        class
MainWindow;
}

class
time_outDialog;

class MainWindow : public
QMainWindow
{
        Q_OBJECT
public:
…………

};

#endif  // The end of _MAINWINDOW_H_

更一般的情况,类A和类B需要彼此互相引用,这样必然有一个类会先被定义,而另外一个类后被定义,这样在先被定义的类引用后被定义的类的时候,就导致了所谓的超前引用。

   超前引用导致的错误有以下几种处理办法:
   1)
使用类声明
   在超前引用一个类之前,首先用一个特殊的语句说明该标识符是一个类名,即将被超前引用。其使用方法是:
           a)
 用class
ClassB;声明即将超前引用的类名
           b)
 定义class
ClassA
           c)
 定义class
ClassB;
           d)
 编制两个类的实现代码。
    上述方法适用于所有代码在同一个文件中,一般情况下,ClassA和ClassB分别有自己的头文件和cpp文件,这种
方法需要演变成:
           a)
分别定义ClassA和ClassB,并在cpp文件中实现之
           b)
在两个头文件的开头分别用class ClassB;和class
ClassA;声明对方
           c)
在两个cpp文件中分别包含另外一个类的头文件
    NOTE:这种方法切记不可使用类名来定义变量和函数的变量参数,只可用来定义引用或者指针。
   
    2)
使用全局变量
    由于全局变量可以避免超前引用,不用赘述。我的习惯是,把类对象的extern语句加在该类头文件的最后,大家喜欢
怎样写那都没有什么大问题,关键是保证不要在头文件中胡乱包含。
    3)
使用基类指针。
    这种方法是在引用超前引用类的地方一律用基类指针。而一般情况下,两个互相引用的类并不涉及其基类,因此不会造成
超前引用。以开始的例子说:在CMyDialog类中用CView*代替CMyView*,在CMyView类中用CDialog*代替CMyDialog*,这样必然
不会造成超前引用。

    说明:本文中,为了叙述方便,把class
AClass;语句成为类AClass的声明,把class
AClass开始的对AClass的类成员变量、
成员函数原型等的说明称为类的定义,而把在CPP中的部分称为类的定义。如果大家对这三个词有不同的理解,请按照自己的本意
把这三个词换成相应的词来理解。

以下摘自http://hi.baidu.com/shilyx/blog/item/c1e3f7f277e29811b17ec5f2.html

C++中头文件相互包含的几点问题

2007-06-19 17:42





一、类嵌套的疑问

C++头文件重复包含实在是一个令人头痛的问题,前一段时间在做一个简单的数据结构演示程序的时候,不只一次的遇到这种问题。假设我们有两个类A和B,分别定义在各自的有文件A.h和B.h中,但是在A中要用到B,B中也要用到A,但是这样的写法当然是错误的:
class
B;

class
A
{
      public:
          B
b;
};

class
B
{
      public:
          A
a;
};
因为在A对象中要开辟一块属于B的空间,而B中又有A的空间,是一个逻辑错误,无法实现的。在这里我们只需要把其中的一个A类中的B类型成员改成指针形式
就可以避免这个无限延伸的怪圈了。为什么要更改A而不是B?因为就算你在B中做了类似的动作,也仍然会编译错误,表面上这仅仅上一个先后顺序的问题。
      为什么会这样呢?因为C++编译器自上而下编译源文件的时候,对每一个数据的定义,总是需要知道定义的数据的类型的大小。在预先声明语句class
B;之后,编译器已经知道B是一个类,但是其中的数据却是未知的,因此B类型的大小也不知道。这样就造成了编译失败,VC++6.0下会得到如下编译错
误:
      error C2079: ‘b‘ uses undefined
class
‘B‘
将A中的b更改为B指针类型之后,由于在特定的平台上,指针所占的空间是一定的(在Win32平台上是4字节),这样可以通过编译。

二、不同头文件中的类的嵌套

在实际编程中,不同的类一般是放在不同的相互独立的头文件中的,这样两个类在相互引用时又会有不一样的问题。重复编译是问题出现的根本原因。为了保证头文
件仅被编译一次,在C++中常用的办法是使用条件编译命令。在头文件中我们常常会看到以下语句段(以VC++6.0自动生成的头文件为例):

#if
!defined(AFX_STACK_H__1F725F28_AF9E_4BEB_8560_67813900AE6B__INCLUDED_)
#define
AFX_STACK_H__1F725F28_AF9E_4BEB_8560_67813900AE6B__INCLUDED_
      //很多语句……
#endif

其中首句#if
!defined也经常做#ifndef,作用相同。意思是如果没有定义过这个宏,那么就定义它,然后执行直到#endif的所有语句。如果下次在与要这
段代码,由于已经定义了那个宏,因此重复的代码不会被再次执行。这实在是一个巧妙而高效的办法。在高版本的VC++上,还可以使用这个命令来代替以上的所
有:
      #pragma
once
它的意思是,本文件内的代码只被使用一次。

但是不要以为使用了这种机制就全部搞定了,比如在以下的代码中:

//文件A.h中的代码
#pragma once

#include "B.h"

class
A
{
      public:
          B*
b;
};

//文件B.h中的代码
#pragma once

#include "A.h"

class
B
{
      public:
          A*
a;
};

这里两者都使用了指针成员,因此嵌套本身不会有什么问题,在主函数前面使用#include
"A.h"之后,主要编译错误如下:
      error C2501: ‘A‘
: missing storage-class or type
specifiers
仍然是类型不能找到的错误。其实这里仍然需要前置声明。分别添加前置声明之后,可以成功编译了。代码形式如下:

//文件A.h中的代码
#pragma once

#include "B.h"

class B;

class
A
{
      public:
          B*
b;
};

//文件B.h中的代码
#pragma once

#include "A.h"

class B;

class
B
{
      public:
          A*
a;
};

这样至少可以说明,头文件包含代替不了前置声明。有的时候只能依靠前置声明来解决问题。我们还要思考一下,有了前置声明的时候头文件包含还是必要的
吗?我们尝试去掉A.h和B.h中的#include行,发现没有出现新的错误。那么究竟什么时候需要前置声明,什么时候需要头文件包含呢?

三、两点原则

头文件包含其实是一想很烦琐的工作,不但我们看着累,编译器编译的时候也很累,再加上头文件中常常出现的宏定义。感觉各种宏定义的展开是非常耗时间的,远不如自定义函数来得速度。我仅就不同头文件、源文件间的句则结构问题提出两点原则,仅供参考:

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

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

超前引用,布布扣,bubuko.com

时间: 2024-11-07 20:11:48

超前引用的相关文章

超前引用不可使用类名来定义变量和函数的变量参数,只可用来定义引用或者指针。

C:\Users\Administrator\Documents\TreeView\mainwindow.h:31: error: C2079: 'MainWindow::mytree' uses undefined class 'BiTree'

在读程序时遇到的一些问题

static在C/C++中的作用?有初始化值的作用吗?1.先来介绍它的第一条也是最重要的一条:隐藏 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性.如果加了static,就会对其它源文件隐藏.2.static的第二个作用是保持变量内容的持久. 存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化.共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用

通过编写串口助手工具学习MFC过程&mdash;&mdash;(八)遇到的一些问题

通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个串口助手再次熟悉一下MFC,并做了一下记录,以便方便以后查阅.做的过程中多是遇到问题直接百度和谷歌搜索来的,所以很多都是不求甚解,知其然不知其所以然.另外做此工具只是为了熟悉了解,许多功能还没有完善!(开发工具VS2008) (八)遇到的一些问题 本例中用到的控件就介绍完了,本节是在本例过程中遇到的

十一、Jmeter 关联-正则表达式提取器

正则表达式提取器在在网页和json中都可以用(复杂JSON提取最好还是用JsonPath),提取完参数后,相当于把参数以key-value的形式放到参数池,以便后面的请求使用.注意:不能超前引用,即在定义前就进行参数化 实战 在请求的子节点下添加后置处理器正则表达式提取器,如下源文件 正则表达式:<a class="postTitle2" href="(.*?)">,获取到所有随笔的URL 获取下标题,只要把表达式改成:<a class="

.net 工程中引用出现感叹号

在工程中引用出现感叹号,有两个原因 原因1:  这是由于之前引用的Dll文件不见了. 右键有感叹号的项,然后选择 "属性" 里边有一个路径属性 这个路径就是之前这个Dll文件的路径,现在这个文件不在了,你需要找到现在这个文件的路径 右键有感叹号的项,然后选择"移除" 右键"引用",选择添加引用,然后选择那个不在的dll的真实路径 其他的项用相同的方式处理 原因2:可能是引用的.Net版本高于了当前工程的.Net版本 更改所引用的工程文件的.Net

C# 动态生成WebService,无需添加引用

C#项目调用WebService是很常见的现象,但一旦修改链接地址就需要重新更新引用很是麻烦,这里跟大家分享一个通过地址,无需添加引用动态生成Webservice的小方法 方法类: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 using System.ServiceModel.Channels

C++学习笔记----2.4 C++引用在本质上是什么,它和指针到底有什么区别

从概念上讲.指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变. 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量). 在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的: 指针传递参数本质上是值传递的方式,它所传递的是一个地址值.值传递过程中,被调

DLL引用摘录

最近使用DllImport,从网上google后发现,大部分内容都是相同,又从MSDN中搜集下,现将内容汇总,与大家分享. 大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比如Windows中的一些功能,C++中已经编写好的一些方法)要重新编写代码,C#有没有方法可以直接都用这些原本已经存在的功能呢?答案是肯定的,大家可以通过C#中的DllImport直接调用这些功能. DllImport是System.Runtime.InteropServices命名空间下的一个属性

PHP中引用类的属性

在一个类中,可以访问一个特殊的指针--$this.如果当前类的一个属性为$attribute,则当在该类中通过一个操作设置或访问该变量时,可以使用$this->attribute来引用. class classname { public $attribute; function operation($param) { $this->attibute = $param echo $this->attribute; } }