先记录一些零碎的知识点:
1. 一个类可以被声明多次,但只能定义一次,也就是可以 class B; class B; class B; ……; class B {……}; 这样子。
2. 一个类 C 的声明中(函数只声明还没定义)可以使用一个只被声明还没定义的类 B,但只能使用类 B 的指针或引用(用作函数参数或其他等等),不能是完整的对象。
3. 若类 C 的函数中需要使用到类 B 的函数,则类 B 的函数必须已定义好而不能只是声明。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 3 class B; 4 class B; 5 class B; 6 7 class C; 8 class C; 9 10 class B {11 public:12 void func() const { std::cout << "B.func()" << std::endl; }13 void func(C *c) const; // { /* c->func(); */ }14 // 无法在这里直接使用 c->func();如果强迫在同一个文件中实现的话代码结构会变得很乱15 };16 17 class C {18 public:19 C() {}20 void func() const {21 std::cout << "C.func():" << std::endl;22 }23 void func(B *b) const {24 std::cout << "C.func(B *b):" << std::endl;25 b->func();26 }27 void func(const B *b) const { // 底层 const 可以重载,顶层 const 不可以重载28 std::cout << "C.func(const B *b):" << std::endl;29 b->func();30 }31 void func(const B &b) const {32 std::cout << "C.func2(const B &b):" << std::endl;33 b.func();34 }35 };36 37 38 void B::func(C *c) const {39 std::cout << "B.func(C *c):" << std::endl;40 c->func();41 }42 43 44 int main() {45 C c;46 B b;47 b.func(&c);48 const B cb;49 50 c.func(&b);51 c.func(&cb);52 c.func(b);53 std::endl(std::cout);54 return 0;55 }
因此,鉴于以上的种种规则,对于两个互相依赖难以分割的类,我们可以用一些比较规范的方法去组织项目的结构,比如对于两个类 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; ,也就是为要引用的类作声明,所以两个头文件如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 class C;2 3 class B {4 public:5 void func() const { std::cout << "B.func()" << std::endl; }6 void func(C *c) const;7 };
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 class B; 2 3 class C { 4 public: 5 C() {} 6 void func() const { std::cout << "C.func():" << std::endl; } 7 void func(B *b) const ; 8 void func(const B *b) const ; 9 void func(const B &b) const ;10 };
相应的 .cpp 文件如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 void B::func(C *c) const {2 std::cout << "B.func(C *c):" << std::endl;3 c->func();4 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 void C::func(B *b) const { 2 std::cout << "C.func(B *b):" << std::endl; 3 b->func(); 4 } 5 6 void C::func(const B *b) const { // 底层 const 可以重载,顶层 const 不可以重载 7 std::cout << "C.func(const B *b):" << std::endl; 8 b->func(); 9 }10 11 void C::func(const B &b) const {12 std::cout << "C.func2(const B &b):" << std::endl;13 b.func();14 }
然后在主函数中,除了 #include "B.h" 和 #include "C.h" 外,还要依次 #include "B.cpp" 和 #include "C.cpp" :
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #include2 #include "B.h" 3 #include "C.h" 4 #include "B.cpp" 5 #include "C.cpp" 6 7 int main() { 8 C c; 9 B b;10 b.func(&c);11 const B cb;12 13 c.func(&b);14 c.func(&cb);15 c.func(b);16 std::endl(std::cout);17 return 0;18 }
注意,必须先 #include 完所有的 .h 头文件才可以 #include *.cpp 文件,否则编译会报错,这是因为 *.cpp 里的都是实现,必须确实地得到相应的类或函数的定义才行,所以必须先把所有的 .h 头文件也就是所有的声明引入才可以,编译器才能按照其规则生成中间代码和进行函数的链接。(好像 cocos2d-x 中也是这样子的?)
可以看到,分解后的代码结构更清晰更容易维护,否则只能像第一个 main.cpp 文件一样糅合在一起,当类的数量和规模增多时难以维护。
C++ primer ch13 中的 Message 和 Folder 类稍后再整理,休息下准备上课了。