为什么用指针完成类实现会将文件间的依存关系降低?


问题来源于, 我最早看到好多代码类都用 Impl 用作后缀
后来看到这其实来源于 <effective c++> 中条款31:

支持编译依存最小化的一般构想是:相依于声明式,不要相依于定义式,基于此构想的两个手段是Handle classes和Interface classes.

至于 Impl 一般用于提供类的具体实现,用指针进行封装, 看完了,理解了这种类接口和实现思路, 但是不知所以然.

具体, 书中问题代码如下, private 下包括私有函数都是类的实现细节, 依赖的头文件改变,那么此类必须重编


 #include <string>
#include "date.h"
#include "address.h"

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

作者给出了这种的实现,如下:


 #include <string>
#include <memory>
class PersonImpl;
class Date;
class address;
class Person
{
public:
/////////////
private:
    std::tr1::shared_ptr<PersonImpl> pImpl;
}

这样的设计之下,Person的客户端就完全与Dates, Addresses以及Person的实现细目分离了.那些classes的任何实现修改都不需要Person客户端重新编译.

不太明白, 就算 PersonImpl 包装了 address 等实现细节,运行中 new 了一个 PersonImpl 对象实现.
但是编译的时候如果 address 等类有更改, PersonImpl 不也跟着更改了么, 而且 Pserson PersonImpl 一般也放在一个cpp里面定义, 不懂哪里节省编译了. 这块有点糊涂. 各位指点一二.

C++

ROKUHO 9 years, 9 months ago

这个叫 PIMPL idiom ,在大型 C++ 项目里极为常见,减少编译时间只是其中一个好处。更多请见 Is the pImpl idiom really used in practice?

就你的例子而言:

  • 针对前面那种实现,随便对 Person , Date , Address 进行一点点修改, 所有引用 person.h 的文件都必须全部编译 。引用的一多,编译时间大的无法想象。
  • 针对后面那种实现,之前可能对 Person 的修改转移到 cpp 中的 PersonImpl 中,对于 Date Address 的直接头文件引用,也都改为了 前置声明 。你无论改变 PersonImpl , Date , Address ,都不会对 所有引用 person.h 的文件造成影响(它们都不需要重新编译)

person.h 是一个非常重要、且常用的接口。那么节省的编译时间是非常巨大的。

这玩意其实不局限于 C++ 中,往大了说,是 桥接模式 。极为常见的设计模式。

Magic.y answered 9 years, 9 months ago

传统实现:
如果Address等实现细节有变动,那么项目中所有引用到Person头文件的源文件都需要重新编译,这在大一点的项目里很有可能就得10~30分钟

Impl实现:
Address等实现细节有变动只需要重新编译PersonImpl和唯一引用PersonImpl的Person源文件,编译时间瞬间缩短到秒级别

怎么老是你 answered 9 years, 9 months ago

Your Answer