使用指针隐藏实现(Pimpl idiom)

时间:2012-11-06 17:51:04

标签: c++ dependencies pimpl-idiom

在某种程度上可以实现以下目标:

x.hpp - 此文件包含在许多其他类

class x_impl; //forward declare
class x {
    public:
        //methods...
    private:
        x_impl* impl_;
};

x.cpp - 实施

#include <conrete_x>
typedef concrete_x x_impl;    //obviously this doesn't work
//implementation of methods...

基本上,我希望用户包含文件 x.hpp ,但不知道 conrete_x.hpp 标题。

由于我只能通过指针使用concrete_x并且它只显示为私有数据成员,因此前向声明应该足以让编译器知道准备多少空间。它看起来很像着名的“pimpl成语”。

你能帮我解决这个问题吗?

PS。我不想使用void*并将其投射..

4 个答案:

答案 0 :(得分:9)

实际上,甚至可以完全隐藏用户:

// Foo.hpp
class Foo {
public:

    //...

private:
    struct Impl;
    Impl* _impl;
};

// Foo.cpp
struct Foo::Impl {
    // stuff
};

我只想提醒你:

  • 你需要写一个正确的析构函数
  • 因此您还需要一个合适的复制构造函数,复制赋值运算符,移动构造函数和移动赋值运算符

有一些方法可以自动化PIMPL,代价是一些黑魔法(类似于std::shared_ptr所做的那样)。

答案 1 :(得分:5)

作为@Angew答案的替代方案,如果不应该让x类用户知道名称​​ concrete_x ,那么你可以这样做:

x.hpp

中的

class x_impl;
class x {
  public:
    x();
    ~x();
    //methods...
  private:
    x_impl* impl_;
};
x.cpp

中的

#include <concrete_x>
class x_impl : public concrete_x { };

x:x() : impl_(new x_impl) {}
x:~x() { delete impl_; }

答案 2 :(得分:2)

这仅在前向声明声明类的实际名称时才有效。因此,要么将 x.hpp 更改为:

class concrete_x;
class x {
    public:
        //methods...
    private:
        concrete_x* impl_;
};

或使用名称x_impl作为标题<concrete_x>中定义的类。

答案 3 :(得分:0)

这就是接口的用途。在共享头文件中定义接口(纯虚拟类)并将其提供给用户。从接口继承您的具体类并将其放在非共享头文件中。在cpp文件中实现具体类(您甚至可以在cpp中定义具体类)。