3

Pimpl イディオムを使用する次の 2 つのクラスを考えてみましょう。

ClassA: Pimpl クラスの前方宣言と変数宣言を別の行で行う

ClassA.h:

#include <memory>

class ClassA {
public:
    ClassA();
    ~ClassA();
    void SetValue( int value );
    int GetValue() const;

private:

    class ClassA_Impl;
    // ^^^^^^^^^^^^^^ class forward declaration on its own line

    std::unique_ptr<ClassA_Impl> m_pImpl;
    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ variable declaration on its own line

    //EDIT:
    //Even if we use a raw pointer instead of a smart pointer,
    //i.e. instead of declaring the smart pointer above, if we declare:
    ClassA_Impl *m_pImpl;
    //the situation in the *.cpp files and my questions (below) are the same.


};

ClassA.cpp:

#include "ClassA.h"

class ClassA::ClassA_Impl {
public:
    void SetValue( int value );
    int GetValue() const;
private:
    int value_;   
};

// Private class implementation
void
ClassA::ClassA_Impl::SetValue( int value ) {
    value_ = value;
}

int
ClassA::ClassA_Impl::GetValue() const {
    return value_;
}

// ClassA implementation
ClassA::ClassA() : m_pImpl( new ClassA_Impl() ) {}

ClassA::~ClassA() {}

void
ClassA::SetValue( int value ) {
    m_pImpl->SetValue( value );
}

int
ClassA::GetValue() const {
    return m_pImpl->GetValue();
}

ClassB: Pimpl クラスの前方宣言と変数宣言を 1 行で

ClassB.h:

#include <memory>

class ClassB {
public:
    ClassB();
    ~ClassB();
    void SetValue( int value );
    int GetValue() const;

    private:
        std::unique_ptr<class ClassB_Impl> m_pImpl;
        //             ^^^^^^^^^^^^^^^^^^ class forward declaration
        //             combined with variable declaration on one line,
        //             in one shot.

        //EDIT:
        //Even if we use a raw pointer instead of a smart pointer,
        //i.e. instead of declaring the smart pointer above, if we declare:
        class ClassB_Impl *m_pImpl;
        //the situation in the *.cpp files and my questions (below) are the same.
};

ClassB.cpp:

#include "ClassB.h"

class ClassB_Impl {
public:
    void SetValue( int value );
    int GetValue() const;
private:
    int value_;
};

// Private class implementation
void
ClassB_Impl::SetValue( int value ) {
    value_ = value;
}

int
ClassB_Impl::GetValue() const {
    return value_;
}

// ClassB implementation
ClassB::ClassB() : m_pImpl( new ClassB_Impl() ) {}

ClassB::~ClassB() {}

void
ClassB::SetValue( int nValue ) {
    m_pImpl->SetValue( nValue );
}

int
ClassB::GetValue() const {
    return m_pImpl->GetValue();
}

質問:

  1. ClassB.h の 1 行で前方宣言と変数宣言を組み合わせると、ClassB.cppClassB_Implのプライベート クラスの実装で「スコープを解除」する必要があるのはなぜですか?

    つまり、ClassA.cpp では、プライベート クラス メソッドの定義は

    void ClassA::ClassA_Impl::foo() {...
    

    ただし、ClassB.cpp では、プライベート クラス メソッドの定義は

    void ClassB_Impl::foo() {...
    
  2. 各方法の意味は何ですか?どちらの方がよいですか?

  3. Galikの回答に対するフォローアップの質問

    クラスの前方宣言とそのクラスの変数の宣言を1つのステートメントで組み合わせると...

    //one-liner
    class ClassB_Impl *m_pImpl;
    

    ...これは何と呼ばれていますか?この種の組み合わせステートメントの名前はありますか? そして、なぜそのようなステートメントの結果として のClassB_Impl内部クラスにならないのでしょうか?ClassB

    これと比較して...

    //two-liner
    class ClassA_Impl;
    ClassA_Impl *m_pImpl;
    

    ...その場合、 の内部クラスになりClassA_Impl ますClassA

    ワンライナーがClassB_Implグローバル名前空間に配置され、ツーライナーがの名前空間に配置ClassA_Implされるのはなぜですか? ClassAなぜ違うのですか?

4

1 に答える 1

1

ClassB.h の 1 行で前方宣言と変数宣言を組み合わせると、ClassB.cpp のプライベート クラスの実装で ClassB_Impl を「スコープ外」にする必要があるのはなぜですか?

最初の例では、 の内部クラスClassA_Implとして宣言しているためです。ClassA

の一部ではないテンプレート パラメータで宣言するClassB_Impl場合。ClassB

各方法の意味は何ですか?どちらの方がよいですか?

これは意見の問題です。個人的には、内部クラスは面倒で、ほとんど報酬を得るのが難しいと思います。

私の推奨する方法は、インターフェイスを再宣言する回数を減らすのに役立つ別のインターフェイスクラスを使用します。

参照: c++ でアジャイル Pimpl を作成することは可能ですか?

于 2016-02-08T06:35:52.283 に答える