C++交叉引用问题

C++类的交叉引用问题 注:类的交叉引用本身就是一种不好的设计,BUT……如果我们非要这么用…… 1、错误的实现方法: main.cpp #include #include "class_a.h" #include "class_b.h" int main() { A aa; aa.InvokeB(); B bb; bb.InvokeA(); } class_a.h #ifnde 阅读详情

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 <iostream>
#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++中前置声明的应用与陷阱, 优快云. http://blog.youkuaiyun.com/yunyun1886358/article/details/5672574

c++类相互引用的问题,如何避免头文件相互包含 如果是在类B成员变量中使用A,那就在B的头文件中,前向声明一个类A,并且在B中定义一个指向A的指针pA,不需要有A的实例的创建,这时候就不需要知道A的定义了,就可以避免包含A的头文件。而在类B的cpp文件中去包含A的头文件,在cpp文件中创建pA的实例。如果只在B的成员函数中使用类A,那就更简单了,只需要在类B的cpp文件中包含类A的头文件就可以了。两个类A,B,相互引用,如果互相包含彼此的头文件,造成无限包含,编译出错。如果只有类A的声明而没有包含实际的头文件,就会导致类的不完整定义错误。 阅读详情

相关推荐

【licode】日志__attribute__

__attribute__

突围 241

C++ 类的交叉引用

什么是交叉引用? 简单来说就是 A类中包含B类的对象,B类中包含A类的对象。 class A{ B b; }; class B{ A a; }; 这样写是会出问题的。 为什么产生交叉引用? 因为编译器如果先编译 A,那么 A 中引用了 B,B 还没有编译所以不能先编译 A,相反如果先编译 B ,B 中引用了 A,A 还没有编译,所以也不能先编译 B。于是编译器就迷茫了。 解决办法 //...

LYSM 2801

一文搞懂【Linux 搭建 Dread Hunger 服务器】附本体及Docker部署集群方案

看直播发现了五花八门的开服器和版本,一时兴起按官方文档进行了搭建尝试,所以我的版本基于官方Stream2024.01.10更新的版本,也就是停服时的玩法,应该可以根据mod和补丁魔改成更那些开服器一样的版本,本文不做谈论,注重服务器搭建,毕竟8个人不可能在一个局域网里乱象:首先,我看了大量教程视频,一类是开服器的推广,另一类自建的基本采用Windows搭建,我觉得最离谱的是大部分教程都会教你买一台远程Windows虚拟机???

LHmine的博客 3983

关于【error C3646: 未知重写说明符】的若干种可能性

如果在程序中出现了error C3646,那么这个错误可能是由多种问题所导致的,这篇文章将对多种错误及对应解决方法进行分析。 在MSDN的官方网站中,Complier Error C3646的定义为unknown override specifier,并且其解释为The compiler found a token in the position where it expecte

BiuBiu741的博客 8151

Simulink/Requirements Toolbox 学习(一):概述

编写、导入、导出需求并使需求正式化;两种创建需求的模块:需求编辑器、需求透视;描述需求的方式:文本、图像和关键字;通过导入和导出需求文件,与第三方需求管理工具(例如 Microsoft Word、Microsoft Excel®、IBM DOORS和 IBM DOORS Next)集成。涉及模块:Requirements Eidtor、Requirements Table等;

854

STM32F103四位数码管动态显示实战:从硬件连接到代码调试(附Proteus仿真)

本文详细介绍了STM32F103驱动四位数码管的动态显示技术,从硬件连接到代码调试的全过程。内容包括共阴/共阳数码管的区别、GPIO配置、动态显示原理及优化方法,并附有Proteus仿真验证。通过实战案例,帮助开发者快速掌握STM32数码管动态显示的核心技术。

weixin_29168393的博客 468

CPP编程中交叉引用问题

Cpp编程中交叉引用

fox0815的博客 344

华为星闪SLE实战:手把手教你用HarmonyOS开发智能家居控制应用(附完整代码)

