C++ 惯用法之 PIMPL

背景

PIMPL 是 C++ 中的一个编程技巧,意思为指向实现的指针。具体操作是把类的实现细节放到一个单独的类中,并用一个指针进行访问。

二进制兼容性

①.概述

二进制兼容是指当库文件升级后所有使用该库的应用程序不必重新编译,其本质就是类的内存布局不变。使用 pimpl 方法设计类可以实现二进制兼容的目的。

②.类成员更改后的内存布局

原始类定义:

class demoClass
{
private:
  int a;
  int b;
};

在这里插入图片描述

类更改后的定义:

class demoClass
{
private:
  char c;
  int a;
  int b;
};

在这里插入图片描述

②.pimpl 下类的内存布局

class demoClass
{
private:
  class demoClassImpl;
  demoClassImpl* impl;
};

class demoClass::demoClassImpl
{
public:
  int a;
  int b;
};

在这里插入图片描述

如上图所示,无论类的实现类的数据成员如何变化,类的布局始终不变。

功能实现细节隐藏

①.概述

作为接口的提供者,我们希望接口的使用者不必知道接口实现的更多细节,因为根据类的私有数据成员和方法一般就可以猜测出接口的设计方式。

②.隐藏实现细节

通过 pimp 方法设计类可以实现隐藏类的私有成员和方法的目的,仅对外暴露公有的接口。

class demoClass
{
public:
  void func();//对外接口
private:
  class demoClassImpl;
  demoClassImpl* impl;
};

class demoClass::demoClassImpl
{
private:
  int a;
  int b;
  
  void func1();
  void func2();
public:
  void func();
};

void demoClass::func()
{
  impl->func();
}

编译依赖

①.概述

在一个常用的头文件中如果包含了太多其他不必要的头文件会严重降低编译效率。

②.值类型的成员必须引用其头文件

值类型的成员因为要分配内存大小必须知道其确定的定义,需要包含其头文件

#include "A.h"

class demoClass
{
  A a;
};

如果仅有类的申明则会出错:

class A;
class demoClass
{
  A a;
};

在这里插入图片描述

③.指针或者引用类型,仅需要类的申明

class A;

class demoClass
{
  A *a;
};

④.函数的参数和返回值类型,仅需要类的申明

class A;

class demoClass
{
  A  func(A a);
};

⑤.使用 pimpl 降低编译依赖

一般库文件使用者仅需要包含当前库对应的头文件即可,不应该再包含其他的头文件。假设库的头文件定义如下:

#include "A.h"

class demoClass
{
private:
  A a;
public:
  void func();
};

此时,若 A 为另外一个公共库,则库的使用者需要在项目中配置 A.h 的路径;若 A 为自定义类,则库的提供者还需要额外提供 A.h 文件。

使用 pimpl 方法改进则可以减少编译依赖,仅在类的实现文件中包含头文件即可:

// demoClass.h
class demoClass
{
public:
  void func();//对外接口
private:
  class demoClassImpl;
  demoClassImpl* impl;
};

// demoClass.cpp
#include "A.h"
class demoClass::demoClassImpl
{
private:
  A a;
public:
  void func();

};

动态配置功能的实现方法

①.概述

使用 pimpl 的方式把类的功能实现用另外一个独立的类来完成,可以在需要的时候动态的配置类的实现方法,而保持类的接口不变。

②.代码示例

公共接口类:

class demoClassImpl;
class demoClass
{
public:
    void func();//对外接口
public:
    demoClassImpl* impl;
};

void demoClass::func()
{
    impl->func();
}

功能实现抽象类:

class demoClassImpl
{
public:
    virtual void func() = 0;
};

功能实现派生类:

class demoClassImpl1 : public demoClassImpl
{
public:
    void func() { cout << "实现方式1" << endl; }
};

class demoClassImpl2 : public demoClassImpl
{
public:
    void func() { cout << "实现方式2" << endl; }
};

功能实现方式的动态配置:

demoClass* demo = new demoClass;

demoClassImpl1* impl1 = new demoClassImpl1;
demo->impl = impl1;
demo->func();

demoClassImpl2* impl2 = new demoClassImpl2;
demo->impl = impl2;
demo->func();

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值