Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1658015
  • 博文数量: 695
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4027
  • 用 户 组: 普通用户
  • 注册时间: 2013-11-20 21:22
文章分类

全部博文(695)

文章存档

2018年(18)

2017年(74)

2016年(170)

2015年(102)

2014年(276)

2013年(55)

分类: C/C++

2015-08-06 21:59:11

前置声明的使用

      有一定C++开发经验的朋友可能会遇到这样的场景:两个类A与B是强耦合关系,类A要引用B的对象,类B也要引用类A的对象。好的,不难,我的第一直觉让我写出这样的代码:


  1. // A.h  
  2. #include "B.h"  
  3. class A  
  4. {  
  5.     B b;  
  6. public:  
  7.     A(void);  
  8.     virtual ~A(void);  
  9. };  
  10.   
  11. //A.cpp  
  12. #include "A.h"  
  13. A::A(void)  
  14. {  
  15. }  
  16.   
  17.   
  18. A::~A(void)  
  19. {  
  20. }  
  21.   
  22. // B.h  
  23. #include "A.h"  
  24. class B  
  25. {  
  26.     A a;  
  27. public:  
  28.     B(void);  
  29.     ~B(void);  
  30. };  
  31.   
  32. // B.cpp  
  33. #include "B.h"  
  34. B::B(void)  
  35. {  
  36. }  
  37.   
  38.   
  39. B::~B(void)  
  40. {  
  41. }  


 

好的,完成,编译一下A.cpp,不通过。再编译B.cpp,还是不通过。编译器都被搞晕了,编译器去编译A.h,发现包含了B.h,就去编译B.h。编译B.h的时候发现包含了A.h,但是A.h已经编译过了(其实没有编译完成,可能编译器做了记录,A.h已经被编译了,这样可以避免陷入死循环。编译出错总比死循环强点),就没有再次编译A.h就继续编译。后面发现用到了A的定义,这下好了,A的定义并没有编译完成,所以找不到A的定义,就编译出错了。提示信息如下:

1>d:/vs2010/test/test/a.h(5): error C2146: syntax error : missing ';' before identifier 'b'
1>d:/vs2010/test/test/a.h(5): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:/vs2010/test/test/a.h(5): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

 

      那怎么办?有办法,C++为我们提供了前置声明。前置声明是什么?举个形象点的例子,就是我要盖一个屋子(CHOuse),光有屋子还不行啊,我还得有床(CBed)。但是屋子还没盖好,总不能先买床吧,床的大小我定了,改天买。先得把房子盖好,盖房子的时候我先给床留个位置,等房子盖好了,我再决定买什么样的床。前置声明就是我在声明一个类(CHouse)的时候,用到了另外一个类的定义(CBed),但是CBed还没有定义呢,而且我还先不需要CBed的定义,只要知道CBed是一个类就够了。那好,我就先声明类CBed,告诉编译器CBed是一个类(不用包含CBed的头文件):


  1. class CBed;  


然后在CHouse中用到CBed的,都用CBed的指针类型代(因为指针类型固定大小的,但是CBed的大小只用知道了CBed定义才能确定)。等到要实现CHouse定义的时候,就必须要知道CBed的定义了,那是再包好CBed的头文件就行了。

      前置声明有时候很有用,比如说两个类相互依赖的时候要。还有前置声明可以减少头文件的包含层次,减少出错可能。上面说的例子。

 

 

