2

Foo直接実装しないクラスがありますが、外部ライブラリ(FooXternal1またはFooXternal2)をラップします。これを行うために見た1つの方法は、プリプロセッサディレクティブを次のように使用することです。

#include "config.h"
#include "foo.h"
#ifdef _FOOXTERNAL1_WRAPPER_
//implementation of class Foo using FooXternal1
#endif

#ifdef _FOOXTERNAL2_WRAPPER_
//implementation of class Foo using FooXternal2
#endif

aconfig.hは、これらのプリプロセッサフ​​ラグ(_FOOXTERNAL1_WRAPPER_および_FOOEXTERNAL2_WRAPPER_)を定義するために使用されます。これは、プリプロセッサディレクティブを使用している、デバッグが難しいなどの理由で、C ++プログラマーコミュニティに嫌われている印象があります。さらに、両方の実装を並行して存在させることはできません。

Foo基本クラスを作成し、それを継承して、両方の実装が互いに並行して存在できるようにすることを考えました。しかし、私は2つの問題に遭遇しました。

  1. 純粋仮想関数:cannot instatiate an object of type 'Foo'、使用中に必要です。
  2. 仮想関数は、(適切な)実装なしでオブジェクトを実行するリスクを伴います。

私は何かが足りないのですか?これを行うためのよりクリーンな方法はありますか?

編集:要約すると、ラッピングを行うには3(.5 ?!)の方法があります-2(.5)はicepackによって与えられ、最後はSergeyによって与えられます1-ファクトリメソッドを使用します2-プリプロセッサディレクティブを使用します2.5-makefileまたはプリプロセッサディレクティブの作業を効果的に行うためのIDE3.5-Sergayによって提案されたテンプレートを使用する

リソースが限られている組み込みシステムに取り組んでいるtemplate<enum = default_library>ので、テンプレートの特殊化を使用することにしました。後のユーザーにとっては理解しやすいです。少なくともそれは私が思うことです

4

4 に答える 4

2

外部実装のすべてのメソッド名が類似している場合は、テンプレートを使用できます。外部実装を次のようにします。

class FooX1
{
public:
    void Met1()
    {
        std::cout << "X1\n";
    }
};

class FooX2
{
public:
    void Met1()
    {
        std::cout << "X2\n";
    }
};

次に、いくつかのバリエーションを使用できます。

バリアント1.テンプレートタイプのメンバーを宣言し、呼び出しの前にいくつかの準備を行った場合でも、すべての呼び出しを外部実装にラップできます。デストラクタ で削除implすることを忘れないでください。~Foo

template<typename FooX>
class FooVariant1
{
public:
    FooVariant1()
    {
        impl=new FooX();
    }

    void Met1Wrapper()
    {
        impl->Met1();
    }
private:

    FooX *impl;
};

使用法:

FooVariant1<FooX1> bar;
bar.Met1Wrapper();

バリアント2。テンプレートパラメータから継承できます。この場合、メンバーを宣言せず、名前で実装のメソッドを呼び出すだけです。

template<typename FooX>
class FooVariant2 : public FooX
{
};

使用法:

FooVariant2<FooX1> bar;
bar.Met1();

テンプレートを使用することの欠点は、実行時に実装を変更する簡単な方法がないことです。ただし、型はコンパイル時に生成され、仮想関数のテーブルがないため、プログラムの速度が低下する可能性があるため、見返りとして、はるかに最適なコードが得られます。

于 2012-12-11T15:16:05.870 に答える
2

実行時に2つの実装を共存させたい場合は、インターフェイスが最適です(たとえば、@ nmが提案しているように、ファクトリメソッドデザインパターンを使用して具体的なオブジェクトをインスタンス化できます)。

コンパイル時に必要な実装を決定できる場合は、いくつかのオプションがあります。

  • 引き続きインターフェースを使用します。これにより、将来、実行時に両方の実装が必要になった場合に、簡単に移行できます。

  • プリプロセッサディレクティブを使用します。C ++を考慮する限り、ここでは何も問題はありません。これは純粋な設計上の問題です。

  • 実装を別のファイルに入れ、設定に従っていずれかをコンパイルするようにコンパイラを構成します-これは実際にはプリプロセッサディレクティブを使用するのと似ていますが、よりクリーンでコードにゴミを追加しません(フラグがsolution /makefileにあるため) /コンパイラが使用するものは何でも)。

于 2012-12-11T15:16:09.630 に答える
1

私が眉をひそめる唯一のことは、同じソースファイルに両方の実装を含めることです。それは混乱するかもしれません。それ以外の場合、これは、特に両方のライブラリを同時にリンクしていない場合に、プリプロセッサフ​​ラグが得意とすることの1つです。複数のオペレーティングシステムをサポートしているようなものです。すべての場合に一貫したインターフェースを提供し、実装の詳細を別の場所に隠します。

タイプFooは、各ライブラリに固有の情報を保持する必要がありますか?そうでない場合は、これで逃げることができるかもしれません:

#include "Foo.h"

#if defined _FOOXTERNAL1_WRAPPER_
    #include "Foo_impl1.cpp"
#elif defined _FOOXTERNAL2_WRAPPER_
    #include "Foo_impl2.cpp"
#else
    #error "Warn about a missing define here"
#endif

このようにして、仮想関数や継承を気にする必要がなく、メンバー関数が実装されなくなるのを防ぐことができます。

于 2012-12-11T15:03:27.107 に答える
1

Foo抽象化してください。ファクトリメソッドを提供する

Foo* MakeFoo();

FooImpl1タイプまたはのいずれかの新しいオブジェクトを割り当て、FooImpl2そのアドレスを返します。

ファクトリメソッドパターンに関するウィキペディア

于 2012-12-11T15:10:49.783 に答える