PIMPL:休想窥探我的隐私!
时间:2022-07-25
本文章向大家介绍PIMPL:休想窥探我的隐私!,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
前言
有时候我们需要提供对外的API,通常会以头文件的形式提供。举个简单的例子: 提供一个从某个指定数开始打印的接口,头文件内容如下:
//来源:公众号编程珠玑
//作者:守望先生
#ifndef _TEST_API_H
#define _TEST_API_H
//test_api.h
class TestApi{
public:
TestApi(int s):start(s){}
void TestPrint(int num);
private:
int start_ = 0;
};
#endif //_TEST_API_H
实现文件如下:
//来源:公众号编程珠玑
//作者:守望先生
#include "test_api.h"
#include <iostream>
//test_api.cc
TestApi::TestPrint(int num){
for(int i = start_; i < num; i++){
std::cout<< i <<std::endl;
}
}
类TestApi中有一个私有变量start_,头文件中是可以看到的。
#include "test_api.h"
int main(){
TestApi test_api{10};
test_api.TestPrint(15);
return 0;
}
常规实现缺点
从前面的内容来看, 一切都还正常,但是有什么问题呢?
- 头文件暴露了私有成员
- 实现与接口耦合
- 编译耦合
第一点可以很明显的看出来,其中的私有变量star_能否在头文件中看到,如果实现越来越复杂,这里可能也会出现更多的私有变量。有人可能会问,私有变量外部也不能访问,暴露又何妨?
不过你只是提供几个接口,给别人看到这么多信息干啥呢?这样就会导致实现和接口耦合在了一起。
另外一方面,如果有另外一个库使用了这个库,而你的这个库实现变了,头文件就会变,而头文件一旦变动,就需要所有使用了这个库的程序都要重新编译!
这个代价是巨大的。
所以,我们应该尽可能地保证头文件不变动,或者说,尽可能隐藏实现,隐藏私有变量。
PIMPL
Pointer to implementation,由指针指向实现,而不过多暴露细节。废话不多说,上代码:
//来源:公众号编程珠玑
//作者:守望先生
#ifndef _TEST_API_H
#define _TEST_API_H
#include <memory>
//test_api.h
class TestApi{
public:
TestApi(int s);
~TestApi();
void TestPrint(int num);
private:
class TestImpl;
std::unique_ptr<TestImpl> test_impl_;
};
#endif //_TEST_API_H
从这个头文件中,我们可以看到:
- 实现都在TestImpl中,因为只有一个私有的TestImpl变量,可以预见到,实现变化时,这个头文件是基本不需要动的
- test_impl_是一个unique_ptr,因为我们使用的是现代C++,这里需要注意的一点是,它的构造函数和析构函数必须存在,否则会有编译问题。
我们再来看下具体的实现:
//来源:公众号编程珠玑
//作者:守望先生
#include "test_api.h"
#include <iostream>
//test_api.cc
class TestApi::TestImpl{
public:
void TestPrint(int num);
TestImpl(int s):start_(s){}
TestImpl() = default;
~TestImpl() = default;
private:
int start_;
};
void TestApi::TestImpl::TestPrint(int num){
for(int i = start_; i < num; i++){
std::cout<< i <<std::endl;
}
}
TestApi::TestApi(int s){
test_impl_.reset(new TestImpl(s));
}
void TestApi::TestPrint(int num){
test_impl_->TestPrint(num);
}
//注意,析构函数需要
TestApi::~TestApi() = default;
从实现中看到,TestApi中的TestPrint调用了TestImpl中的TestPrint实现,而所有的具体实现细节和私有变量都在TestImpl中,即便实现变更了,其他库不需要重新编译,而仅仅是在生成可执行文件时重新链接。
总结
从例子中,我们可以看到PIMPL模式中有以下优点:
- 降低耦合,头文件稳定,类具体实现改变不影响其他模块的编译,只影响可执行程序的链接
- 接口和实现分离,提高稳定性
当然了,由于实现在另外一个类中,所以会多一次调用,会有性能的损耗,但是这点几乎可以忽略。
- Spark调优
- perl语言十分钟入门【零基础可入】
- Spark硬件配置推荐
- Spark作业调度
- 如何把Photoshop改造成远程控制工具(RAT)来利用
- Office高级威胁漏洞在野利用分析
- 10行代码告诉你,为什么说Python数据可视化是一件艺术品
- 没想到你是这样的Linux | 终端下有趣的命令合集
- PhEmail:基于Python的开源网络钓鱼测试工具
- 数据库中间件mysql-proxy细节【mysql官方的中间件】
- Office CVE-2017-8570远程代码执行漏洞复现
- Java 并发包中的读写锁及其实现分析
- 深入理解 Spring 事务原理
- Chrome开发者工具的小技巧
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- OSCache页面缓存的使用
- 静态化技术Freemarker
- 递归的编译优化(1)
- JAVA高并发处理------多线程
- 安卓笔记一
- 这样使用df和du命令更完美
- 批处理中的IF详解
- 来,我们一起打造一款代码命名工具
- HTML转PDF工具Wkhtmltopdf安装使用
- 【Vue.js】Vue.js组件库Element中的时间选择器、日期选择器、日期时间选择器和颜色选择器
- 【Vue.js】Vue.js的Chrome浏览器开发插件DevTools的安装步骤
- 训练提速60%!只需5行代码,PyTorch 1.6即将原生支持自动混合精度训练。
- TensorFlow 模型剪枝
- 机器人如何使用 RRT 进行路径规划?
- 高效 PyTorch:6个训练Tips