[c-sharp] view plaincopy
  1. // House.h  
  2. class CBed; // 盖房子时:现在先不买,肯定要买床的  
  3. class CHouse  
  4. {  
  5.     CBed* bed; // 我先给床留个位置  
  6. public:  
  7.     CHouse(void);  
  8.     virtual ~CHouse(void);  
  9.     void GoToBed();  
  10. };  
  11.   
  12. // House.cpp  
  13. #include "Bed.h"  
  14. #include "House.h" // 等房子开始装修了,要买床了  
  15.   
  16. CHouse::CHouse(void)  
  17. {  
  18.     bed = new CBed(); // 把床放进房子  
  19. }  
  20.   
  21. CHouse::~CHouse(void)  
  22. {  
  23. }  
  24.   
  25. void CHouse::GoToBed()  
  26. {  
  27.     bed->Sleep();  
  28. }  
  29.   
  30. // Bed.h  
  31. class CBed  
  32. {  
  33.   
  34. public:  
  35.     CBed(void);  
  36.     ~CBed(void);  
  37.     void Sleep();  
  38. };  
  39.   
  40. // Bed.cpp  
  41. #include "Bed.h"  
  42.   
  43. CBed::CBed(void)  
  44. {  
  45. }  
  46.   
  47.   
  48. CBed::~CBed(void)  
  49. {  
  50. }  
  51.   
  52. void CBed::Sleep()  
  53. {  
  54.   
  55. }  


 

前置声明中的陷阱

注意这里有陷阱:

1、CBed* bed;必须用指针或引用

引用版本:


  1. // House.h  
  2. class CBed; // 盖房子时:现在先不买,肯定要买床的  
  3. class CHouse  
  4. {  
  5.     CBed& bed; // 我先给床留个位置  
  6.     // CBed bed; // 编译出错  
  7. public:  
  8.     CHouse(void);  
  9.     CHouse(CBed& bedTmp);  
  10.     virtual ~CHouse(void);  
  11.     void GoToBed();  
  12. };  
  13.   
  14. // House.cpp  
  15. #include "Bed.h"  
  16. #include "House.h" // 等房子开始装修了,要买床了  
  17.   
  18. CHouse::CHouse(void)  
  19.     : bed(*new CBed())  
  20. {  
  21.     CBed* bedTmp = new CBed(); // 把床放进房子  
  22.     bed = *bedTmp;  
  23. }  
  24.   
  25. CHouse::CHouse(CBed& bedTmp)  
  26.     : bed(bedTmp)  
  27. {  
  28. }  
  29.   
  30. CHouse::~CHouse(void)  
  31. {  
  32.     delete &bed;  
  33. }  
  34.   
  35. void CHouse::GoToBed()  
  36. {  
  37.     bed.Sleep();  
  38. }  


2、不能在CHouse的声明中使用CBed的方法

使用了未定义的类型CBed

bed->Sleep的左边必须指向类/结构/联合/泛型类型


  1. class CBed; // 盖房子时:现在先不买,肯定要买床的  
  2. class CHouse  
  3. {  
  4.     CBed* bed; // 我先给床留个位置  
  5.     // CBed bed; // 编译出错  
  6. public:  
  7.     CHouse(void);  
  8.     virtual ~CHouse(void);  
  9.     void GoToBed()  
  10.     {  
  11.         bed->Sleep();  // 编译出错,床都没买,怎么能睡  
  12.     }  
  13. };  


3、在CBed定义之前调用CBed的析构函数


