C++类包含问题(重复包含和相互包含)
一.重复包含头文件
头文件重复包含,可能会导致的错误包括:变量重定义,类型重定义及其他一些莫名其妙的错误。C++提供两种解决方案,分别是#ifndef和#pragma once
#ifndef _SOME_FILE_H_ #pragma once
#define _SOME_FILE_H_ ...//一些声明语句
...//一些声明语句
#endif
第一种方式:通过这种预处理实现唯一检查。预处理首先测试_SOME_FILE_H_变量是否未定义,如果_SOME_FILE_H_未定义,那么#ifndef测试成功,跟在#ifndef后面的所有行都被执行,直到发现#endif。相反,如果_SOME_FILE_H_已定义,那么#ifndef指示测试为假,该指示和#endif指示间的代码都被忽略。
为了保证头文件在给定的源文件中只处理一次,首先检测#ifndef。第一次处理头文件时,测试会成功,因为_SOME_FILE_H_还未定义,下一条语句定义了_SOME_FILE_H_,那样的话,如果编译的文件恰好又一次包含了该头文件,#ifndef指示会发现_SOME_FILE_H_已经定义,并且忽略该头文件的剩余部分。
当没有两个头文件定义和使用同名的预处理器常量时,这个策略相当有效。当有两个文件使用同一个宏,这个策略就失效了。有两种方案解决:
1.可以为定义在头文件里的实体(如类)命名预处理器变量,来避免预处理器变量重名的问题。如,一个程序只能含有一个名为sales_item的类,通过使用类名来组成头文件和预处理器变量的名字,可以使得很可能只有一个文件将会使用该预处理器变量
2.为了保证宏的唯一性,可以采用google提供的解决方案,在定义宏时,宏名基于其所在的项目源代码数的全路径命名,宏命名格式为:_<PROJECT><PATH>_<FILE>_H_
在定义#ifndef测试宏时,宏名最好采用全大写字母表示
在定义测试宏时,最好采用google提供的解决方案
第二种方式:#pragma once
这种方式一般由编译器提供,只要在头文件的最开始加入这条指令就能够保证头文件只被编译一次。#pragma once用来防止某个头文件被多次include,#ifndef方式用来防止某个宏被多次定义。#pragma once是编译相关,即在这个编译系统上使用,但在其它编译系统上不一定能用,也就是说移植性差,不过现在基本上已经是每个编译器都有这个定义了。
针对#pragma once,GCC已经取消对其的支持了,而微软的VC++却依然支持
如果写的程序需要跨平台,最好使用#ifndef方式,而避免使用#pragma once方式
二.相互包含问题
CA类包含CB类的实例,而CB类也包含CA类的实例。代码如下
//A.h实现CA类的定义 //B.h实现CB类的定义
#include "B.h" #include "A.h" int main()
class CA class CB {
{ { CA instanceA;
public: public: return 0;
int iData; int iData; }
CB instaceB; CA instaceA;
}; };
编译出错分析:
CA类定义时使用CB类的定义,CB类定义时使用CA类的定义,递归定义
A.h包含了B.h,B.h包含了A.h,也存在递归包含的问题
其实,无论是结构体的递归定义,还是类的递归定义,最后都归于一个问题,C/C++采用静态编译模型,在程序运行时,结构或类大小都会在编译后确定。程序要正确编译,编译器必须知道一个结构或结构所占用的空间大小,否则编译器就会报出奇怪的编译错误。
解法:
1.向前声明实现
//A.h实现CA类的定义 //B.h实现CB类的定义
class CB//前向声明CB类 #include "A.h" #include "B.h"
class CA class CB int main()
{ { {
public: public: CA instaceA;
int iData; int iData; return 0;
CB* pinstaceB; CA instaceA; }
}; };
前向声明实现方式的主要实现原则
主函数只需要包含B.h就可以,因为B.h中包含了A.h
A.h中不需要包含B.h,但要声明class CB,在避免死循环的同时也成功引用了CB
包含class CB声明,而没有包含头文件B.h,这样只能声明CB类型的指针,而不能实例化
2.friend声明定义
//A.h实现CA类的定义 //B.h实现CB类的定义
class CB//前向声明CB类 #include "A.h" #include "B.h"
class CA class CB int main()
{ { {
public: public: CA instaceA;
friend classCB; //友元类声明
int iData; int iData; return 0;
CB* pinstaceB; CA instaceA; }
}; };
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的实现部分包含类B的头文件而非声明部分。
C++类包含问题(重复包含和相互包含)的更多相关文章
- 多态时最好将基类的析构函数设为virtual、 C++中两个类相互包含引用问题 (转载)
多态:http://blog.csdn.net/tmljs1988/article/details/8146521 C++中两个类相互包含引用问题:http://blog.csdn.net/leo11 ...
- C++两个类相互包含引用的问题
在构造自己的类时,有可能会碰到两个类之间的相互引用问题,例如:定义了类A类B,A中使用了B定义的类型,B中也使用了A定义的类型 class A { B b; } class B { A* a; } 请 ...
- C++中两个类相互包含引用问题
在构造自己的类时,有可能会碰到两个类之间的相互引用问题,例如:定义了类A类B,A中使用了B定义的类型,B中也使用了A定义的类型 class A { int i; B b; } class B { in ...
- C++中头文件相互包含与前置声明
一.类嵌套的疑问 C++头文件重复包含实在是一个令人头痛的问题,前一段时间在做一个简单的数据结构演示程序的时候,不只一次的遇到这种问题.假设我们有两个类A和B,分别定义在各自的有文件A.h和B.h中, ...
- C/C++ 中头文件相互包含引发的问题
转自:http://blog.csdn.net/hazir/article/details/38600419 今天下午遇到一个头文件相互包含而导致的编译问题,花了我不少时间去调试没找到问题,最后晚上跟 ...
- 下位机多个".c, .h"文件的相互包含及排版
一.背景: 自从接触单片机编程以来,由于工作上的需要,不可避免的时常会接手别人的代码,但常常由于上一位同事的编码随意性有点大,导致可读性非常的差,有时候不得不完全舍弃原有代码,推倒重来,无形中增加了工 ...
- qt c++对象头文件如何相互包含
今天在写qt时,遇到了两个类相互包含的问题,类A要用到类B,类B要用到类A. 类A:a.h #ifndef A_H #define A_H #include <b.h> class A { ...
- [Google Guava] 2.3-强大的集合工具类:java.util.Collections中未包含的集合工具
原文链接 译文链接 译者:沈义扬,校对:丁一 尚未完成: Queues, Tables工具类 任何对JDK集合框架有经验的程序员都熟悉和喜欢java.util.Collections包含的工具方法.G ...
- [VS] - "包含了重复的“Content”项。.NET SDK 默认情况下包括你项目中的“Content”项。
copy to :http://www.cnblogs.com/jinzesudawei/p/7376916.html VS 2017 升级至 VS 2017 v15.3 后,.Net Core 1 ...
随机推荐
- 【Windows 8 Store App】学习:目录
原文http://www.cnblogs.com/java-koma/archive/2013/05/22/3093302.html 写在前面:我之前从事java开发,对MS的一整套东西还没入门哈,难 ...
- WIN7 64位通过VPN远程登录 ASP.Net通过VPN访问Oracle服务器
因为客户这边的服务器是64位的,所以本人手贱,把系统换成了64位的win7,以为来客户这边工作会更方便,谁知道来到客户这边,进不了他们公司的内网,然后给我一个VPN的账号,先VPN然后才能登录他们的测 ...
- 【POJ】3009 Curling 2.0 ——DFS
Curling 2.0 Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11432 Accepted: 4831 Desc ...
- i美股投资研报--Michael Kors(IPO版) _Michael Kors(KORS) _i美股
i美股投资研报--Michael Kors(IPO版) _Michael Kors(KORS) _i美股 i美股投资研报--Michael Kors(IPO版)
- css学习笔记二
下面来总结一下盒子模型,流式布局,浮动布局,层布局(定位布局). 1.盒子模型 有二种:IE盒子模型 和 标准w3c盒子模型 1)IE的盒子模型的content包含了padding和border 2) ...
- nyist 303序号互换(数学推理)
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=303 思路: 开始看错题了,以为最多只有两个字母. 字母转数字的表达式很容易看出来是:(2 ...
- 新辰:4G时代怎样利用手机进行移动APP营销?
未来的时代是4G时代,新辰手机用户的搜索量不在电脑端之下.那么,我们要怎样用手机进行营销呢?手机站点的竞价文章,要怎样去写比較好?手机站点要做专题吗?手机站点的优化思路在哪里?手机的系统不同,在不同的 ...
- asp.net Listbox控件用法
2008-02-18 19:56 来源: 作者: ListBox(列表框)控件可以显示一组项目的列表,用户可以根据需要从中选择一个或多个选项.列表框可以为用户提供所有选项的列表.虽然也可设置列表框为多 ...
- sql server 2005中没有等于等于,高手自行跳过。。
set ANSI_NULLS ONset QUOTED_IDENTIFIER ONgo ALTER TRIGGER [qiandaoTrigger] ON [dbo].[bbsQianDao] AFT ...
- osg项目经验1<MFC+OSG中模型点选效果>
点选主要是重载osg的GUIEventHandler, class CPickHandler : public osgGA::GUIEventHandler{ //自定义回调函数名:CPickHand ...