ezp*_*sso 2 c++ design-patterns idioms
PIMPL Idiom是一种实现隐藏的技术,其中公共类包装公共类所属的库外部无法看到的结构或类.这会隐藏来自库用户的内部实现细节和数据.
但是可以实现相同的参考使用吗?
MCanvasFont.h
namespace Impl {
class FontDelegate;
}
class MCanvasFont
{
public:
MCanvasFont();
virtual ~MCanvasFont();
protected:
// Reference count
long m_cRef;
// agg font delegate
const Impl::FontDelegate& m_font;
}
Run Code Online (Sandbox Code Playgroud)
MCanvasFont.cpp
// helpers
#include "ImplHelpers/FontDelegate.h"
MCanvasFont::MCanvasFont()
: m_cRef(1),
m_font(Impl::FontDelegate() )
{
// constructor's body
}
Run Code Online (Sandbox Code Playgroud)
PS此代码编译时没有任何G ++问题.
程序中存在错误,它位于构造函数的初始化列表中:
MCanvasFont::MCanvasFont()
: m_cRef(1),
m_font(Impl::FontDelegate() ) // <--- BANG
{
Run Code Online (Sandbox Code Playgroud)
问题Impl::FontDelegate()在于它构造了一个临时对象.这不会比构造函数更长 - 实际上它在进入构造函数体之前实际上已被销毁,因为它的生命周期是它出现的表达式的生命周期.因此,您的m_font参考立即无效.
虽然您可以使用手动分配的对象(*new Impl::FontDelegate())初始化它,但如果分配失败,您将处于未定义的区域,除非您在运行时启用了异常.delete无论如何,你仍然需要在你的析构函数中使用该对象.所以这个引用确实没有给你带来任何好处,它只是为了一些相当不自然的代码.我建议使用const指针代替:
const Impl::FontDelegate* const m_font;
Run Code Online (Sandbox Code Playgroud)
编辑:只是为了说明问题,采取这个等效的例子:
#include <iostream>
struct B
{
B() { std::cout << "B constructed\n"; }
~B() { std::cout << "B destroyed\n"; }
};
struct A
{
const B& b;
A() :
b(B())
{
std::cout << "A constructed\n";
}
void Foo()
{
std::cout << "A::Foo()\n";
}
~A()
{
std::cout << "A destroyed\n";
}
};
int main()
{
A a;
a.Foo();
}
Run Code Online (Sandbox Code Playgroud)
如果你运行它,输出将是:
B constructed
B destroyed
A constructed
A::Foo()
A destroyed
Run Code Online (Sandbox Code Playgroud)
因此b几乎立即无效.