【发布时间】:2018-05-12 04:09:51
【问题描述】:
我有一个类,我想为它创建一个接口而不显示任何实现(不是因为它是封闭源代码,而是因为它附带了许多不必要的头文件,例如 OpenGL),让我们称这个类为 Foo。我知道这通常是通过 PImpl-idiom 或虚拟接口完成的,并且我已经为该类实现了 PImpl-idiom。
但是,这个类具有返回其他类的公共函数,这些类也包含这些标头,所以显然我不能按原样返回这些对象,因为这需要我包含我不想包含的标头呸。但是,这些类在库内部经常使用,因此我不想用 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 在创建时使用对对象内部实际 Bar 的引用来实例化 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 之外构造,所以这不是问题。
谢谢!
【问题讨论】:
标签: c++ design-patterns interface shared-libraries pimpl-idiom