需求

cpp impl 是什么?

解决

impl 解决的问题

  • 依赖太多,包含头文件过多,一个头文件修改,一堆源文件都需要重新编译,导致的编译时间过长。
  • 库文件,不喜欢在头文件中暴露太多的内部成员变量等信息给使用着。

简单 pimpl

// 使用Pimpl

// 在头文件person.hpp中
#include <memory>
class Person {
 public:
  Person();
 private:
  // Person类的实现细节放置在该前向声明的实现类中。
  struct Impl;
  // 指向实现类Impl的私有指针
  std::unique_ptr<Impl> pimpl_;
};

// 在源文件person.cpp中
#include "person.hpp"
#include "basic_info.hpp"
#include <string>
#include <memory>
struct Person::Impl {
  std::string name;
  std::string id;
  BasicInfo basic_info;
};
Person::Person() : pimpl_(std::make_unique<Impl>()) {}

具体的成员变量放到 cpp 文件中,编译为库后,有以下好处:

  • 就算需要修改包含的成员变量,也不需要修改头文件,那么编译的时候,只会编译这个 cpp 文件,其他依赖这个头文件的都不需要重新编译。
  • 使用者并不能从头文件中发现成员变量的细节。

pimpl 更多细节

pimpl 在使用的时候,还会碰到很多需要注意的地方:

  • 析构函数的实现需要完整的类型
  • 移动赋值需要完整类型
  • 移动构造需要完整类型
  • 拷贝构造和拷贝赋值都需要完整类型

所以上面这些都需要放在 cpp 中实现。

#ifndef WEIGHT_H
#define WEIGHT_H
#include <memory>
class Weight
{
public:
    Weight();
    ~Weight();

    Weight(Weight&& rhs);
    Weight& operator=(Weight&& rhs);
private:
    struct Impl;
    std::unique_ptr<Impl> m_impl;
};

#endif // WEIGHT_H
#include "Weight.h"
#include <vector>
#include <string>

struct Weight::Impl {
    std::string name;
    std::vector<double> data;
};

Weight::Weight()
    : m_impl(new Impl())
{

}

Weight::~Weight() = default;
Weight::Weight(Weight&& rhs) = default;
Weight& Weight::operator=(Weight&& rhs) = default;

Weight& Weight::operator=(const Weight& rhs) {
    if (this != &rhs) {
      *m_impl = *rhs.m_impl;
    }
    return *this;
}

参考

C++编程技巧: Pimpl

c++ 类之间的依赖问题:impl、代理模式

软件特攻队|pimpl 惯用法,C++ 必须掌握的设计模式

究竟是什么毁了我的impl实现