本文详细介绍了如何利用华为星闪SLE技术,结合HarmonyOS开发智能家居控制应用。通过对比传统无线技术,阐述了星闪SLE在低时延、高可靠性和高并发方面的优势,并提供了从环境搭建、设备发现、连接管理到核心控制代码的完整实战教程,帮助开发者快速上手这一无线短距通信技术。

bert9linguist的博客 505

C++解决智能指针交叉引用问题

智能指针的交叉引用问题,是如下代码发生的问题,导致的结果是对象无法析构,资源无法释放,问题严重! class B; class A { public: A(){cout&lt;&lt;"A()"&lt;&lt;endl;} ~A(){cout&lt;&lt;"~A()"&lt;&lt;endl;} shared_ptr&lt;B&gt; _ptrb; };

yishizuofei的博客 2900

从ARCore到AICore:揭秘Google如何通过系统级服务推动Android生态创新

本文深入探讨了Google如何通过AICore等系统级服务推动Android生态创新。从ARCore到AICore,Google通过分层解耦策略将AI能力下沉至系统层,实现端侧大模型推理,如Gemini Nano在Pixel 8 Pro中的应用。文章剖析了AICore的技术架构、开发者赋能路径及场景化实践,展示了其在通信、内容创作和生产力工具中的卓越表现。

rust6ferris的博客 544

EtherCAT APWR报文实战:从站地址配置(0x0010)的初始化流程与报文解析

本文详细解析了EtherCAT APWR报文在从站地址配置(0x0010寄存器)中的初始化流程与实战应用。通过类比物业分配门牌号,生动解释了APWR报文如何高效批量配置从站地址,并深入探讨了0x0010寄存器的特殊作用、初始化步骤及常见问题排查方法。文章结合IgH主站的三从站配置案例,提供了报文解析技巧和调试建议,帮助工程师快速掌握EtherCAT网络配置的核心技术。

weixin_28744423的博客 410

详解C++智能指针及交叉引用问题

技术交流qq:1032492413 技术交流VX:Chamfion

偶然i的博客 3490

AI智能体丨使用Coze一键生成中式养生视频,39 条作品涨粉 37.9 万!(附保姆级教程)

本文介绍如何利用扣子(Coze)AI智能体工作流高效制作中式养生短视频。该方案通过AI自动生成养生文案、古风插画素材、语音合成和视频剪辑,将传统制作流程从2-3小时缩短至1-2分钟。工作流包含文案创作、音频生成、图片素材制作和视频合成四大模块,采用大模型处理各环节内容,最终输出可直接导入剪映的草稿文件。特别适合希望快速批量制作文化氛围感强、兼具干货与视觉效果的养生内容创作者,效率提升近百倍。

李同学Lino的博客 3026

C++ 遇到的bug,自己的问题,交叉引用了

心情难以平静,调bug调了一天。 Compiling chatserver ../Src/config_redis.cpp          -----------------------------------------------------0 g++  -c -w -D__cplusplus -pthread -fshort-wchar  -fPIC -rdynamic -w -O3

chen199199的专栏 464

2026年迈瑞医疗嵌入式岗位面试题集(含参考答案)

文章摘要 本文总结了嵌入式开发面试中的30道题目,涵盖7大模块:嵌入式硬件与底层驱动、C/C++语言基础、操作系统与多线程编程、网络通信协议、数据结构与算法、开发工具与调试能力、综合素养与职业认知。重点解析了I²C通信协议的工作原理、速率选择及异常排查方法;USB的报文协议与四种传输类型及其适用场景;硬中断与软中断的区别及设计注意事项;字节序的概念与处理方法;以及C++中static和const关键字的核心作用。内容深入实用,适用于嵌入式开发者的技术提升与面试准备。

Open 52

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

C++类的交叉引用

对于C++中,两个类中相互引用对方,当然只能是在指针的基础上,于是我们知道。也就是说在A类的有一个指针引用B类的成员函数或成员对象,而B类中又有一个指针来访问A中的成员函数或对象。这就是C++中类的交叉引用编译于。那如何解决这个问题呢?当然方法很多,但是我一般采用的方法就是声明与实现的分离。也就是说类中的成员函数我们只在类中声明,然后函数的实现要放到另一个文件中去。主要是在类中的交叉引用时候,存在...

