最近在做一个不算大的项目,编写的头文件也就6-7个左右,再加上用上了OpenCV库与Eigen库,所以编译的时候特别蛋疼,一堆的warning,而且都是一样的warning,因为一个头文件里有warning出现,然后又有其他几个头文件包含了这个出现“warning”的头文件,所以这编译的输出又刷屏了!这样我想起当初玩WOW的时候用自动宏刷屏。- -
关键的是,这拖慢了编译速度。还好<<Effective C++>>这本书看到了条款31,有降低编译依存关系的方法。书里介绍了两种方法:Handle class,Interface class.下面我重点介绍一下Handle class的方法。
这个方法设计到class的定义与声明。有的情况下,必须给出class的定义,才能使用该class及其相关的东西,如:
Date birthday;
这种情况下就必须给出Date类的定义,如包含定义其的头文件:
#include "Date.h"
否则无法通过编译。
有的情况下,给出class的声明即可。如该class的对象当形参,或者定义一个该class的指针或引用:
class Date; //声明
Date today(); //没问题
void getWeather(Date d); //当形参
Date *someOneBirthday; //定义指针
以上都能通过编译,而仅仅作class的声明就可以。
所以,尽量跟着这条光明大道走:
1. 如果能使用对象引用(object references)或者对象的指针(object pointers)可以完成任务,就不用使用对象(objects)。
2. 如果能够,尽量以class声明式代替class的定义式。
还有一个技巧就是:
为声明式和定义式提供不同的头文件。
如:
#include "datefwd.h"
即刚才class Date的声明头文件。
下面举一个血淋淋的例子。
#include <memory>
#include <string>
#include "Address.h"
#include "Date.h"
using namespace std;
class Person
{
//tr1::shared_ptr<PersonImpl> pImpl;
string mName;
Date mBirthDate;
Address mAddress;
public:
Person(const string& name, const Date& birthday,
const Address& address);
/* 偷偷懒,只写一个get函数 */
string getmName() const;
};
看我的Person class里,由于跟Date与Address关联,所以必须包含着两个class的头文件,如果跟更多的类关联的话,你就要包含更多的头文件,然后,你就可以享受被刷屏的乐趣了。
Handle class的方法则是把Person类分割成2个classes,一个提供接口一个提供实现。下面可以看到,如何把那些烦人的与外部类关联的成员封装到另外一个实现类(PersonImpl),然后在Person类风骚地定义一个指向PersonImpl的指针。好吧,有了这个指针,我就对那些外部关联的class作一个声明就行了。
Person的定义:
#include "DependFwd.h" //我是声明头文件
#include <memory>
#include <string>
using namespace std;
class Person
{
tr1::shared_ptr<PersonImpl> pImpl; //这个是智能指针
public:
Person(const string& name, const Date& birthday,
const Address& address);
/* 偷偷懒,只写一个get函数 */
string getmName() const;
};
来看看PersonImpl类怎么定义:
/* 包含其定义式才能使用 */
#include "Date.h"
#include "Address.h"
using namespace std;
/* Person接口实现类 */
class PersonImpl
{
/* 原来Person的成员都在这定义了 */
string mName;
Date mBirthDate;
Address mAddress;
public:
/* 构造函数 */
PersonImpl(const string& name, const Date& birthday,
const Address& address)
:mName(name), mBirthDate(birthday), mAddress(address)
{}
/* 获取姓名 */
string getmNmae() const{ return mName;}
/* 生日和地址的获取函数就不写了 */
};
class Address;
class Date;
class PersonImpl;
只有三行。
测试一下:
#include <iostream>
#include "Person.h"
#include "Date.h"
#include "Address.h"
using namespace std;
int main()
{
Date birthday;
Address address;
Person p("John", birthday, address);
cout << p.getmName() << endl;
system("pause");
return 0;
}
输出
John
当然,使用这些最小化编译依存关系的方法,对程序运行效率还是有所影响的,而且消耗的内存量也有所增加。不过这也解除了接口和实现之间的耦合关系。
如果大家还记得80-20原则:
80%的运行时间都消耗在其中20%的代码上。
所以,还是不要放弃上面提到的技术,如果愿意的话,还是对那80%对效率影响不大的代码作一些结构上的优化,而把提高效率的重心放在另外那20%的代码上。
以后有空的话会写一些Interface classes的方法。