有时候在设计数据结构的时候,可能会遇到两个类需要相互引用的情形。比如类A有类型为B的成员,而类B又有类型为A的成员。

那么这种情形下,两个类的设计上需要注意什么呢?

同一文件

尝试方案

将A和B的定义都放在一个文件中,例如:

#include <iostream>class A {public:A() {aa_ = 'A';}char aa_;B b_;
};class B {public:B() {bb_ = 'B';}char bb_;A a_;
};int main() {return 0;
}

编译这一段代码,编译出错:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11
main.cpp:9:5: error: ‘B’ does not name a typeB b_;^

编译的报错提示,B不是一个数据类型。可能你会想,会不会前置声明一下就可以了?即将代码修改为:

#include <iostream>class B;class A {public:A() {aa_ = 'A';}char aa_;B b_;
};class B {public:B() {bb_ = 'B';}char bb_;A a_;
};int main() {return 0;
}

编译这一段代码,编译仍然出错:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11
main.cpp:11:7: error: field ‘b_’ has incomplete typeB b_;^

编译时出现"field has incomplete type",通常的错误原因为:类或结构体的前向声明只能用来定义指针对象或引用,因为编译到这里时还没有发现定义,不知道该类或者结构的内部成员,没有办法具体的构造一个对象,所以会报错。

解决办法:将类成员改成指针就好了。程序中使用incomplete type实现前置声明,有助与实现数据类型细节的隐藏。

按照这个办法来进行修改,将b_的类型由B的对象修改成指向类型B的指针:

#include <iostream>class B;class A {public:A() {aa_ = 'A';}char aa_;B *b_;
};class B {public:B() {bb_ = 'B';}char bb_;A a_;
};int main() {A tmp1;std::cout << tmp1.aa_ << " " << tmp1.b_->bb_ << std::endl;B tmp2;std::cout << tmp2.bb_ << " " << tmp2.a_.aa_ << std::endl;return 0;
}

编译这一段代码,编译顺利,没有问题。运行这段代码:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11
yngzmiao@yngzmiao-virtual-machine:~/test$ ./main
A H
B A

又有问题了,tmp1.b_->bb_的打印结果为H。这个问题很容易检查出问题:在类A的定义中,定义了指向类型B的指针b_,但是并没有对该指针分配内存空间,当然会有一些奇怪的值打印出来。可以修改为:

#include <iostream>class B;class A {public:A() {aa_ = 'A';b_ = new B();}char aa_;B *b_;
};class B {public:B() {bb_ = 'B';}char bb_;A a_;
};int main() {A tmp1;std::cout << tmp1.aa_ << " " << tmp1.b_->bb_ << std::endl;B tmp2;std::cout << tmp2.bb_ << " " << tmp2.a_.aa_ << std::endl;return 0;
}

编译又出现问题了:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11
main.cpp: In constructor ‘A::A()’:
main.cpp:9:18: error: invalid use of incomplete type ‘class B’b_ = new B();^
main.cpp:3:7: error: forward declaration of ‘class B’class B;^

编译错误的原因还是incomplete type,即类型B的结构还不知道,怎么能new出来呢?

最终代码

如果想要获得正确的代码,不能将new的操作放在构造函数中,放在其他地方手动创建即可:

#include <iostream>class B;class A {public:A() {aa_ = 'A';}char aa_;B *b_;
};class B {public:B() {bb_ = 'B';}char bb_;A a_;
};int main() {A tmp1;tmp1.b_ = new B();std::cout << tmp1.aa_ << " " << tmp1.b_->bb_ << std::endl;B tmp2;std::cout << tmp2.bb_ << " " << tmp2.a_.aa_ << std::endl;return 0;
}

编译并运行这段代码:

yngzmiao@yngzmiao-virtual-machine:~/test$ g++ main.cpp -o main --std=c++11
yngzmiao@yngzmiao-virtual-machine:~/test$ ./main
A B
B A

原因分析

类A和类B相互引用比较麻烦的根本原因在于:定义A的时候,A的里面有B,所以就需要去查看B的占空间大小,但是查看的时候又发现需要知道A的占空间大小,从而造成死循环

不同文件

不同文件指的是:类型A定义在A.h文件,类型B定义在B.h文件,同时在main.cpp中创建A、B类型的对象进行输出。通过上文的一些经验,可能还需要create()函数来对B指针进行new操作。

由于不同文件的写法,坑比较多,接下来就直接给出正确的代码内容。

在A.h文件中,定义类A:

#ifndef A_H
#define A_Hclass B;                // highlight 1class A {public:A() {aa_ = 'A';}void create();char aa_;B *b_;
};#endif

在A.cpp文件中,定义类A方法的实现:

#include "B.h"        // highlight 2void A::create() {b_ = new B();
}

在B.h文件中,定义类B:

#ifndef B_H
#define B_H#include "A.h"        // highlight 3class B {public:B() {bb_ = 'B';}char bb_;A a_;
};#endif

最终在main.cpp文件中,使用类A和类B:

#include "B.h"
#include <iostream>int main() {A tmp1;tmp1.create();std::cout << tmp1.aa_ << " " << tmp1.b_->bb_ << std::endl;B tmp2;std::cout << tmp2.bb_ << " " << tmp2.a_.aa_ << std::endl;return 0;
}

