C++创建稳定的接口-PImpl
c++语言本质上对抽象的原则不太友好,语法要求将public接口和private(或protected)数据成员及方法放在一个类定义中。从而将类的某些内部实现细节向客户公开。这种做法的缺点在于,如果不得不在类中加入新的非public方法或者数据成员,所有的客户代码都将必须重新编译,这对于大型项目来说非常的浪费时间。
不过好消息是这种问题可以被避免。可以创建清晰的接口,并隐藏所有实现细节,从而得到稳定的接口。坏消息是这样做有点复杂。基本原则是为想编写的每个类都定义两个类,接口类和实现类。实现类与不采用这种方法编写的类相同,接口类给出与实现类一样的public方法,但只有一个数据成员,指向实现类对象的一个指针。这被称为pimpl idiom或bridge模式,接口类方法的实现只是调用实现类对象的等价方法。这样做的结果就是无论实现如何改变,都不会影响public接口类,从而降低了重新编译的可能。
当且只有实现改变时,使用接口类的客户不需要重新编译。注意只有在单个数据成员是实现类的指针时,这个方法才有效。如果他是按值传递的数据成员,在实现类的定义改变时,客户代码必须重新编译。
代码示例:
Foo.h
#include <iostream>
#include <memory>
using namespace std;
// 接口类
class Foo
{
public:
Foo();
Foo(int n);
void print();
private:
class Impl;
unique_ptr<Impl> m_impl;
};
Foo.cpp
#include "Foo.h"
using namespace std;
// 实现类
class Foo::Impl
{
public:
void print()
{
cout << "hello" << endl;
}
Impl(int n) : m_n(n) { cout << "impl(int n)" << endl; }
private:
int m_n;
};
Foo::Foo(int n) : m_impl{make_unique<Impl>(n)} {};
void Foo::print()
{
m_impl->print();
}
int main()
{
Foo foo(10);
foo.print();
return 0;
}
为了实现与接口分离,另一种方法是使用抽象接口及实现该接口的实现类,抽象接口时只有纯虚方法的接口。