不透明的参考而不是PImpl。可能吗?

时间:2012-05-28 11:56:31

标签: 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;
}

MCanvasFont.cpp

// helpers
#include "ImplHelpers/FontDelegate.h"

MCanvasFont::MCanvasFont()
: m_cRef(1),
  m_font(Impl::FontDelegate() )
{
    // constructor's body
}

P.S。这段代码编译时没有任何G ++问题。

1 个答案:

答案 0 :(得分:5)

程序中存在错误,它位于构造函数的初始化列表中:

MCanvasFont::MCanvasFont()
: m_cRef(1),
  m_font(Impl::FontDelegate() ) // <--- BANG
{

Impl::FontDelegate()的问题在于它构造了一个临时对象。这不会比构造函数更长 - 实际上它在进入构造函数体之前实际上已被销毁,因为它的生命周期是它出现的表达式的生命周期。因此,您的m_font引用会立即失效。

虽然你的可能的使用手动分配的对象初始化它(*new Impl::FontDelegate())你在不确定的领土,如果除非你有你的运行时启用例外分配失败。无论如何,你仍然必须在你的析构函数中delete对象。所以这个引用确实没有给你带来任何好处,它只是为了一些相当不自然的代码。我建议改为使用const指针:

const Impl::FontDelegate* const m_font;

编辑:为了说明问题,请使用此等效示例:

#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();
}

如果你运行它,输出将是:

B constructed
B destroyed
A constructed
A::Foo()
A destroyed

所以b几乎立即失效。