文档下载

上一篇文档,PIMPL(一)

1 如何使用PIMPL

  有多种方式实现PIMPL,这里按照《Effective C++》中介绍的方式。

  1.1 基本步骤

  假设原有Person如下:

Person.h

struct Person
{
public:
Person(const int _age);
void print(); private:
int age;
};

Person.cc

Person::Person(const int _age)
: age(_age)
{} void
Person::print()
{
std::cout << "Person::print::age=" << age << std::endl;
}

    1.1.1 将Person改名为PersonImpl

PersonImpl.h

struct PersonImpl
{
public:
PersonImpl(const int _age);
void print(); private:
int age;
};

PersonImpl.cc

PersonImpl::PersonImpl(const int _age)
: age(_age)
{} void
PersonImpl::print()
{
std::cout << "PersonImpl::print::age=" << age << std::endl;
}

    1.1.2 抽象public和protected方法

  将PersonImpl中的public和protected方法成Person。Person中的方法实际调用的是PersonImpl中对应的方法,Person的定义中需要使用PersonImpl,在Person.h文件中绝对不能#include “PersonImpl.h”,这样就是做无用功了。

  • 为什么不能将PersonImpl的对象作为Person的成员变量?

  因为Person类的定义中需要知道该类的大小,如果直接使用PersonImpl的对象,那么就必须知道PersonImpl的定义,而我们恰恰希望在Person的定义中隐藏PersonImpl的定义。这时候,指针就大显神通了。因为指针的大小只与操作系统的位数有关(32位的机器都占4个字节,64位的机器都占8个字节),所以我们可以使用指针指向PersonImpl,从而只需要前置声明就可以了。这就是暗度陈仓吧。

Person.h

struct PersonImpl;  // 前置声明,PIMPL的关键

struct Person
{
public:
Person(const int _age);
void print(); private:
std::shared_ptr<PersonImpl> pImpl; // 指针,暗度陈仓
};

Person.cc

Person::Person(const int _age)
: pImpl(new PersonImpl(_age))
{} void
Person::print()
{
pImpl->print();
}

    1.1.3 使用Person

  在其它地方我们就可以使用Person了,例如:

int main()
{
Person p();
p.print(); return ;
}

    1.1.4     示例源码下载

2 优缺点

  2.1 优点

  • 改变类的私有成员无需重新编译依赖它的文件,所以整个工程重新编译的速度会快很多。
  • 头文件中采用声明式,因此编译时间会快很多。
  • 接口与实现的分离,更有利于单元测试。

  2.2 缺点

  • 实现者需要做更多的工作。
  • 代码会变得不易理解。
  • 运行时动态的获取要执行的方法,性能降低。
  • 需要注意定义拷贝构造换函数或将其禁用。

3 总结

  如《Effective C++》中所说:

  •   如果使用object reference或object pointers可以完成任务,就不要使用objects
  •   尽量以class声明式替换class定义式

4 参考

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