3

ライブラリに、ユーザーに公開したいクラスがあります。後でバイナリ互換性のない変更を行う可能性があるため、クラス全体を公開したくありません。次の方法のどれが最適かについて混乱しています。

ケース 1:

struct Impl1;
struct Handle1
{
  // The definition will not be inline and will be defined in a C file
  // Showing here for simplicity
  void interface()
  {
    static_cast<Impl1*>(this)->interface();
  }
}

struct Impl1 : public Handle1
{
  void interface(){ /* Do ***actual*** work */ }
  private:
  int _data; // And other private data
};

ケース 2:

struct Impl2
struct Handle2
{
  // Constructor/destructor to manage impl
  void interface() // Will not be inline as above.
  {
    _impl->interface();
  }
  private:
  Impl2* _impl;
}

struct Impl2
{
  void interface(){ /* Do ***actual*** work */ }
  private:
  int _data; // And other private data
};

Handleクラスは、機能を公開するためだけのものです。ライブラリ内でのみ作成および管理されます。継承は、実装の詳細を抽象化するためのものです。複数の/異なるimplクラスはありません。性能的にはどちらも同じだと思います。それは...ですか?ケース1のアプローチで行くことを考えています。対処する必要がある問題はありますか?

4

1 に答える 1

2

2番目のアプローチは、コンパイルファイアウォールイディオム( PIMPLイディオムとも呼ばれます)に非常によく似ています。

唯一の違いは、コンパイルファイアウォールのイディオムでは、実装クラスは通常(常にではありませんが)メンバーとして定義されていることです。コンストラクタ(Implを割り当てる)とデストラクタ(Implを解放する)を忘れないでください。コピーコンストラクタと代入演算子とともに。

最初のアプローチも機能しますが、オブジェクトを作成するにはファクトリ関数が必要になります。私がそれを使用したとき、私は単にすべての関数をHandle純粋な仮想で作成し、クライアントコードにそれらを直接呼び出させました。

この場合、クライアントコードには実際にはオブジェクトへのポインターが含まれているため(コンパイルファイアウォールイディオムでは、ポインターはHandleクラス自体にのみ存在します)、クライアントはメモリ管理について心配する必要があります。サイクルが不可能な場合、これはshared_ptr非常に理にかなっている1つのケースです。shared_ptr(たとえば、ファクトリ関数はを返すことができ 、クライアントコードは生のポインタを見ることはありません。)

于 2012-09-28T11:01:40.373 に答える