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

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

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

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

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

 1 #include<iostream>
 2 
 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 }
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; ,也就是为要引用的类作声明,所以两个头文件如下:

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 };
B.h

  

 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 };
C.h

  相应的 .cpp 文件如下:

1 void B::func(C *c) const {
2     std::cout << "B.func(C *c):" << std::endl;
3     c->func();
4 }
B.cpp
 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 }
C.cpp

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

 1 #include<iostream>
 2 #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 }
main.cpp

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

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

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

原文地址:https://www.cnblogs.com/Newdawn/p/4994357.html