关于前置声明以及两个类之间的构造引用
一、头文件与前置声明
关于头文件,以下是我所查的Google 关于C++编程规范中对头文件依赖的表述:
1.使用前置声明(forward declarations)尽量减少.h文件中#include的数量。
2.当一个头文件被包含的同时也引入了一项新的依赖(dependency),只要该头文件被修改,代码就要重新编译。如果你的头文件包含了其他头文件,这些头文件的任何改变也将导致那些包含了你的头文件的代码重新编译。因此,我们宁可尽量少包含头文件,尤其是那些包含在其他头文件中的。
3.使用前置声明可以显著减少需要包含的头文件数量。举例说明:头文件中用到类File,但不需要访问File的声明,则头文件中只需前置声明class File;无需#include "file/base/file.h"。
4.在头文件如何做到使用类Foo而无需访问类的定义?
1) 将数据成员类型声明为Foo *或Foo &;
2) 参数、返回值类型为Foo的函数只是声明(但不定义实现);
3) 静态数据成员的类型可以被声明为Foo,因为静态数据成员的定义在类定义之外。
5.另一方面,如果你的类是Foo的子类,或者含有类型为Foo的非静态数据成员,则必须为之包含头文件。
6.有时,使用指针成员(pointer members,如果是scoped_ptr更好)替代对象成员(object members)的确更有意义。然而,这样的做法会降低代码可读性及执行效率。如果仅仅为了少包含头文件,还是不要这样替代的好。
当然,.cc文件无论如何都需要所使用类的定义部分,自然也就会包含若干头文件。即:能依赖声明的就不要依赖定义。
那么什么时候选择头文件,什么时候选择前置声明,以下是我总结的两点。
第一个原则应该是,如果可以不包含头文件,那就不要包含了。这时候前置声明可以解决问题。如果使用的仅仅是一个类的指针,没有使用这个类的具体对象(非指针),也没有访问到类的具体成员,那么前置声明就可以了。因为指针这一数据类型的大小是特定的,编译器可以获知。
第二个原则应该是,尽量在CPP文件中包含头文件,而非在头文件中。假设类A的一个成员是是一个指向类B的指针,在类A的头文件中使用了类B的前置声明并便宜成功,那么在A的实现中我们需要访问B的具体成员,因此需要包含头文件,那么我们应该在类A的实现部分(CPP文件)包含类B的头文件而非声明部分(H文件)。
二、实现两个类的互相构造和引用
对于两个类的构造和引用,以下是我编译通过的实现A类和B类对象的构造和引用的源代码程序,并附注释:
a.h文件:
#ifndef A_H
#define A_H
class B;//前置声明,避免依赖
class A
{
private:
int x;
B *b;//定义为指针,以实现两个类的互相引用
public:
A();//A的构造函数,实现构造B类函数
void doSomething();
B* getB();//返回B类型的指针
void setB(B* b);
};
#endif
b.h文件:
#ifndef B_H
#define B_H
class A;//前置声明,避免依赖
class B
{
private:
A * a;//定义为指针,以实现两个类的互相引用
public:
B();
void doSomething();
void doOtherthing();
};
#endif // B_H
a.cpp文件:
#include<iostream>
using namespace std;
#include "b.h"
#include "a.h"
void A::doSomething()
{ cout<<x<<endl;b->doSomething(); }
B *A::getB()//返回B类型的指针
{
return b;
}
void A::setB(B *b)//构造B类对象
{
this->b=b;
}
A::A(){//对其私有数据成员进行初始化
x=5;
}
b.cpp文件:
#include "b.h"
#include "a.h"
#include <iostream>
using namespace std;
void B::doSomething()
{ cout<<"OK"<<endl; }
void B::doOtherthing()
{ cout<<"OK"<<endl;}
B::B(){//通过B类里的A对象来完成B类的构造
a=new A;
a->setB(this);
}
main.cpp文件:
#include <iostream>
using namespace std;
#include "a.h"
#include "b.h"
int main(int argc, char *argv[])
{
A * a = new A;
a->doSomething();
a->getB()->doSomething();
return 0;
}
将下列程序运行后,得到如下结果: