[关闭]
@lishuhuakai 2016-11-04T21:58:29.000000Z 字数 1720 阅读 1419

CPP常识 01 -- cpp的前向声明

cpp

转自网络,来源不可考.

在这里,我想每天来更新一些关于CPP的,我们应该知道的知识,日积月累,我相信一定能够到达一个比较高的境界的.虽然我以后不一定会使用这门语言.

CPP的前向声明

两个类相互包含引用的问题

在构造自己的类时,有可能会碰到两个类之间的相互引用问题,例如:定义了类ABA中使用了B定义的类型,B中也使用了A定义的类型:

  1. class A
  2. {
  3. int i;
  4. B b;
  5. }
  6. class B
  7. {
  8. int i;
  9. A* a;
  10. }

请注意上面的定义内容,一般情况下是不能出现类A,类B相互引用都定义对象,即如下的样子:

  1. class A
  2. {
  3. int i;
  4. B b;
  5. }
  6. class B
  7. {
  8. int i;
  9. A a;
  10. }

在这种情况下,想想能够有a.b.a.b.a.b.a.b.a.b…………,很有点子子孙孙无穷尽之状,那么我的机器也无法承受。最主要的还是这种关系很难存在,也很难管理。这种定义方式类同程式中的死循环。所以,一般来说,两者的定义,至少有一方是使用指针,或两者都使用指针,但是决不能两者都定义实体对象

言归正传,那么,在定义时因为相互引用肯定会需要相互包含头文档,假如仅仅只是在各自的头文档中包含对方的头文档,是通但是编译的,如下:

  1. //class A.h
  2. #include "B.h"
  3. class A
  4. {
  5. int i;
  6. B b;
  7. }
  8. //class B.h
  9. #include "A.h"
  10. class B
  11. {
  12. int i;
  13. A *a;
  14. }

如上的包含方式可能会造成编译器有错误提示:A.h文档中使用了示知类型B
怎么办?

一般的做法是:两个类的头文档之中,选一个包含另一个类的头文档,但另一个头文档中只能采用class *;的申明形式,而在实现文档中(*.cpp)中包含头文档,如下:

  1. //class A.h
  2. #include "B.h"
  3. class A
  4. {
  5. int i;
  6. B b;
  7. }
  8. //class B.h
  9. class A;
  10. class B
  11. {
  12. int i;
  13. A *a;
  14. }
  15. //B.cpp
  16. //在B.cpp中的文档包含处要有下面语句,否则不能调用成员a的任何内容
  17. #include "A.h"
  18. B::B()
  19. {
  20. ……
  21. }

为什么要前向声明?

我们先举一个栗子:

  1. //foo.h
  2. class foo
  3. {......};
  4. //util.h
  5. class foo;
  6. class util
  7. {
  8. private:
  9. foo* m_foo;
  10. };
  1. 前向声明用于降低inclusion tree的复杂程度,降低模块间的耦合度。

  2. 前提是前向声明的类foo只能用来定义指针或者引用,而不可以定义实体对象,因为指针占用的空间大小固定,而类实体占用的空间大小必须引入头文件才能确定。

    前向声明是一种不完全类型声明,所以它并不能取代完全类型,对于编译器来说,在需要知其被声明对象大小和内容时,前向声明,己不可用。故其应用场景,只有一下两点:

    • 声明引用和指针;
    • 作函数(仅声明)的返回类型或是参数类型.
  3. 实现方式是在util.h头文件中声明类foo,而不引入头文件foo.h。关于这点,你可能会说,即使util.h文件里不包含foo.h,但是你util.cpp文件中仍然要包含foo.h。的确。那么问题来了,如果别人要使用你的util类,就不需要知道foo类的相关信息了。如果即使foo发生改变,引用util.h的那文件,也不需要重新编译了。如果你将fooutil封装成库,那只需要公开util.h就可以了,foo.h根本不需要露面,也就加强了封装性。否则,你发布的库还必须带着foo.h一起发布。

  4. 对于类和结构体,都可以用以上方式进行声明,但是对于结构体,有一种情况是无法前向声明的:

    1. typedef struct{ …… }MyStruct, *MyStruct;

    对于这种形式,你是无法用MyStruct进行前向声明的。所以尽量不要将结构体设计成这种形式,哪怕非得这样写,也给它个名字,以便前向声明:

    1. typedef struct _MyStruct{ …… }MyStruct, *MyStruct;

总结

所以这点的结论其实很简单,那就是如果可以的话,尽量多使用cpp的前向声明,因为这个玩意可以减少依赖,如果很多文件包含了某个头文件,如果这个头文件发生改变,那么这些包含了头文件的文件都要重新编译,这是一件非常耗时的事情,这虽然是一个小的知识点,但是用处还是挺大的.

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注