您的位置:首页 > 编程语言 > C语言/C++

文件间的编译依存关系 分类: C/C++ 2015-06-10 15:37 34人阅读 评论(0) 收藏

2015-06-10 15:37 477 查看
关于头文件的编译,在我另一篇转载的博客中有很详细的说明,在这里不再多言。

现在直接比较以下两种类的实现方式:

//头文件1
#include <string>
#include "date.h"
#include "address.h"

using namespace std;

class Person{
public:
Person(const std::string& name,const Date& birthday,const Address& addr);
string name()const;
string birthDate()const;
string address()const;
...
private:
string theName;
Date theBirthDate;
Address theAddress;
};

//头文件2
#include <string>
//注意,这里的string不是class,它本身其实是一个宏定义
//对C++标准库当中定义的类或函数在了解有限的情况下,个人认为不应当随意继承或更改
class Date;
class Address;

using namespace std;

class Person{
public:
Person(const std::string& name,const Date& birthday,const Address& addr);
string name()const;
string birthDate()const;
string address()const;
...
private:
string theName;
Date theBirthDate;
Address theAddress;
};


以上两个头文件,其类定义是完全相同的,不同之处在于类的声明方式,头文件1中直接在类定义包含进头文件中,头文件2则使用了前置声明的方式。对于这两种方式前者是优于后者的,原因如下:

因为采用#include方式包含其实类似于宏展开,把一个头文件里的东西copy到另一个文件当中,的确,头文件不会直接参与编译,但头文件要不要编译呢?答案是肯定的,不编译的话如果有错误谁负责?头文件的编译其实是在包含它的源文件当中,用于IDE的都知道,一旦源文件当中的某一个字符更改了,那源文件就会重新编译。一旦Date或Address中的接口或数据成员改变,那包含它的所有源文件就要重新编译。而头文件2中的方式则不会引起这种效果,因为头文件2中只有声明,声明只是告诉编译器这个符号的存在。

这就是所谓的编译依存关系,一个头文件更改会让所有包含它的源文件重新编译。

那头文件2是否就完美了呢?其实不然,当class Person的接口或成员变量改变的时候,包含头文件2的源文件同样需要重新编译。有什么办法可以进一步降低编译依存关系呢?当我只想改变Person的实现或Person的成员变量而不改变接口的时候,如何避免包含头文件2的源文件重新编译?

//头文件3
#include <string>
class Date;
class Address;
class PersonImpl;

using namespace std;

class Person{
public:
Person(const std::string& name,const Date& birthday,const Address& addr);
string name()const;
string birthDate()const;
string address()const;
...
private:
std::tr1::shared_ptr<PersonImpl> pImpl;
};

//头文件3对应的源文件
#include "Person.h"
#include "PersonImpl.h"
Person::Person(const std::string& name,const Date& birthday,
const Address& addr)
:pImpl(new PersonImpl(name,birthday,addr))
{
}
string Person::name()const
{
return pImpl->name();
}


Person和PersonImpl的内容几乎是一模一样的,我把PersonImpl理解为Person的影子,Person的所有操作都是调用PersonImpl的对应操作,会包含#include"PersonImpl"的源文件有且仅有Person类,PersonImpl的改变仅仅只会影响到Person的源文件。Person只含一个PersonImpl的指针,指针或引用的定义只需要相应类的声明式,而定义式不是必须的。只要PersonImpl的接口不变,那么Person的接口也不必改变,Person不改变,那就不会引起编译依存效应。

这种编程技法叫pimpl idion (pimpl 是“point to implementation”)的缩写。接口与实现完全分离!

另一种方法是采用抽象类的方式实现,后绪补充!

本文所有内容均来自《Effective C++》!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: