pImpl技术学习笔记

MirrorYuChen
MirrorYuChen
发布于 2024-11-30 / 22 阅读
0
0

pImpl技术学习笔记

1.pImpl技术

1.1 原理介绍

指向实现对象的指针(Pointer to implementation, PImp)是一项C++编程技术,通过将类实现细节放置在单独类中(内部通过不透明指针访问),进而实现将类实现细节从类声明中移除。
通过这项技术就可以实现:

  • (1) 声明与定义分离,对用户隐藏实现;
  • (2) 加快编译速度:原始实现中,当需要改变成员变量时,所有包含该头文件的源文件都需要重新编译,现在只需要去实现文件中进行修改,从这个层面上来讲,加快了编译速度;

1.2 实现方法

1.2.1 一般实现

  • 头文件代码:
// A.h
#pragma once
#include <string>
#include <memory>

class A {
public:
  A();
  ~A();
  
  double getData(int index) const;
  std::string getName() const;
  
private:
  struct Impl;
  std::unique_ptr<Impl> pimpl_;
};
  • 实现代码
// A.cc
#include "A.h"
#include <string>
#include <vector>

struct A::Impl {
  std::string name_ = "mirror";
  std::vector<double> data_ = { 1.0, 2.0, 3.0};
};

A::A() {
  pimpl_.reset(new Impl());
}

double A::getData(int index) const {
  if (index > 0 && index < pimpl_->data_.size()) {
    return pimpl_->data_[index];
  }
  return 0;
}

std::string A::getName() const {
  return pimpl_->name_;
}

A::~A() = default;
  • 当A的析构函数实现直接写在A.h中时,会出现如下错误
required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = A::Impl; _Dp = std::default_delete<A::Impl>]'
error: invalid application of 'sizeof' to incomplete type 'A::Impl'
static_assert(sizeof(_Tp)>0
  • 原因:A析构函数实现在头文件时,Impl对于A的析构函数而言只有声明,实现则是不可见的,因此并不知道Impl有哪些成员,也不知道Impl类占用多大内存,所以sizeof(Impl)时报错。

1.2.2 更一般的实现

  • 上面那种实现还是需要前置声明实现类Impl,这里可以用一种更常见的做法,利用C语言的void*万能指针来实现:
  • 头文件代码
#pragma once
#include <string>
class A {
public:
  A();
  ~A();
  
  double getData(int index) const;
  std::string getName() const;
  
private:
  void *pimpl_;
};
  • 实现代码
// A.cc
#include "A.h"
#include <string>
#include <vector>

struct Impl {
  std::string name_ = "mirror";
  std::vector<double> data_ = { 1.0, 2.0, 3.0};
};

A::A() {
  pimpl_ = new Impl();
}

double A::getData(int index) const {
  if (index > 0 && index < pimpl_->data_.size()) {
    return pimpl_->data_[index];
  }
  return 0;
}

std::string A::getName() const {
  return pimpl_->name_;
}

A::~A() {
  Impl *ptr = ( Impl * )pImpl_;
  delete ptr;
  ptr = nullptr;
}

1.3 技术优缺点

优点:

  • (1) 隔离内外,形成一个安全区,将错误控制在设计范围内;
  • (2) 减少二义性,隐藏意味着外部调用产生二义性的可能性被尽量隔绝;
  • (3) 头文件依赖减少,带来编译开销降低;
  • (4) 对ABI(Application Binary Interface)有更好的兼容性;
  • (5) 有可能使用延迟加载,提高资源利用率;

缺点:

  • (1) 增加复杂性,多一层抽象就多一层效率耗减,同时对指针管理也增加了复杂性;
  • (2) 需要处理拷贝(要么禁止掉);
  • (3) 编译器不能跨过 pImpl指针看到实现类内部细节,不能强制 const约束,因此存在const脱离了编译器掌控。当使用pImpl模式时,成员函数即使被声明为 const,它仍然可以通过 pImpl指针调用实现类中非 const成员函数,从而潜在地修改对象的状态。

评论