zoukankan      html  css  js  c++  java
  • PIMPL(一)

    1 参考

    1. 《effective C++》 条款31:将文件间的编译关系降至最低
    2. PIMPL Idiom: http://c2.com/cgi/wiki?PimplIdiom

    2 什么是PIMPL?

      PIMPL是指pointer to implementation。通过使用指针的方式隐藏对象的实现细节。是实现“将文件间的编译依存关系降至最低”的方法之一。另一个方式是通过接口实现,但其原理一样。

      PIMPL又称作“编译防火墙”、“笑脸猫技术”,它只在C/C++等编译语言中起作用。

    3 为什么要使用PIMPL?

      3.1 理论分析

      庞大的项目,修改一个文件之后,重新编译,所有依赖该文件的文件都需要重新编译,导致编译时间太长。

      3.2 工程实例

      通过描述一个实例来证明上一小节的理论。

        3.2.1 不使用PIMPL

      文件间的依赖关系如图:

      有三个源文件依赖“Person.h”,实际中可以有更多个文件依赖它,为了说明意思,我源码写的都非常简单,主要是为了表明文件间的依赖关系而已。

    Person.h
    
    #ifndef PERSON_H_
    #define PERSON_H_
    struct Person
    {
      void print();
    };
    #endif
    
    Person.cc
    
    #include "Person.h"
    #include <iostream>
    
    void
    Person::print()
    {
      std::cout << "Person::print()" << std::endl;
    }
    
    PersonUser.cc
    
    #include "Person.h"
    
    main.cc
    #include
    "Person.h"
    int main() { return 0; } Makefile
    # # Makefile # author: zhaokai # date:
    2013-11-28 # TESTS = main all : $(TESTS) clean : rm -f $(TESTS) rm -f main.o PersonUser.o Person.o main.o: main.cc Person.h g++ -c main.cc PersonUser.o : PersonUser.cc Person.h g++ -c PersonUser.cc
    Person.o: Person.cc Person.h g
    ++ -c Person.cc $(TESTS): main.o PersonUser.o Person.o g++ -o main main.o PersonUser.o Person.o

       现在我们开始修改Person.h文件:

    #ifndef PERSON_H_
    #define PERSON_H_
    
    struct Person
    {
      int i; // add int i
      void print();
    };
    #endif

      然后make,结果如下:

      依赖Person.h的三个文件都被重新编译了,最后链接生成执行文件。

        3.2.2 使用PIMPL

      使用PIMPL需要将Person类的实现移到PersonImpl类中,使用指针的方式将实现隐藏,相当于Person.h只是一个傀儡而已,而以前依赖它的文件依旧依赖之,文件间的依赖关系如图:

      有三个源文件依赖“Person.h”,实际中可以有更多个文件依赖它,有两个源文件和一个头文件依赖“PersonImpl.h”。

    Person.h
    
    #ifndef PERSON_H_
    #define PERSON_H_
    #include <memory>
    
    struct PersonImpl;
    struct Person
    {
      void print();
     private:
      std::shared_ptr<PersonImpl> pImpl;
    };
    #endif
    
    Person.cc
    
    #include "Person.h"
    #include "PersonImpl.h"
    
    void
    Person::print()
    {
      pImpl->print();
    }
    
    PersonImpl.h
    #ifndef PERSONIMPL_H_
    #define PERSONIMPL_H_ struct PersonImpl { void print(); }; #endif PersonImpl.cc #include "PersonImpl.h" #include <iostream> void PersonImpl::print() { std::cout << "PersonImpl::print()" << std::endl; } PersonUser.cc
    #include
    "Person.h" main.cc
    #include
    "Person.h" int main() { return 0; } Makefile # # Makefile # author: zhaokai # date: 2013-11-28 # TESTS = main all : $(TESTS) clean : rm -f $(TESTS) rm -f main.o PersonUser.o Person.o PersonImpl.o main.o: main.cc Person.h g++ --std=c++11 -c main.cc PersonUser.o : PersonUser.cc Person.h g++ --std=c++11 -c PersonUser.cc Person.o: Person.cc Person.h PersonImpl.h g++ --std=c++11 -c Person.cc PersonImpl.o: PersonImpl.cc PersonImpl.h g++ --std=c++11 -c PersonImpl.cc $(TESTS): main.o PersonUser.o Person.o PersonImpl.o g++ -o main main.o PersonUser.o Person.o PersonImpl.o

      现在我们开始修改PersonImpl.h文件,注意这时候Person.h已经是傀儡了,如果想给Person增加属性那应该修改PersonImpl.h文件:

    #ifndef PERSONIMPL_H_
    #define PERSONIMPL_H_
    
    struct PersonImpl
    {
      int i; // add int i
      void print();
    };
    #endif

      然后make,结果如下:

      依赖PersonImpl.h的两个文件都重新编译了,而依赖于“Person.h”的文件main.cc和PersonUser.cc都没有重新编译。

        3.2.3 对比

      同样是一件事情,为Person类增加属性int i;两种方法导致编译的过程就不同,我们举得例子比较小,如果有100个类似PersonUser这样的文件,那么使用PIMPL,编译时还是只有“Person.cc”和“PersonImpl”两个文件重新编译了;但是不使用PIMPL的话,就是“main.cc”,“Person.cc”和100个类似“PersonUser.cc”这样的文件重新编译,那就是102个文件。

      通过上面的实例就可证明理论分析部分了。

      至于如何使用PIMPL,敬请期待下一篇文章!

  • 相关阅读:
    Cisco网络模拟器踩坑记录
    PAT甲级1009水题飘过
    PAT甲级1011水题飘过
    springmvc中项目启动直接调用方法
    Eclipse中Java文件图标由实心J变成空心J的问题
    mysql求时间差
    maven常用命令
    java单例模式(两种常用模式)
    mybatis一对多,多对一
    mybatis简介
  • 原文地址:https://www.cnblogs.com/lovers/p/pimpl.html
Copyright © 2011-2022 走看看