C++ Pimpl 用法
C++开发中使用 Pimpl 的用法可以加速编译速度、避免头文件包含依赖、保持更新后ABI的兼容性。原始的用法是将所有的私有成员变量放在头文件中,这样所有包含该头文件的源文件都需要同步包含一些依赖,Pimpl将其私有成员放到一个类或结构体中,然后提供一个私有指针来进行操作,所有的实现都放在cpp文件中,提供了更好的封装性
1、原始指针方式
将所有成员变量放到一个结构体 ImplData 中
原始指针的形式
// RawPtr.h 头件
#pragma once
#include <string>
class WidgetRaw
{
public:
WidgetRaw();
~WidgetRaw();
void SetCity(const std::string& cityName);
private:
struct ImplData; // 声明一个结构体
ImplData* m_impl; // 声明一个该结构体的指针成员
};
// cpp 文件
#include "RawPtr.h"
#include <vector>
using std::string;
using std::vector;
struct WidgetRaw::ImplData
{
string city;
int year;
vector<int> index;
};
WidgetRaw::WidgetRaw():
m_impl(new ImplData)
{
}
WidgetRaw::~WidgetRaw()
{
delete m_impl;
m_impl = nullptr;
}
void WidgetRaw::SetCity(const string& cityName)
{
m_impl->city = cityName;
}
2、智能指针方式
智能指针
// SmartPtr.h 文件
#include <memory>
#include <string>
class WidgetSmart
{
public:
WidgetSmart();
~WidgetSmart(); // 必须要自定义析构函数,避免编译器自动生成默认析构函数
void SetCity(const std::string& city);
private:
struct ImplData;
std::unique_ptr<ImplData> m_impl; // 使用智能指针来代替原始指针
};
// cpp 文件
#include "SmartPtr.h"
#include <vector>
using std::string;
using std::vector;
struct WidgetSmart::ImplData
{
string city;
int year;
vector<int> index;
};
WidgetSmart::WidgetSmart():
m_impl(std::make_unique<ImplData>())
{
}
WidgetSmart::~WidgetSmart()
{
}
void WidgetSmart::SetCity(const std::string& city)
{
m_impl->city = city;
}
【注意】在智能指针实现中,必须要在头文件声明析构函数,在实现中定义析构函数,以避免编译器生成默认析构函数,否则会编译报错。因为如果没有自定义的析构函数,编译器默认生成析构函数时,此时头文件中只有 ImplData的前向声明,是一个不完整类型。编译器在生成默认析构函数时,在类中没有找到ImplData的定义,就会立刻报错 use of undefined type 'WidgetSmart::ImplData' ,虽然在cpp中定义了ImplData,单编译器此时还未解析到这里。如果在头文件中声明自定义的析构函数,然后在cpp中进行定义,就是告诉编译器它的实现在后面,等解析到析构函数处,已经有了ImplData的完整定义。
浙公网安备 33010602011771号