什么是Pimpl?
Pimpl(Pointer to implementation) 是一种隐藏实现的小技巧。尤其是我们在对外提供API时,通常需要将头文件给出,这样很明显的将头文件中的私有成员暴漏给外部。另外Pimpl还可以少代码依赖和编译时间,实现了简单的编译解耦。
Pimpl如何实现?
在接口类内部实例化一个业务的子类,将函数功能实现写入到业务的子类中,接口类本身的函数只用于和外部的业务交互。
通过Pimpl又达成了接口与实现的解耦,提高了代码的可复用性(移植性)。
代码实现
- 首选创建好实现类
//=================================traffic.h=================================//
#pragma once
#include <string>
class Traffic {
public:
Traffic();
~Traffic();
int GetType();
int GetPrice();
std::string GetName();
private:
int type_;
int price_;
std::string name_;
};
//=================================traffic.cpp=================================//
#include "traffic.h"
Traffic::Traffic() {
type_=1;
price_=1000;
name_ = "xiaomi car";
}
Traffic::~Traffic() {}
int Traffic::GetType() { return type_; }
int Traffic::GetPrice() { return price_; }
std::string Traffic::GetName() { return name_; }
- 接着实现我们的接口类,通过接口函数来调用与之对应的实例类函数
//=================================i_traffic.h=================================//
#pragma once
#include <iostream>
#include <memory>
class Traffic;
class I_Traffic {
public:
I_Traffic();
~I_Traffic();
int GetType();
int GetPrice();
std::string GetName();
private:
std::unique_ptr<Traffic> traffic_pimpl_;
};
//=================================i_traffic.cpp=================================//
#include "i_traffic.h"
#include "traffic.h"
I_Traffic::I_Traffic() :
traffic_pimpl_(std::make_unique<Traffic>()) {}
I_Traffic::~I_Traffic() {
}
int I_Traffic::GetType() {
return traffic_pimpl_->GetType();
}
int I_Traffic::GetPrice() {
return traffic_pimpl_->GetPrice();
}
std::string I_Traffic::GetName() {
return traffic_pimpl_->GetName();
}
- 调用
#include <iostream>
#include <memory>
#include "i_traffic.h"
using namespace std;
int main() {
std::unique_ptr<I_Traffic> traffic = std::make_unique<I_Traffic>();
cout << traffic->GetName() << endl;
cout << traffic->GetPrice() << endl;
cout << traffic->GetType() << endl;
return 0;
}
代码解析
- 在i_traffic.h文件中,通过前置声明的形式来声明业务类Traffic
- 在i_traffic.cpp访问实体业务类的函数
- 在对外的接口文件i_traffic.h中,无法看到业务类的中的私有变量
使用Pimpl的优点:
- 极大程度上减少了编译的依赖项,减少头文件的Include的次数;
- 接口和实现分离;
- 隐藏了业务类的实现;私有变量,私有函数等,外部人员拿到的接口类无从知晓业务类的头文件声明
- 提高了代码的可阅读、可移植性;接口类专注对外暴漏,业务类专注与算法和逻辑,接口文件稳定性大大提高;
什么时候不适用Pimpl?
- 如果类本身比较简单,使用Pimpl则会过度嵌套,消耗堆栈内存