【问题标题】:Opaque reference instead of PImpl. Is it possible?不透明的参考而不是 PImpl。可能吗?
【发布时间】:2012-06-02 19:10:41
【问题描述】:

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
}

附:这段代码用 G++ 编译没有任何问题。

【问题讨论】:

    标签: c++ design-patterns idioms


    【解决方案1】:

    你的程序有一个错误,它在构造函数的初始化列表中:

    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 几乎立即无效。

    【讨论】:

    • 如果分配失败,你会得到一个异常,所以那里没有问题。 (除了通常的构造函数异常问题)
    • 可以使用单例模式静态分配对象。它的 GetInstance 将返回对静态对象的引用。
    • 临时的生命周期实际上被延长到覆盖构造函数的主体;但正如你所说,它肯定不会涵盖对象的生命周期。
    • @MikeSeymour 嗯,GCC 似乎不同意 - 当我使用记录的构造函数/析构函数运行示例时,临时在构造函数的主体之前被销毁。
    • @Hurkyl 是的,尽管许多禁用异常在这种情况下 new 只返回 NULL。
    猜你喜欢
    • 2011-01-28
    • 1970-01-01
    • 2011-09-10
    • 2014-05-27
    • 1970-01-01
    • 2021-02-02
    • 2011-06-18
    • 2011-11-06
    • 1970-01-01
    相关资源
    最近更新 更多