[c-sharp] view plaincopy
  1. // House.h  
  2. class CBed; // 盖房子时:现在先不买,肯定要买床的  
  3. class CHouse  
  4. {  
  5.     CBed* bed; // 我先给床留个位置  
  6.     // CBed bed; // 编译出错  
  7. public:  
  8.     CHouse(void);  
  9.     virtual ~CHouse(void);  
  10.     void GoToBed();  
  11.     void RemoveBed()  
  12.     {  
  13.         delete bed; // 我不需要床了,我要把床拆掉。还没买怎么拆?  
  14.     }  
  15. };  
  16.   
  17. // House.cpp  
  18. #include "Bed.h"  
  19. #include "House.h" // 等房子开始装修了,要买床了  
  20.   
  21. CHouse::CHouse(void)  
  22. {  
  23.     bed = new CBed(); // 把床放进房子  
  24. }  
  25.   
  26. CHouse::~CHouse(void)  
  27. {  
  28.     int i = 1;  
  29. }  
  30.   
  31. void CHouse::GoToBed()  
  32. {  
  33.     bed->Sleep();  
  34. }  
  35.   
  36. // Bed.h  
  37. class CBed  
  38. {  
  39.     int* num;  
  40. public:  
  41.     CBed(void);  
  42.     ~CBed(void);  
  43.     void Sleep();  
  44. };  
  45.   
  46. // Bed.cpp  
  47. #include "Bed.h"  
  48.   
  49. CBed::CBed(void)  
  50. {  
  51.     num = new int(1);  
  52. }  
  53.   
  54. CBed::~CBed(void)  
  55. {  
  56.     delete num; // 调用不到  
  57. }  
  58.   
  59. void CBed::Sleep()  
  60. {  
  61.   
  62. }  
  63.   
  64. //main.cpp  
  65. #include "House.h"  
  66.   
  67. int main()  
  68. {  
  69.     CHouse house;  
  70.     house.RemoveBed();  
  71. }  


前置声明解决两个类的互相依赖

接下来,给出开篇第一个问题的答案:


  1. // A.h  
  2. class B;  
  3. class A  
  4. {  
  5.     B* b;  
  6. public:  
  7.     A(void);  
  8.     virtual ~A(void);  
  9. };  
  10.   
  11. //A.cpp  
  12. #include "B.h"  
  13. #include "A.h"  
  14. A::A(void)  
  15. {  
  16.     b = new B;  
  17. }  
  18.   
  19.   
  20. A::~A(void)  
  21. {  
  22. }  
  23.   
  24. // B.h  
  25. class A;  
  26. class B  
  27. {  
  28.     A a;  
  29. public:  
  30.     B(void);  
  31.     ~B(void);  
  32. };  
  33.   
  34. // B.cpp  
  35. #include "A.h"  
  36. #include "B.h"  
  37. B::B(void)  
  38. {  
  39.     a = New A;  
  40. }  
  41.   
  42.   
  43. B::~B(void)  
  44. {  
  45. }  


 

前置声明在友元类方法中的应用

《C++ Primer 4Edition》在类的友元一章节中说到,如果在一个类A的声明中将另一个类B的成员函数声明为友元函数F,那么类A必须事先知道类B的定义;类B的成员函数F声明如果使用类A作为形参,那么也必须知道类A的定义,那么两个类就互相依赖了。要解决这个问题必须使用类的前置声明。例如:


  1. // House.h  
  2. #include "Bed.h"  
  3. class CHouse  
  4. {  
  5.     friend void CBed::Sleep(CHouse&);  
  6. public:  
  7.     CHouse(void);  
  8.     virtual ~CHouse(void);  
  9.     void GoToBed();  
  10.     void RemoveBed()  
  11.     {  
  12.     }  
  13. };  
  14.   
  15. // House.cpp  
  16. #include "House.h"  
  17.   
  18. CHouse::CHouse(void)  
  19. {  
  20. }  
  21.   
  22. CHouse::~CHouse(void)  
  23. {  
  24.     int i = 1;  
  25. }  
  26.   
  27. void CHouse::GoToBed()  
  28. {  
  29. }  
  30.   
  31. // Bed.h  
  32. class CHouse;  
  33. class CBed  
  34. {  
  35.     int* num;  
  36. public:  
  37.     CBed(void);  
  38.     ~CBed(void);  
  39.     void Sleep(CHouse&);  
  40. };  
  41.   
  42. // Bed.cpp  
  43. #include "House.h"  
  44. CBed::CBed(void)  
  45. {  
  46.     num = new int(1);  
  47. }  
  48.   
  49. CBed::~CBed(void)  
  50. {  
  51.     delete num;  
  52. }  
  53.   
  54. void CBed::Sleep(CHouse& h)  
  55. {  
  56.   
  57. }  


阅读(714) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~