先记录一些零碎的知识点:

1. 一个类可以被声明多次,但只能定义一次,也就是可以 class B;  class B;  class B; ……;  class B {……};  这样子。

2. 一个类 C 的声明中(函数只声明还没定义)可以使用一个只被声明还没定义的类 B,但只能使用类 B 的指针或引用(用作函数参数或其他等等),不能是完整的对象。

3. 若类 C 的函数中需要使用到类 B 的函数,则类 B 的函数必须已定义好而不能只是声明。

 #include<iostream>

 class B;
class B;
class B; class C;
class C; class B {
public:
void func() const { std::cout << "B.func()" << std::endl; }
void func(C *c) const; // { /* c->func(); */ }
// 无法在这里直接使用 c->func();如果强迫在同一个文件中实现的话代码结构会变得很乱
}; class C {
public:
C() {}
void func() const {
std::cout << "C.func():" << std::endl;
}
void func(B *b) const {
std::cout << "C.func(B *b):" << std::endl;
b->func();
}
void func(const B *b) const { // 底层 const 可以重载,顶层 const 不可以重载
std::cout << "C.func(const B *b):" << std::endl;
b->func();
}
void func(const B &b) const {
std::cout << "C.func2(const B &b):" << std::endl;
b.func();
}
}; void B::func(C *c) const {
std::cout << "B.func(C *c):" << std::endl;
c->func();
} int main() {
C c;
B b;
b.func(&c);
const B cb; c.func(&b);
c.func(&cb);
c.func(b);
std::endl(std::cout);
return ;
}

main.cpp

  因此,鉴于以上的种种规则,对于两个互相依赖难以分割的类,我们可以用一些比较规范的方法去组织项目的结构,比如对于两个类 B 和 C:

1. 在 B.h 和 C.h 两个头文件中分别声明好 class B {……} 和 class C {……} ,类内需要引用到另一个类的函数只有声明而暂时没有定义,而把这些函数的定义也就是实现全部写到 B.cpp 和 C.cpp 中(或者把所有函数的定义都放到 .cpp 文件中去);

2. 在 B.h 头文件的顶端写上 class C; ,在 C.h 头文件的顶端写上 class B; ,也就是为要引用的类作声明,所以两个头文件如下:

 class C;

 class B {
public:
void func() const { std::cout << "B.func()" << std::endl; }
void func(C *c) const;
};

B.h

  

 class B;

 class C {
public:
C() {}
void func() const { std::cout << "C.func():" << std::endl; }
void func(B *b) const ;
void func(const B *b) const ;
void func(const B &b) const ;
};

C.h

  相应的 .cpp 文件如下:

 void B::func(C *c) const {
std::cout << "B.func(C *c):" << std::endl;
c->func();
}

B.cpp

 void C::func(B *b) const {
std::cout << "C.func(B *b):" << std::endl;
b->func();
} void C::func(const B *b) const { // 底层 const 可以重载,顶层 const 不可以重载
std::cout << "C.func(const B *b):" << std::endl;
b->func();
} void C::func(const B &b) const {
std::cout << "C.func2(const B &b):" << std::endl;
b.func();
}

C.cpp

  然后在主函数中,除了 #include "B.h" 和 #include "C.h" 外,还要依次 #include "B.cpp" 和 #include "C.cpp" :

 #include<iostream>
#include "B.h"
#include "C.h"
#include "B.cpp"
#include "C.cpp" int main() {
C c;
B b;
b.func(&c);
const B cb; c.func(&b);
c.func(&cb);
c.func(b);
std::endl(std::cout);
return ;
}

main.cpp

  注意,必须先 #include 完所有的 .h 头文件才可以 #include *.cpp 文件,否则编译会报错,这是因为 *.cpp 里的都是实现,必须确实地得到相应的类或函数的定义才行,所以必须先把所有的 .h 头文件也就是所有的声明引入才可以,编译器才能按照其规则生成中间代码和进行函数的链接。(好像 cocos2d-x 中也是这样子的?)

  可以看到,分解后的代码结构更清晰更容易维护,否则只能像第一个 main.cpp 文件一样糅合在一起,当类的数量和规模增多时难以维护。

  C++ primer ch13 中的 Message 和 Folder 类稍后再整理,休息下准备上课了。

