热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C++交叉引用问题

C++交叉引用问题问题下面是几个编译错误:missingtypespecifier-intassumed.unknownoverridespecifier.

C++交叉引用问题

问题

下面是几个编译错误:

missing type specifier - int assumed.

unknown override specifier.

‘CTestA’ does not name a type

我想不少人会遇到上面的编译错误,这很可能是交叉引用所造成的。
虽然C++交叉引用并不是一个很常见的问题,而且应该尽量避免交叉引用。但是如果迫不得已非要采取交叉引用的话,那有什么好的解决方法吗?答案是肯定的。

解决方法

下面我们通过一个简单的例子来辅助说明。下面是某个工程下的几个文件:

  • TestB.h
#include "CTestB.h"

class CTestB
{
private:
CTestA m_a
;
public:
CTestB() {
}
virtual ~CTestB() {}
};
  • TestA.h
#include "CTestA.h"

class CTestA
{
public:
CTestB m_b
;
public:
CTestA() {
}
virtual ~CTestA() {}
};
  • Test.cpp
#include 
#include "CTestA.h"
#include "CTestB.h"

int main()
{
CTestA A;

return 0;
}

如果我们使用GNU GCC编译器,那么编译结果十分的恐怖,不仅编译时间比较长,而且产生的错误也会很多,你可能会得到下面的编译日志:

mingw32-g++.exe -Wall -fexceptions -g -Iinclude -c

E:\blog_test\main.cpp -o obj\Debug\main.o

In file included from include/CTestB.h:4:0,

from include/CTestA.h:4,

from include/CTestB.h:4,

from include/CTestA.h:4,

from include/CTestB.h:4,

from include/CTestA.h:4,

from include/CTestA.h:4,
from include/CTestB.h:4,
from include/CTestA.h:4,
from E:\blog_test\main.cpp:2:
include/CTestB.h:6:7: error: redefinition of ‘class CTestB’

以及下面的编译信息:

include\CTestA.h|4|error: #include nested too deeply
include\CTestB.h|4|error: #include nested too deeply
include\CTestA.h|9|error: ‘CTestB’ does not name a type
include\CTestB.h|6|error: previous definition of ‘class CTestB’
include\CTestA.h|6|error: redefinition of ‘class CTestA’

include\CTestA.h|6|error: previous definition of ‘class CTestA’
include\CTestB.h|6|error: redefinition of ‘class CTestB’
include\CTestB.h|6|error: previous definition of ‘class CTestB’

我们不难发现,上面的编译错误的很可能就是上述的两个头文件互相引用对方所造成的,即所谓的交叉引用。其实原理也很好理解,我们可以站在编译器编译器角度(think like a compiler)想一想:

当你编译编译源文件时,你需要为对象A分配空间,因为A中有成员变量m_b(CTestB类型),所以给A分配的空间必须足够存放m_b。那么此时也必须为m_b分配足够存放CTestA大小的空间…如此循环下去,你会发现编译进入了一个死锁。于是就出现了上面我们看到的编译结果。

NOTE:编译信息取决于你使用的编译器,使用不同的编译器可能会得到不同的编译结果。

宏保护(Include guard)

为了防止编译过程进入死锁,我们可以在头文件中加入一些特殊的宏:

#ifndef CTESTB_H
#define CTESTB_H
...
#endif

或者使用

#pragma once

这两种宏都可以确保所在文件文件在一次单独编译中只被包含一次。这两者的优缺点比较可以在这里找到。即使在上面的文件中加入宏保护,也无法完全解决问题,程序有产生了 新的编译错误:

‘CTestA’ does not name a type

前置声明(Forward declaration)

要解决上述编译错误,可以采取前置声明(Forward declaration),即在定义CTestA之前,先声明类CTestB;类CTestB同理。同时让我们来看看头文件TestA.h的改变:

  • TestA.h
#ifndef CTESTA_H
#define CTESTA_H
#include "CTestB.h"
class CTestB;
class CTestA
{
public:
CTestB *m_b;
public:
CTestA() {}
virtual ~CTestA() {}
};

#endif // CTESTA_H

这里我们必须要注意一点,类A中的成员变量必须是指针或引用类型。有一篇博文中很形象的讲解了前置声明,有兴趣的朋友可以参考这里。

总结

言多必失,这里我只用一句话作总结:在程序中我们应该尽量避免使用C++交叉引用。

参考资料
  1. Resolve header include circular dependencies in C++, stackoverflow-http://stackoverflow.com/questions/625799/resolve-header-include-circular-dependencies-in-c

  2. C++交叉引用问题,互相包含头文件容易出问题,所以头文件能不包含就不包含, 开源中国. http://my.oschina.net/jlmpp/blog/135929

  3. C++中前置声明的应用与陷阱, CSDN. http://blog.csdn.net/yunyun1886358/article/details/5672574


推荐阅读
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
author-avatar
mobiledu2502932321
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有