前置声明相关

前置声明相关

一个前置声明是指在程序员尚未给出完整定义之前对一个标示符(一个类型、一个变量或者一个函数)的声明。一个很简单的例子就是我们在函数A中使用了函数B,但是函数B的声明在函数A之后,这个时候,就需要对函数B进行前置声明,实际上就是在函数A之前提供一个函数B的原型(prototype)。这种现象其实在C语言编程中我们已经习以为常了,在C++中亦是如此,只不过在编写较大规模程序的时候,由于定义了较多的类,而这些类之间有可能是互相依赖的,换言之,类与类之间会互相引用,包括对成员函数的引用等等。

比如需要在文档类调用视图类

在doc头文件加前置声明
class CTestView;
如有必要在doc源文件加上testview.cpp(或者加在头文件也可),
在testview也是如此,在头文件
加前置声明
class CTestDoc;
源文件加testdoc.cpp(或者加在头文件也可)

一、类嵌套的疑问

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

复制到剪贴板  C/C++代码
[cpp] view plaincopy
  1. class B;     
  2.     
  3. class A     
  4. {     
  5.    public:     
  6.    B b;     
  7. };     
  8.     
  9. class B     
  10. {     
  11.    public:     
  12.    A a;     
  13. };     
  14.     

因为在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
它的意思是,本文件内的代码只被使用一次。

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

C/C++代码
[cpp] view plaincopy
  1. //文件A.h中的代码     
  2. #pragma once     
  3.     
  4. #include “B.h”     
  5.     
  6. class A     
  7. {     
  8.    public:     
  9.    B* b;     
  10. };     
  11.     
C/C++代码
[cpp] view plaincopy
  1. //文件B.h中的代码     
  2. #pragma once     
  3.     
  4. #include “A.h”     
  5.     
  6. class B     
  7. {     
  8.    public:     
  9.    A* a;     
  10. };     
  11.     


 

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

C/C++代码
复制到剪贴板  C/C++代码
[cpp] view plaincopy
  1. //文件A.h中的代码     
  2. #pragma once     
  3.     
  4. #include “B.h”     
  5.     
  6. class B;     
  7.     
  8. class A     
  9. {     
  10.    public:     
  11.    B* b;     
  12. };     
  13.     
[cpp] view plaincopy
  1. //文件B.h中的代码     
  2. #pragma once     
  3.     
  4. #include “A.h”     
  5.     
  6. class B;     
  7.     
  8. class B     
  9. {     
  10.    public:     
  11.    A* a;     
  12. };     
  13.     

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

三、两点原则

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

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

 

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

posted on 2013-07-21 16:05 zhanzc 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/jameszhan/p/declare.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/274153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

陌生人社会_陌生人之旅

陌生人社会The Last of Us Part II is a game that is deeply invested in the minutiae of its characters. The pain they cause, the things that drive them, and the particularities of their self-destruction and salvation. The game’s commitment to the true natur…

设计师更高效_要成为更好的设计师,我们需要更多的时间进行游戏

设计师更高效重点 (Top highlight)I’m a busy designer. I’m fortunate to be booked out months in advance. My freelance career has proven more stable than other’s “jobs”. I don’t wear busy as a badge of honour. I don’t condone hustle culture or compare …

java数据类型及其说明

Java数据类型及其说明java基本数据类型: java数据类型分为基本数据类型和引用数据类型,基本数据类型就是4类8种,分为数值类型(整数型[byte,short,int,long]),字符型&…

ux设计师薪水_公司与 设计机构:UX设计师的津贴和陷阱

ux设计师薪水Written by Yegor Tsynkevich由Yegor Tsynkevich撰写 The more companies understand the power of a great user experience design and its impact on customer loyalty, the more they are willing to have it embedded in their culture. With so much emphas…

ZOJ 2165 Red and Black

1.采用dfs&#xff1a; #include <iostream>#include <cstdio>#include <cstring>#include <string>using namespace std;char map[25][25];int count 1;int r,c;int dx[4] {1,-1,0,0};int dy[4] {0,0,1,-1};bool judge(int x,int y){ if(x<0…

java中的equals用法

在Object 类中定义有&#xff1a; 1、public boolean equals(Object object )方法提供定义对象是否“相等”逻辑。 2、Object的equals方法定义为&#xff1a;x.equals(y)当x和y是同一个对象的引用时&#xff0c;返回true&#xff0c;否则返回false 3、在其他一些类中&#xff0…

根据图片获得配色方案_配色系列(1)—从图片中获得配色灵感

