Pimpl Idiom(Pointer to IMPLementation),也称为 "Opaque Pointer"(不透明指针)或 "Cheshire Cat"(柴郡猫)模式或者"指针实现模式"和"实现隐藏模式"。这种模式通过将类的实现细节放在一个单独的私有结构体中,并通过指针来访问,从而在不改变类接口的情况下,允许类的实现独立于类的定义进行修改和编译。
文章目录
- [Pimpl Idiom 的基本思想](#Pimpl Idiom 的基本思想)
- 实现步骤
- 典型示例
- [Pimpl Idiom 的优点](#Pimpl Idiom 的优点)
- [Pimpl Idiom 的缺点](#Pimpl Idiom 的缺点)
Pimpl Idiom 的基本思想
Pimpl Idiom 的核心思想是将类的实现细节放在一个私有的实现类中,并使用指向该实现类的指针在公共接口类中进行引用。这种方式可以将类的实现细节完全隐藏在实现文件中,用户只需要看到公共接口。
实现步骤
- 定义公共接口类:在头文件中定义一个公共接口类,并在其中包含一个指向实现类的私有指针。
- 定义实现类:在源文件中定义实现类,包含类的实际实现细节。
- 在公共接口类的构造函数和析构函数中管理实现类的指针:在构造函数中创建实现类的实例,在析构函数中销毁它。
典型示例
头文件:Person.h
cpp
#ifndef PERSON_H
#define PERSON_H
#include <string>
#include <memory>
class Person {
public:
Person(const std::string& name, int age);
~Person();
std::string getName() const;
int getAge() const;
void setName(const std::string& name);
void setAge(int age);
private:
class Impl; // 前向声明实现类
std::unique_ptr<Impl> pImpl; // 使用智能指针管理实现类指针
};
#endif // PERSON_H
头文件:PersonImpl.h
cpp
#ifndef PERSONIMPL_H
#define PERSONIMPL_H
#include <string>
class Person::Impl {
public:
Impl(const std::string& name, int age) : name(name), age(age) {}
std::string name;
int age;
};
#endif // PERSONIMPL_H
源文件:Person.cpp
cpp
#include "Person.h"
#include "PersonImpl.h"
Person::Person(const std::string& name, int age)
: pImpl(std::make_unique<Impl>(name, age)) {}
Person::~Person() = default;
std::string Person::getName() const {
return pImpl->name;
}
int Person::getAge() const {
return pImpl->age;
}
void Person::setName(const std::string& name) {
pImpl->name = name;
}
void Person::setAge(int age) {
pImpl->age = age;
}
使用示例:main.cpp
cpp
#include <iostream>
#include "Person.h"
int main() {
Person person("John Doe", 30);
std::cout << "Name: " << person.getName() << ", Age: " << person.getAge() << std::endl;
person.setName("Jane Doe");
person.setAge(25);
std::cout << "Name: " << person.getName() << ", Age: " << person.getAge() << std::endl;
return 0;
}
从使用来看,用户仍然是无感的。
通过将 Person 和 PersonImpl 设计成两个不同的头文件,Pimpl Idiom 提供了一种强大的机制来隐藏实现细节,减少编译依赖,并提高代码的封装性和模块化程度。这种设计模式特别适用于需要频繁修改实现但希望接口稳定不变的场景。
Pimpl Idiom 的优点
- 隐藏实现细节:用户只能看到头文件中的公共接口,无法看到实现细节。
- 减少编译依赖:头文件只包含指向实现类的指针,因此不需要包含实现类的头文件,从而减少编译依赖。
- 加快编译时间:由于头文件的变化不会影响依赖该头文件的其他文件,编译时间得到了显著加快。
- 二进制兼容性:由于实现类的变化不会影响公共接口类,二进制兼容性得到了保证。
Pimpl Idiom 的缺点
- 额外的内存分配和间接访问开销:由于使用了指针,存在额外的内存分配和间接访问的开销。
- 代码复杂性增加:需要定义和管理实现类和指针,代码复杂性有所增加。