前置声明的使用

来源:互联网 发布:股票龙虎榜软件 编辑:程序博客网 时间:2024/05/18 00:58

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

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

 

好的,完成,编译一下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的头文件):

[cpp] view plaincopy
  1. class CBed;  

然后在CHouse中用到CBed的,都用CBed的指针类型代(因为指针类型固定大小的,但是CBed的大小只用知道了CBed定义才能确定)。等到要实现CHouse定义的时候(在.h 中不用include 对应类的头文件,直接声明为 指针对象,而在对应的cpp中需要初始化时,才在cpp中include对应类的头文件),就必须要知道CBed的定义了,那是再包好CBed的头文件就行了。

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

 

 

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

 

 

前置声明中的陷阱

注意这里有陷阱:

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

引用版本:

[cpp] view plaincopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// House.h 
 class CBed;// 盖房子时:现在先不买,肯定要买床的 
 class CHouse 
 
     CBed& bed; // 我先给床留个位置 
   <strong> // CBed bed; // 编译出错 </strong> 必须要include CBed.h 才能直接声明对象,因为不允许在定义对象之前就声明...,但是允许在定义对象之前先声明对象指针(空指针)
 public
     CHouse(void); 
     CHouse(CBed& bedTmp); 
     virtual ~CHouse(void); 
     void GoToBed(); 
 }; 
    
 // House.cpp 
<span style="color: #ff0000;"><strong> #include "Bed.h" </strong> </span>
 #include "House.h" // 等房子开始装修了,要买床了 
    
 CHouse::CHouse(void
     : bed(*new CBed()) 
 
     CBed* bedTmp = new CBed();// 把床放进房子 
     bed = *bedTmp; 
 
    
 CHouse::CHouse(CBed& bedTmp) 
     : bed(bedTmp) 
 
 
    
 CHouse::~CHouse(void
 
     delete &bed; 
 
    
 void CHouse::GoToBed() 
 
     bed.Sleep(); 
 

 

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

使用了未定义的类型CBed;

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

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

 

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

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

 

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

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

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

 

 

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

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

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

*************************************************************

0 0