对这一段代码中的坑进行罗列:

  1. A.h中包含B.h且B.h中包含A.h,头文件不能循环include。需要在定义非指针类的那个.h文件include另一个;而定义指针类的那个.h文件需要使用前置声明
  2. create()函数不能在.h文件中进行定义,因为在该函数中需要进行new操作,而该操作需要又另一个类的完整定义,即需要include。由于第一点原因,只好在.cpp文件中进行方法的实现

总结

两个类相互引用,一个用对象、include;另一个用指针、前置声明、create手动new。手动new的过程不能在构造函数中进行,同时需要知道另一个类的完整定义(include)

注意:本文所举例的部分都没有对new出来的空间进行delete操作,会引发内存泄漏。这部分需要读者自行补充。

相关阅读

  • C++中两个类互相引用的解决方法
  • 程序编译是出现"field has incomplete type"问题的解决

【C++】两个类的相互引用相关推荐

  1. 在使用idea进行开发时,同一个项目当中的类不能相互引用

    问题描述: 同一个项目当中的类不能相互引用,但是项目能够正常运行,导入的包名是灰色的,而类名标红. 解决方案: File → invalidate Caches/Restart → invalidat ...

  2. idea中同一个包中的类相互引用失败

    idea类相互引用失败 问题描述: 在开发过程中,从git上拉取项目,发现同一包下的两个类在相互引用时出错,无法找到对应类,无法运行.困扰好久,记录一下解决办法 原因分析: 由于是同一包下的两个pub ...

  3. 【C++】错误C2027:使用了未定义类型错误原因 两个类之间怎么相互使用成员 向前声明概念

    错误C2027:使用了未定义类型.两个类之间相互引用成员.向前声明 错误C2027:使用了未定义类型 向前声明 单独编译 两个类之间相互引用成员 通过单独编译和向前声明方法实现两个类之间相互引用成员 ...

  4. 毕业设计出现的一个严重错误----文件不能相互引用

    昨晚大概1点,突然点某几个链接,出现了"你指定的网站不能访问,链接错误"这样的情况,然点击其它的网页却没有错误,一段时间极度郁闷,知道1点半没弄出来 今天下午经过一番删除测试,终于 ...

  5. 两个类相互包含引用的问题--类前向声明

    在构造自己的类时,有可能会碰到两个类之间的相互引用问题,例如:定义了类A类B,A中使用了B定义的类型,B中也使用了A定义的类型 class A {     int i;     B b; } clas ...

  6. Cocos Creator两个类相互引用(调用)

    如果两个类相互引用,脚本加载阶段就会出现循环引用,循环引用将导致脚本加载出错: ///Game.js var Item = require("Item"); var Game = ...

  7. cocos 时间函数需要什么引用_Cocos Creator两个类相互引用(调用)

    如果两个类相互引用,脚本加载阶段就会出现循环引用,循环引用将导致脚本加载出错: ///Game.js var Item = require("Item"); var Game = ...

  8. C++两个类互相引用的做法

    1.简单采取前向引用 我们知道C++的类应当是先定义,然后使用.但在处理相对复杂的问题.考虑类的组合时,很可能遇到俩个类相互引用的情况,这种情况称为循环依赖. 例如: class A {public: ...

  9. php 命名空间不能继承,PHP两个类使用同一个命名空间,无法相互调用

    创建了两个类,C和D,都属于同一个namespace:func,在D的构造函数中实例化C并调用C里的cc方法,报找不到func下的C类. //C.php namespace func; class C ...

最新文章

  1. linux修正系统错误指令fsck和badblocks
  2. 如何在Unity中播放影片
  3. Spring AOP的一个简单实现
  4. 【Android】可以下拉刷新的webview,使你的webview效果更加好看,封装自己的WebView...
  5. S5PV210开发 -- QT4.8 移植
  6. 事物与持久化_揭开Redis面纱,发布订阅、事务、安全、持久化
  7. 恭喜我的同事黄玉奇入选开放原子开源基金会TOC
  8. .net中实现伪静态的学习小结
  9. inner join 与 left join 、right join之间的区别
  10. mysqldump导出数据
  11. NS方程求解-NSFnet
  12. Java的高并发编程系列(三)
  13. Java聊天室的设计与实现毕业设计
  14. java整除符号是什么意思_java除法及java除法运算的基础知识
  15. 会让你变得与众不同的22个技巧
  16. 周围剃光头顶留长发型_为什么很多秃顶的人,宁可留周围一圈头发,也不直接剃成光头?...
  17. 重磅 I IT4IT 2.1中文版正式发布特邀专家彭斐推荐
  18. HDI板和盲埋孔电路板的区别
  19. C++转换构造函数和隐式转换函数
  20. Android实现TTS语音播报

热门文章

  1. 前端不哭 | 没有设计师的界面也要支棱起来
  2. Jetson Orin(Ubuntu20.04)安装NoMachine和Jtop
  3. 基于matlab测量物体直径,基于MATLAB视频工件处理的在线检测.doc
  4. Git分支合并请求冲突处理规则
  5. hdu_oj1538A Puzzle for Pirates(海盗分金)
  6. Android 的暗示 hint 用法
  7. 客户端交互设计适配之——屏幕大小
  8. Oracle HRMS APIs
  9. JavaScript(二)--实现分栏
  10. 2022年保育员专业知识(初级)考试单选题专项训练及答案