文件间的编译依存关系 分类: C/C++ 2015-06-10 15:37 34人阅读 评论(0) 收藏
2015-06-10 15:37
477 查看
关于头文件的编译,在我另一篇转载的博客中有很详细的说明,在这里不再多言。
现在直接比较以下两种类的实现方式:
以上两个头文件,其类定义是完全相同的,不同之处在于类的声明方式,头文件1中直接在类定义包含进头文件中,头文件2则使用了前置声明的方式。对于这两种方式前者是优于后者的,原因如下:
因为采用#include方式包含其实类似于宏展开,把一个头文件里的东西copy到另一个文件当中,的确,头文件不会直接参与编译,但头文件要不要编译呢?答案是肯定的,不编译的话如果有错误谁负责?头文件的编译其实是在包含它的源文件当中,用于IDE的都知道,一旦源文件当中的某一个字符更改了,那源文件就会重新编译。一旦Date或Address中的接口或数据成员改变,那包含它的所有源文件就要重新编译。而头文件2中的方式则不会引起这种效果,因为头文件2中只有声明,声明只是告诉编译器这个符号的存在。
这就是所谓的编译依存关系,一个头文件更改会让所有包含它的源文件重新编译。
那头文件2是否就完美了呢?其实不然,当class Person的接口或成员变量改变的时候,包含头文件2的源文件同样需要重新编译。有什么办法可以进一步降低编译依存关系呢?当我只想改变Person的实现或Person的成员变量而不改变接口的时候,如何避免包含头文件2的源文件重新编译?
Person和PersonImpl的内容几乎是一模一样的,我把PersonImpl理解为Person的影子,Person的所有操作都是调用PersonImpl的对应操作,会包含#include"PersonImpl"的源文件有且仅有Person类,PersonImpl的改变仅仅只会影响到Person的源文件。Person只含一个PersonImpl的指针,指针或引用的定义只需要相应类的声明式,而定义式不是必须的。只要PersonImpl的接口不变,那么Person的接口也不必改变,Person不改变,那就不会引起编译依存效应。
这种编程技法叫pimpl idion (pimpl 是“point to implementation”)的缩写。接口与实现完全分离!
另一种方法是采用抽象类的方式实现,后绪补充!
本文所有内容均来自《Effective C++》!
现在直接比较以下两种类的实现方式:
//头文件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++》!
相关文章推荐
- C++const成员规则
- c语言 文件操作
- C++ 实现链式栈
- 链栈的c++实现
- cocox2d-x HelloWorld分析
- 【C语言学习】C语言功能
- C++引用
- 网上昨天出现的一份纯c语言简历,转过来玩玩,作者很有才
- c++强制类型转换:dynamic_cast、const_cast 、static_cast、reinterpret_cast
- A lightweight synchronous event framework for C++11
- c语言红黑二叉树
- C++之父:C++ 的五个普遍误解
- C语言 二叉树中寻找指定两个数值的最低父节点
- VC++ 输入流迭代器
- MFC C++监听网页窗口的点击超链接跳转问题(即打开新的浏览器窗口)
- c++ Getline()的问题
- 线性链表的c语言实现
- OpenGL Tips: 在Visual C++中调用FreeGLUT
- C++ STL之map详解
- const in C and C++