大型开发都会遇到的问题
随着软件规模的扩大,包含在一个工程中的模块的数量在不断增长,模块之间的依赖关系也日益复杂。这里只举一个相对简单的例子:一个包含2个类,5个文件的工程。
ImportantClass.h
头文件中声明了构造函数,doWork成员函数和数据成员buffer。
ImportantClass.cpp
cpp文件中为构造函数和doWork提供了最简单的实现。
userclass.h
声明了构造函数,析构函数和doSomething成员函数以及ImportClass类型的数据成员。由于ip是ImportClass的实例,所以构建UserClass的声明中需要ImportantClass头文件(包含类结构信息)。
userclass.cpp
userclass.cpp为构造函数,析构函数和doSomething成员函数提供了最简单的实现。
main.cpp
代码中创建了一个UserClass对象并调用了它的doSomething成员函数。
为了理解上述代码中的问题,首先明确一件事:一般来讲C++编译器编译的编译对象是工程中的那些cpp文件。.h文件一边不会单独编译,而是和包含它的cpp文件一起编译。具体到例子工程,可以大致如下理解:
importclass.cpp在编译时同时编译了importclass.h文件。
userclass.cpp在编译时同时编译的userclass.h文件和importclass.h文件。
main.cpp在编译时同时编译了userclass.h和importclass.h文件。
上面代码中的主要问题是main.cpp真正希望使用的是UserClass,但由于userclass.h又引用了importantclass.h,所以产生了额外的依赖关系。
除了main.cpp以外,importclass.cpp和userclass.cpp中实际会使用到ImportClass类,这两处依赖关系是必要的。
前置声明
上述问题的解决方法就是前置声明,具体代码如下。
代 码中的变化有两点,一个是数据ImportantClass成员的类型变成了指针类型;另一个是include语句变成了class声明语句。两个变化缺 一不可,第二个变化是第一个变化的前提条件。因为这里只是声明了指针类型,所以只要告诉编译器ImportantClass一个类就够了。真正的类型信息 在userclass.cpp中提供。这种方式就是前置声明。以下是修改过的userclass.cpp文件:
通过这种方式消除了main.cpp对于ImportantClass的依赖。项目的依赖层次越多,前置声明的效果也就越明显。
enum前置声明
C++11中,enum类型也可以前置声明了。下面直接引用C++ Primer中的例子。
enum intValues : unsigned long long; //不限定作用域的,必须指明类型
enum class open_modes; //限定作用域的也可以指定,如果不指定则默认int
实际的枚举值可以在另外的cpp文件中另外定义。