定义

有些时候我们可以声明一些类但是并不去定义它,当然这个类的作用也很有限了。

比如class foo;

声明一个foo类,这个声明,有时候也叫做前向声明(forward declaration),在声明完这个foo类之后,定义完这个foo类之前的时期,foo类是一个不完全的类型(incomplete type),也就是说foo类是一个类型,但是这个类型的一些性质(比如包含哪些成员,具有哪些操作)都不知道。

作用

这个类的作用很有限.

  1. 不能定义foo类的对象。
  2. 可以用于定义指向这个类型的指针或引用。
  3. 用于声明(不是定义)使用该类型作为形参或者返回类型的函数。

应用

在c++中,如果要为类编写头文件的话,一般是要#include一堆头文件的,但利用前向声明和c++编译器的特性,其中大部分是不需要的。

c++编译器做的事情主要是:

  1. 扫描符号;

  2. 确定对象大小。

所以很多时候并不需要将类都include进来。

  1. 由于所有对象类型的引用所占用的空间都是相同大的,所以c++编译器很好确认对象大小。

    1
    2
    3
    4
    5
    6
    
    class string;
    class Sample
    {
        private:
        string &s;
    };
    

    ``

    这里只需要做一个string的前向声明就可以了,不需要#include

  2. 由于所有类型的指针也是相同大小的。也可以只做前向声明就好。

    1
    2
    3
    4
    5
    6
    
    class string;
    class Sample
    {
        private:
        string *s;
    };
    

    ``

    这里只需要做一个string的前向声明就可以了,不需要#include

  3. 声明成员函数的形参或者是返回类型,也可以利用前向声明的性质。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    class string;
    class foo;
    class Sample
    {
    public:
        foo foo_test(foo &);
    private:
        string &s;
        foo *f;
    };
    

    ``

    这里,我根本没有定义一个foo类,但是还是可以这样用,因为成员函数不占类对象的大小,所以c++编译器还是可以确定对象的大小。

    前向声明的作用在于告诉编译器这个一个在别的地方定义的类型。这样C++编译器就能生成正确的符号表了。

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

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

class A
{
	int i;
	B b;
}
class B
{
	int i;
A* a;
}

一般来说,两者的定义,至少有一方是使用指针,或两者都使用指针,但是决不能两者都定义实体对象。

class A
{
	int i;
	B b;
}
class B
{
	int i;
	A a;
}

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

//class A.h
#include "B.h"
class A
{
	int i;
	B b;
}

//class B.h
#include "A.h"
class B
{
	int i;
	A *a;
}

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

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

//class A.h
#include "B.h"
class A
{
	int i;
	B b;
}

//class B.h
class A;
class B
{
	int i;
	A *a;
}

//B.cpp
//在B.cpp中的文档包含处要有下面语句,否则不能调用成员a的任何内容
#include "A.h"
B::B()
{
……
}