2

PIMPL イディオムは、パブリック クラスがその一部であるライブラリの外では見ることができない構造またはクラスをパブリック クラスがラップする実装隠蔽の手法です。これにより、内部実装の詳細とデータがライブラリのユーザーから隠されます。

しかし、参照を利用して同じことを実装することは可能ですか?

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
}

PS このコードは、G++ で問題なくコンパイルされます。

4

1 に答える 1

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ため、ほとんどすぐに無効になります。

于 2012-05-28T12:07:50.097 に答える