weixin_30588675的博客 337

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

C++总结2—shared_ptr交叉引用及多线程安全问题

智能指针是存储指向动态分配对象指针的类,用于生存期控制;能够确保正确销毁动态分配的内存,防止内存泄露。本博文参考博客https://blog.youkuaiyun.com/yusiguyuan/article/details/40628805 (1)智能指针的分类 1.不带引用计数的智能指针auto_ptr 、unique_ptr、 scoped_ptr 2.带引用计数的智能指针shared_ptr强智...

小不点的博客 3385

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

交叉引用的解决方法——类声明的应用

交叉引用的解决方法什么是交叉引用?什么是交叉引用?一言以蔽之,就是:A类中包含B类的对象,B类中包含A类的对象。以一场景为例我们先来看一个场景。假设有一个电子文档(Document)、一个文档下有多个页(Page),每个页下有多个文本单元(TextUnit,表示文档内元素的基本单位),一个文档中的所有文本单元对象都有唯一的ID。这样每创建一个文本单元时都要为其设置一个唯一的ID,我们在Documen

阳光日志 6383

C++经典面试题 | 带引用计数的智能指针循环交叉引用问题

我们先来看看强智能指针shared_ptr循环引用所带来的问题: class B; class A { public: A() { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; } shared_ptr<B> _ptrb; }; class B { public: ...

Lebron 1802

vs 编译问题, C2143, C4430

环境: vs2019 , c++11 情况: A.h 中include"B.h" 原因:头文件互相包含,会导致编译的时候出现编译交叉导致编译出错。 解决方法:去掉A.h中include"B.h", 在A.h加上 class logOpration;然后在A.cpp中include"B.h" ...

sinat_33419023的博客 1019

share_ptr交叉引用导致的内存泄露问题

我们先来看一段代码: #include<iostream> using namespace std; class B; //声明 class A { public: shared_ptr<B> pb_; ~A() { cout << "A delete\n"; } }; class B { public: shared_ptr<A> pa_; ~B() { cout << "B delete\n"; } }; void

qq_31904421的博客 618

交叉引用的解决方法-类方法的应用

交叉引用的解决方法-类方法的应用

never give up的博客 1397

Unreal4 C++ 错误指引(持续更新)

相信同学们现在开始用C++的开发方式去完成某些功能或是去尝试了解UnrealEngine4内部运用流程,这个过程自然少不了与Epic自己包装的C++打交道。 当然与UnrealEngine4的C++必须要遵守UE4工程的规则,比如整个工程配置基本是使用[NMaker]配置完成。在代码编译前需要先编译[UnrealHeaderTool]工具,再由[Unr...

a726219163079648的博客 796

交叉引用头文件报错

当你的代码报警未定义但是你明明已经引用了头文件,那多半是这个原因。要避免头文件交叉包含,如要用到对方类型,我们可以将 类型定义放到 一个公共头文件,如要用到对方的类我们可以使用前置申明并在cpp源文件中引用头文件。下面定义了2个类classA,classB,并在类classA头文件中定义IntA类型,在classB头文件中定义IntB类型,由于两个类都用到了对方头文件中定义的类型因此头文件又相互引用。我们在编写C/C++代码时,如头文件交叉引用将产生未定义报警,但是导致未定义报警的原因是什么呢。

weixin_56940389的博客 590

C++包含头文件规范和交叉引用

包含头文件的语法 #include "header.h" //表示当前文件和header.h文件在同一个工程的同一目录中 #inlcude <header.h> //表示header.h不属于当前工程,是外部引用 声明和定义的区别 声明-declaration 只是表个态,我需要什么类型的参数,返回什么类型参数。 至于怎么实现,不关我的事。 定义-define 函数的实现 如果没有实现这个函数,链接时就会出现undefined reference to "

quietbxj的博客 2630
上一篇: 《解忧杂货店》杂感
下一篇: python离线获取帮助
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值