隐藏PIMPL-Objects拥有的成员的实现

Hiding implementation of members owned by PImpl-objects

本文关键字:成员 实现 拥有 PIMPL-Objects 隐藏      更新时间:2023-10-16

我有一个我想创建一个界面的类,而无需显示任何实现(不是因为它是封闭的源),让我们称之为foo。我知道这通常是通过pimpl-idiom或虚拟接口完成的,我已经实现了该类别的pimpl-idiom。

但是,此类具有公共功能,可以返回其他也包括这些标题的类foo.h.但是,这些类在库中大量使用,因此我不想用PIMPL实现它们,因为这会引入很多开销(它们在3D渲染器的代码中经常使用)。让我们称其中一个酒吧:

foo.h:

#include "Bar.h"
class Foo
{
private:
    class impl;
    std::unique_ptr<impl> m_pimpl;
public:
    Foo();
    Bar& get_bar();
};

foo.cpp:

#include "Foo.h"
class Foo::impl {
private:
    Bar m_bar;
public:
    Bar& get_bar();
};
Foo::Foo() : m_pimpl{std::make_unique<impl>()} 
{ }
Bar& Foo::get_bar() {
    return m_pimpl->get_bar();
}

为此,我需要#include" bar.h",但是bar.h可能包括我想隐藏的标题。这也让我可以选择让酒吧使用pimpl-idiom,但是我不希望酒吧的开销,因为bar在foo内部在内部使用了很多。但是,我想出了一种解决这个问题的方法,但是我对此并不确定,因为我以前从未看到过它:

使用PIMPL而不拥有指针/参考来简单地包装一类并为其创建接口。这样,只有当foo的外部时,开销才适用,但在内部仍将使用非包裹类。

例如,假设PBAR正在包装栏:

pbar.h:

class Bar;
class PBar {
private:
    Bar &m_bar;
public:
    explicit PBar(Bar &bar);
    void do_stuff();
};

pbar.cpp:

#include "PBar.h"
#include "../impl/Bar.h" // This is the actual Bar implementation

PBar::PBar(Bar &bar) : m_bar(bar) {
}
void PBar::do_stuff() {
    m_bar.do_stuff();
}

和foo在创建时实例化PBAR,并引用对象内的实际栏:

foo.h

#include "PBar.h"
class Foo
{
private:
    class impl;
    std::unique_ptr<impl> m_pimpl;
    PBar m_bar;
public:
    Foo();
    PBar& get_bar() { return m_bar; }
};

foo.cpp:

class Foo::impl {
private:
    Bar m_bar;
public:
    Bar& get_bar();
};
Foo::Foo() : m_pimpl{std::make_unique<impl>()},
             m_bar(impl->get_bar())
{ }

这种模式曾经使用过吗?我还有其他方法可以解决这个问题吗?它或多或少与Pimpl相同,但是我还没有想到什么不好?肯定感觉更干净,但是我看不出如何以其他方式完成此操作。

另外,我希望pbar或bar在FOO外面构造,所以这不是问题。

谢谢!

您不能(不应该)更改参考成员引用的对象:您在这里如何执行:Foo a,b; a=b;(假设您初始化非NULL null unique_ptr)。这很容易纠正用指针替换参考。

这个看起来像是一个好主意,您要做的就是缓存放弃。但是您正在失去pimpl成语的某些效率,并且正在使Foo的大小加倍。

您是否考虑过制作class impl标准布局并将Bar放在impl内的已知偏移量:

foo.h

constexpr auto impl_bar_offset = 8;
//...
class Foo{
  private:
    class impl;
    std::unique_ptr<impl> m_impl;
  public:
    bar& get_bar(){
       assert(m_impl);
       return *reinterpret_cast<bar*>(
         reinterpret_cast<unsigned char*>(m_impl.get())+impl_bar_offset);
    }
 };

foo.cpp

class impl{
  long a_long;
  bar a_bar;
  //...
  };
static_assert(impl_bar_offset==offsetof(impl,a_bar));