我有一个类,我想创建一个接口而不显示任何实现(不是因为它是封闭的源代码,而是因为有很多不必要的标题,比如OpenGL随之而来),让我们调用该类Foo。我知道这通常是通过PImpl-idiom或虚拟接口完成的,我已经为该类实现了PImpl-idiom。
但是,这个类有公共函数,它返回其他类,也包括那些标题,所以显然我不能按原样返回那些对象,因为它要求我包含我不想包含的标题Foo.h.但是,这些类在库内部使用很多,因此我不想用PImpl实现它们,因为它会引入很多开销(它们在3D渲染器的代码中被大量使用)。我们打电话给其中一个Bar:
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可能包含我想要隐藏的标题。这让我可以选择让Bar使用PImpl-idiom,但我不希望Bar的开销,因为Bar在Foo内部使用很多。但是,我想办法解决这个问题,但是我不太确定它,因为我之前没有看到它曾经使用过:
使用PImpl而不使用拥有指针/引用来简单地包装类并为其创建接口。这样,开销只适用于Foo的外部,但在内部它仍将使用非包装类。
例如,假设PBar正在包装Bar:
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,并引用对象中的实际Bar:
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的原理或多或少相同,但有什么不好的我尚未考虑过吗?它肯定感觉更不干净,但我看不出如何以任何其他方式做到这一点。
另外,我希望PBo或Bar都不能在Foo之外构建,所以这不是问题。
谢谢!
答案 0 :(得分:0)
您不能(不应该)更改引用成员引用的对象:您如何在此处执行:Foo a,b; a=b;
(假设您初始化非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));