根据图片获得配色方案前言 (Foreword) When we start designing mobile web pages, we always need to determine the color scheme of the web page first. Well, at this time, unless the customer proposes a color scheme, most of the situations we have to face is to …

七大查找算法

1. 顺序查找2. 二分查找3. 插值查找4. 斐波那契查找5. 树表查找6. 分块查找7. 哈希查找 查找是在大量的信息中寻找一个特定的信息元素&#xff0c;在计算机应用中&#xff0c;查找是常用的基本运算&#xff0c;例如编译程序中符号表的查找。本文简单概括性的介绍了常见的七种查…

HA2795Billboard 可用线段树

#include<iostream>using namespace std;#include<cstdio>#include<algorithm>#define maxn 200005int h,n,w;int root[maxn<<4];int ans;//标记void make_tree(int l,int r,int rt){if(lr){root[rt]w;return ;}int mid(rl)/2;make_tree(l,mid,rt*2);m…

axure下拉列表框单选框_如何在Axure中创建下拉菜单和组合框

axure下拉列表框单选框First, let’s clarify what exactly is a dropdown menu, and what is a combo box, aren’t they the same? Well … no, not really, let me explain.首先&#xff0c;让我们弄清楚什么是下拉菜单&#xff0c;什么是组合框&#xff0c;不是吗&#xf…

Android 第一课 Activity

以下说明基于Android Studio&#xff0c;简称AS。&#xff08;红色字体为自行添加&#xff0c;注在醒目&#xff09; 具体包括&#xff1a;创建活动创建项目 加载布局 在AndroidManifest文件中注册 活动中使用&#xff08;提醒&#xff09;Toast 在活动使用&#xff08;菜…

figma设计_一种在Figma中跟踪设计迭代的简单方法

figma设计As designers, telling a good story is always part of the job. A great story engages the client with the journey of decision making; it shows your team the breadth and depth of the exploration; it also encourages us to reflect on our own design pro…

Android 第二课 Intent

上一节&#xff0c;掌握了活动的创建&#xff0c;但是在启动器中点击应用的图标只会进入到该应用的主活动&#xff0c;那么&#xff0c;如何从主活动跳转到其他活动呢&#xff1f;显式IntentIntent有多个构造函数&#xff0c;其中一个是Intent(Context packContext,Class<?…

ok~加油!

你有梦想&#xff0c;放手去做&#xff01;转载于:https://www.cnblogs.com/the-one/p/3217124.html

latex 插图 上下放_专辑插图中上下文中的文本

latex 插图 上下放Especially if, like me, you’re not properly educated in the world of visual design, typography, and all those other things that a formal education can bring. We’re kind of playing around until something fits right, and doesn’t feel jarr…

亡羊补牢,为时不晚?

亡羊补牢&#xff0c;为时不晚 人总是想着第二次比第一次做的好&#xff0c;可是会一直有第二次机会吗&#xff1f;当你思考好&#xff0c;决定做一件事情的时候&#xff0c;为什么不坚持下去呢&#xff1f;当你思考好&#xff0c;记住坚持到底就会胜利。祝福自己&#xff0…

2013年7月份第4周51Aspx源码发布详情

大型企业通用管理ERP源码 2013-7-26 [VS2010]2013.7.4更新内容&#xff1a;1.修复决策模式-客户等级不能保存问题。2.修复企业知识库有报错问题。3.修复运营模式-人力资源分析模块-在部分模块点击查询后&#xff0c;水晶报表显示无法加载文件问题&#xff0c;4.修复行政办公模…

视觉感知_产品设计中的视觉感知

视觉感知The role of the UX designer has evolved immensely over time, but at its core, it remains the same- UX设计人员的角色随着时间的流逝而发生了巨大的变化&#xff0c;但从本质上讲&#xff0c;它保持不变- to deliver information to users in an effective mann…

Android 第三课 Activity的生命周期

新建项目ActivityLifeCycleTest&#xff0c;创建主活动后&#xff0c;再新建两个子活动--NormalActivity和DialogActivity。 现在活动及其对应布局文件创建完毕。 编辑normal_layout.xml文件&#xff0c;代码如下&#xff1a; <?xml version"1.0" encoding"…

转载:Apache commons开源工具简介

Apache Commons是一个非常有用的工具包&#xff0c;解决各种实际的通用问题&#xff0c;下面是一个简述表&#xff0c;详细信息访问http://jakarta.apache.org/commons/index.html BeanUtilsCommons-BeanUtils 提供对 Java 反射和自省API的包装 BetwixtBetwixt提供将 JavaBean …