クラスをインターフェイスの実装として使用したい場合がありますが、コードを変更したり、このクラスをカプセル化したりできません (派生など)。他の状況では、そのような状況を回避したい場合があります。// 次の例では、コードを変更できない次の「封印された」クラスがあると仮定します。
class my_sealed_class;
class my_lock
{
friend class my_sealed_class;
my_lock() {}
my_lock(my_lock const&) {}
};
class my_sealed_class : virtual my_lock
{
private:
int x;
public:
void set_x(int val) { x = val; }
int get_x() const { return x; }
};
基本的に 2 種類のデザイン パターンから選択できます。 1.) インターフェイスのメンバー オブジェクトとしての実装。
template<class implementation>
class my_interface0
{
private:
implementation m_impl;
public:
void set_x(int val) { m_impl.set_x(val); }
int get_x() const { return m_impl.get_x(); }
};
C++03 テンプレートの機能により、これはかなり型安全です。多くのコンパイラは、実際に呼び出された「実装」のメンバー関数をインライン化します - 残念ながら、すべてのコンパイラではありません。場合によっては、不要な呼び出しに対処する必要があります。
2.) ベースから派生せずに CRTP パターンを使用する。
template<class derived>
class my_interface1
{
public:
void set_x(int val) { reinterpret_cast<derived*>(this)->set_x(val); }
int get_x() const { return reinterpret_cast<derived const*>(this)->get_x(); }
};
ええ、私は知っています: それは CRTP パターンが使用される通常の方法ではありません。通常、次のように定義します。
template<class derived> class base { void foo() { static_cast<derived*>(this)->foo(); } };
class derived : base<derived> { void foo() {} };
残念ながら、これはできません。実装クラスのコードは変更できないことに注意してください。私のバージョンの CRTP パターンで my_interface1 を使用して正確に何が起こっているか考えてみましょう。プログラミング言語 C++03 の理論上、派生クラス ポインターを基本クラスの 1 つにキャストしても安全です。一方、反対方向 (ベースから派生) にキャストしたり、まったく異なる型にキャストしたりすることは安全ではありません。メモリ オブジェクトが基本クラスと派生クラスに必要な数のバイトを予約しているという保証はありません。しかし、実際には、インターフェイスにはメンバーが含まれていないため、これはこの例には属しません。したがって、サイズは 0 バイトです (クラスが空の場合でも、演算子 new は少なくとも 1 バイトを割り当てることに注意してください)。この場合は' s - 実際には、任意の型ポインタを 'my_interface1 ポインタにキャストしても安全です。決定的な利点は、ほとんどすべてのコンパイラが実際に呼び出されたメンバー関数への呼び出しをインライン化することです。
int main()
{
// Not even worth to mention: That's safe.
my_sealed_class msc;
msc.set_x(1);
// Safe, because 'my_interface0' instantiates 'my_sealed_class'.
my_interface0<my_sealed_class> mi0;
mi0.set_x(2);
// Absolutely unsafe, because 'my_interface1' will access memory which wasn't allocated by it.
my_interface1<my_sealed_class> mi1;
mi1.set_x(3);
// Safe, because 'my_interface1*' references an my_sealed_class object.
my_interface1<my_sealed_class>* pmi1 = reinterpret_cast<my_interface1<my_sealed_class>*>(&msc);
pmi1->set_x(4);
return 0;
}
では、ベストプラクティスは何だと思いますか? よろしくお願いします。