C++详解PIMPL指向实现的指针

目录
  • 二进制兼容性
  • 功能实现细节隐藏
  • 编译依赖
  • 动态配置功能的实现方法

二进制兼容性

①.概述

二进制兼容是指当库文件升级后所有使用该库的应用程序不必重新编译,其本质就是类的内存布局不变。使用 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();

到此这篇关于C++详解PIMPL指向实现的指针的文章就介绍到这了,更多相关C++ PIMPL内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ pimpl机制详细讲解

    目录 什么是PImpl机制 为什么用PImpl 机制 PImpl实现 方法一 方法二 PImpl 缺点 总结 源码仓库 什么是PImpl机制 Pointer to implementation(PImpl ),通过将类的实现细节放在一个单独的类中,从其对象表示中删除它们,通过一个不透明的指针访问它们(cppreference 是这么说的) 通过一个私有的成员指针,将指针所指向的类的内部实现数据进行隐藏 class Demo { public: ... private: DemoImp* imp_

  • C++学习笔记之pimpl用法详解

    前言 本文主要给大家介绍了关于C++中pimpl用法的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: C++的pImpl可以说是最常见的惯用手法了,在很多的C++项目和C++开发库中都有所见.plmp的缩写就是Pointer to Implementor,顾名思义就是将真正的实现细节的Implementor从类定义的头文件中分离出去,公有类通过一个私有指针指向隐藏的实现类,是促进接口和实现分离的重要机制. 在C++语言中,要定义某个类型的变量或者使用类型的某个成员,就必

  • C++详解PIMPL指向实现的指针

    目录 二进制兼容性 功能实现细节隐藏 编译依赖 动态配置功能的实现方法 二进制兼容性 ①.概述 二进制兼容是指当库文件升级后所有使用该库的应用程序不必重新编译,其本质就是类的内存布局不变.使用 pimpl 方法设计类可以实现二进制兼容的目的. ②.类成员更改后的内存布局 原始类定义: class demoClass { private: int a; int b; }; 类更改后的定义: class demoClass { private: char c; int a; int b; }; ②.

  • 详解C语言中二级指针与链表的应用

    目录 前言 二级指针讲解 链表的应用 定义双链表的结构体 创建双链表 前言 这篇文章即将解决你看不懂或者不会写链表的基本操作的问题,对于初学者而言,有很多地方肯定是费解的.比如函数的参数列表的多样化,动态分配内存空间函数malloc等,其实这些知识和指针联系紧密,尤其是二级指针.那么开始好好的学习这篇文章吧! 二级指针讲解 简述:其实就是一个指针指向另一个指针的地址. 我们都知道指针指向地址,但是指针自身也是一个变量,当然也可以被二级指针所指向. 语法:形如 int x = 10; int *q

  • 详解C++中的this指针与常对象

    C++ this指针详解 this 是C++中的一个关键字,也是一个常量指针,指向当前对象(具体说是当前对象的首地址).通过 this,可以访问当前对象的成员变量和成员函数. 所谓当前对象,就是正在使用的对象,例如对于stu.say();,stu 就是当前对象,系统正在访问 stu 的成员函数 say(). 假设 this 指向 stu 对象,那么下面的语句中,this 就和 pStu 的值相同: Student stu; //通过Student类来创建对象 Student *pStu = &s

  • 详解C++中的对象指针与对象数组

    C++对象指针 指向对象的指针 在建立对象时,编译系统会为每一个对象分配一定的存储空间,以存放其成员.对象空间的起始地址就是对象的指针.可以定义一个指针变量,用来存放对象的指针. 如果有一个类: class Time { public : int hour; int minute; int sec; void get_time( ); }; void Time::get_time( ) { cout<<hour<<":"<<minute<<

  • 详解C语言中的指针与数组的定义与使用

    指针的特点 他就是内存中的一个地址 指针本身运算 指针所指向的内容是可以操作的 操作系统是如何管理内存的 栈空间 4M~8m的大小 当进入函数的时候会进行压栈数据 堆空间 4g的大小 1g是操作系统 全局变量 内存映射 可以对内存的内容修改修改硬盘的内容 一般在数据库中经常使用 内存的分配与释放 c语言分配内存的方法 // malloc(需要分配的大小): 这里的分配的大小需要对齐的2的指数 void *mem = malloc(size); 释放内存 // 一般分配的内容都是在堆空间中的 //

  • GO语言的控制语句详解包括GO语言的指针语法

    GO语言的控制语句 判断结构:if-else 和大多数编程语言一样,if-else的用法基本都一样,直接来一个GO语言的例子 package main import ( "fmt" ) func main( var str string = "A" if str=="A"{ fmt.Println("匹配成功") }else{ fmt.Println("匹配失败") } ) 输出为:匹配成功如果更改了str

  • iOS对象指针和基础数据类型的强转详解

    本文主要介绍了iOS中对象指针和基础数据类型如何进行强转,下面话不多说,直接来看示例详解. 一.对象指针的强转: UIView *view = [UIView new];//new一个UIView类的对象 UILabel *label = (UILabel *)view;//强转成UILabel指针 label.text = @"123";//给label的text属性赋值(调用label的setText方法) 上述代码会产生崩溃,崩溃信息如下: -[UIView setText:]:

  • C++中指针指向二维数组实例详解

    C++中指针指向二维数组实例详解 一维指针通常用指针表示,其指向的地址是数组第一元素所在的内存地址,如下 int ary[4][5]; int(*aryp)[5] = ary; 那么ary[4]相当于int(*aryp),以下理解如此,但参数传递需要知道实参所在 的一维个数,所以传递的时候应该传递多一个参数,子数组的引用可以理解 为(*p),那么取元素就是(*p)[i],如下 void printVal(int(*aryp)[5],int irowCount){ for (int(*p)[5]

  • C++指向函数的指针用法详解

    本文以实例形式展示了C++指向函数的指针用法,是深入学习C++所必须掌握的关键知识点.分享给大家供大家参考之用.具体方法如下: 函数指针 现来看看以下声明语句,看看其含义: float (*h(int, void (*)(int)))(int); 以下是一个变量指针的定义语句: float* pf; 以下是一个普通函数的声明语句: float f(); 请看以下声明语句: float* g(); 因为()的优先级高于*, 所以相当于: float* (g()); g是一个函数, 返回值为floa

  • C++中指向对象的常指针与指向常对象的指针详解

    指向对象的常指针 将指向对象的指针变量声明为const型,并使之初始化,这样指针值始终保持为其初始值,不能改变. 复制代码 代码如下: Time t1(10,12,15),t2;Time * const ptr1=&t1;ptr1=&t2; 定义指向对象的常指针的一般形式为 类名    *    const    指针变量=对象地址; 注意应该在定义指针变量时使之初始化 指向对象的常指针变量的值不能被改变,即始终指向同一个对象,但可以改变其所指向对象中的数据成员(非const型)的值. 往

随机推荐