C++学习笔记一 —— 两个类文件互相引用的处理情况的更多相关文章

  1. Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  2. JVM学习笔记-第六章-类文件结构

    JVM学习笔记-第六章-类文件结构 6.3 Class类文件的结构 本章中,笔者只是通俗地将任意一个有效的类或接口锁应当满足的格式称为"Class文件格式",实际上它完全不需要以磁 ...

  3. Protocol Buffer学习教程之编译器与类文件(三)

    Protocol Buffer学习教程之编译器与类文件(三) 1. 概述 在前面两篇中,介绍了Protobuf的基本概念.应用场景.与protobuf的语法等.在此篇中将介绍如何自己编译protobu ...

  4. AJPFX学习笔记JavaAPI之String类

    学习笔记JavaAPI之String类 [size=10.5000pt]一.所属包java.lang.String,没有子类.特点:一旦被初始化就不可以被改变. 创建类对象的两种方式: String ...

  5. 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理

    在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...

  6. java学习笔记07--日期操作类

    java学习笔记07--日期操作类   一.Date类 在java.util包中定义了Date类,Date类本身使用非常简单,直接输出其实例化对象即可. public class T { public ...

  7. Java程序猿的JavaScript学习笔记(10—— jQuery-在“类”层面扩展)

    计划按例如以下顺序完毕这篇笔记: Java程序猿的JavaScript学习笔记(1--理念) Java程序猿的JavaScript学习笔记(2--属性复制和继承) Java程序猿的JavaScript ...

  8. 多态时最好将基类的析构函数设为virtual、 C++中两个类相互包含引用问题 (转载)

    多态:http://blog.csdn.net/tmljs1988/article/details/8146521 C++中两个类相互包含引用问题:http://blog.csdn.net/leo11 ...

  9. java学习笔记16--I/O流和文件

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note16.html,转载请注明源地址. IO(Input  Output)流 IO流用来处理 ...

随机推荐

  1. Git 实战教程

    CVS及SVN都是集中式的版本控制系统,而Git是分布式版本控制系统,集中式和分布式版本控制系统有什么区别呢? 先说集中式版本控制系统,版本库是集中存放在中央服务器的,而大家工作的时候,用的都是自己的 ...

  2. jquery实现nav的下拉

    <script type="text/javascript"> $(function(){ //tab_flag 根据此标记判断是否由父级nav指向了nav_list ...

  3. Sublime Text3 常用快捷键

    1. 更改变量名的几种方法 a.选中变量,ctrl+d 一个个选择 b.选中变量,alt+F3   2.查找打开过的文件:Ctrl+P,然后输入最近的文件名就可以即时预览到文件内容. 3.ctrl+r ...

  4. Java学习-042-获取目录文件列表(当前,级联)

    以下三个场景,在我们日常的测试开发中经常遇到: 软件自动化测试,在进行参数测试时,我们通常将所有相似功能的参数文件统一放在一个目录中,在自动化程序启动的时候,获取资源参数文件夹中所有参数文件,然后解析 ...

  5. 微信公众平台开发(26) ACCESS TOKEN

    本文介绍微信公众平台下Access Token的概念及获取方法. 一.Access Token access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token.正常 ...

  6. 完全偶图K(3,3)与完全图K5是否存在平面表示

    本文论述k(3, 3)与K5平面表示的存在性.首先给出图的平面表示的定义: 若可以在平面里画出一个图而让边没有任何交叉(边的交叉是指边的直线或弧线在它们的公共端点以外的地方相交),则这个图是平面性的. ...

  7. 利用Java进行MySql数据库的导入和导出

    利用Java来进行Mysql数据库的导入和导出的总体思想是通过Java来调用命令窗口执行相应的命令. MySql导出数据库的命令如下: mysqldump -uusername -ppassword  ...

  8. 借助Glances Monitor,密切关注你的系统

    两种方法安装 glances 通常可以有两种方法安装 glances.第一种是通过编译源代码的方式,这种方法比较复杂另外可能会遇到软件包依赖性问题.还有一种是使用特定的软件包管理工具来安装 glanc ...

  9. zabbix centos 6.4 安装

    --下载地址 http://www.zabbix.com/download.php --环境 centos6.4-x86-64 zabbix 2.2 x64 --所需安装包 rpm -ivh unix ...

  10. Qt之界面实现技巧

    一.主界面 1.窗口 最小化 最小化 关闭按钮 显示状态自定义 setWindowFlags(Qt::CustomireWindowHint); setWindowFlags(Qt::WindowCl ...