0

たとえば、「CDownloader」というクラスがあります。このクラスは、いくつかのXMLデータを読み取り、ノード名によるアクセスを提供します。これは、次のようないくつかのゲッター関数を備えています。

BOOL CDownloader::getInteger ( const CString &name, int *Value );
BOOL CDownloader::getImage   ( const CString &name, BOOL NeedCache, CImage *Image );
BOOL CDownloader::getFont    ( const CString &name, CFont *Font );

CDownloaderクラスを変更できません。代わりに、実際の名前ではなく、boolフラグを使用してアイテムをダウンロードする関数をいくつか作成したいと思います。このようなもの:

BOOL DownloadFont( const CDownloader &Loader, bool Flag, CFont *Font )
{
   if (Flag) {
      // first try the "name_1"
      if ( Loader.getFont("name_1", Font) ) return TRUE;
   }
   // if "name_1" fails or disabled by flag, try "name_2"
   return Loader.getFont("name_2", Font);
}

Download(Font | Integer | Image)関数を個別に記述できますが、これによりコードが重複します。私の考えはテンプレートを作成することですが、それでも途方に暮れています。CDownloaderクラスからどのメソッドを呼び出す必要があるかをどのように判断できますか?データ型ごとにテンプレートを特殊化するということは、コードの重複に再び固執することを意味します。ゲッター関数を「関数へのポインター」パラメーターとして渡すには?しかし、ゲッターの署名はCDownloaderでは異なります...

要約すると、質問は次のとおりです。CDownloaderの周りにジェネリックラッパーを作成することは可能ですか、それとも「get ***」関数ごとにコードを複製する必要がありますか?前もって感謝します!

4

6 に答える 6

1

3 つの異なる名前の関数があり、型に応じて 1 つを選択する必要がある限り、ある時点で、適切な関数を選択するためにオーバーロードまたはいくつかの特性クラスが必要になります。それを回避する方法はないと思います。ただし、これを必要とするのはこれらの関数の 1 つを呼び出すことだけなので、これらのDownloadXXX()関数のコードが示されているよりも多くても、意味がある場合があります。

これは、オーバーロードの代替手段を使用してできることのスケッチです。まず、同じ関数の 3 つのオーバーロードが必要で、それぞれが 3 つの異なる関数の 1 つを呼び出します。関数の 1 つに追加のBOOLパラメーターを指定すると、汎用性が多少損なわれますが、すべての関数にそれを受け入れさせることで回避しましたがBOOL、そのうちの 2 つはそれを無視しています。

inline BOOL Load(CDownloader& Loader, const CString &name, int &Value, BOOL)
{return Loader.getInteger(name, &Value);

inline BOOL Load(CDownloader& Loader, const CString &name, CImage &Value, BOOL NeedCache)
{return Loader.getImage(name, NeedCache, &value);

inline BOOL Load(CDownloader& Loader, const CString &name, CFont &Value, BOOL)
{return Loader.getFont(name, &Font);

これで、その汎用関数を作成できます。ただし、それについて何をすべきかを決定する必要がありBOOLます。

template< typename T >
BOOL Download(const CDownloader &Loader, bool Flag, T &Obj, BOOL NeedCache /*= true*/)
{
   if (Flag) {
      if ( Load(Loader, "name_1", Obj, NeedCache) ) return TRUE;
   }
   return Load(Loader, "name_1", Obj, NeedCache);
}

ただし、おわかりのように、実際には、そのDownload関数がサンプル コードよりもはるかに複雑な場合にのみ、手間をかけるだけの価値があります。そうしないと、追加された複雑さが、汎用性の向上がもたらす利益を簡単に上回ってしまいます。

于 2009-09-16T15:30:23.300 に答える
1

さまざまな署名に適応できるため、関数オブジェクトが最適だと思います。

struct FontLoader {
    CFont *Font;
    FontLoader() {}
    BOOL operator()(const CDownloader& Loader, bool Flag) {
        if (Flag && Loader.getFont("name_1", Font) ) 
            return TRUE;
        return Loader.getFont("name_2", Font);
    }
};

struct ImageLoader {
    CImage *Image;
    BOOL NeedCache;
    ImageLoader(BOOL nc) : NeedCache(nc) {}
    BOOL operator()(const CDownloader& Loader, bool Flag) {
        if (Flag && Loader.getImage("name_3", NeedCache, Image) ) 
            return TRUE;
        return Loader.getImage("name_4", NeedCache, Image);
    }          
};

template <typename T> // T has application operator CDownloader x bool -> T1
BOOL Download( const CDownloader &Loader, bool Flag, T& func)
{
    return func(Loader, Flag);
}

呼び出しは次のようになります。

FontLoader Font_func;
BOOL ret1 = Download(Loader, Flag, Font_func);
ImageLoader Image_func(TRUE);
BOOL ret2 = Download(Loader, Flag, Image_func);

渡された構造体には、ダウンロードされたオブジェクトが含まれます。C++0x では、テンプレート パラメーター T の型チェックを改善する概念を定義できます。

于 2009-09-16T15:54:08.757 に答える
0

3 つのゲッターのメソッド シグネチャが異なるため、ジェネリック ラッパーを作成しても、コードや重複が少なくなるとは思いません。何があっても、これらの周りにラッパー関数が必要になります。3 つの異なる Download* 機能を持つ簡単な方法を使用することもできます。マクロを使用して条件付きロジックを中央の場所に保持することもできますが、それではおそらくコードが非常に読みにくくなり、その価値はありません。

于 2009-09-16T15:17:22.423 に答える
0

メンバー関数へのポインターを使用して、どこかにアクセスできます。

struct X
{
    bool getInt(int* p) const { *p = 42; return true; }
    bool getFloat(float* p) const { *p = 3.14; return true; }
};

template <class Func, class T>
bool load(const X& x, Func f, T* t)
{
    return (x.*f)(t);
}

int main()
{
    int i;
    float f;
    X x;
    load(x, &X::getInt, &i);
    load(x, &X::getFloat, &f);

    //load(x, &X::getFloat, &i);
}

getImage メソッドの例外により、さらに難しくなっています。代わりに、boost::bind / std::tr1::bind インスタンスなどでこれを機能させてみてください。

#include <boost/bind.hpp>

struct X
{
    bool getInt(int* p) const { *p = 42; return true; }
    bool getFloat(float* p, bool b) const { *p = 3.14; return b; }
};

template <class Func, class T>
bool load(Func f, T* t)
{
    return f(t);
}

int main()
{
    using namespace boost;
    int i;
    float f;
    X x;
    load(bind(&X::getInt, x, _1), &i);
    load(bind(&X::getFloat, x, _1, true), &f);
}
于 2009-09-16T15:46:24.780 に答える
-1

これはC-hackyの方法です。

void* DownloadFont( const CDownloader &Loader, bool Flag, CFont *Font )
{
   if (Flag) {
      // first try the "name_1"
      if ( Loader.getFont("name_1", Font) ) return (void*)1; //access this directly and *die*
   }
   // if "name_1" fails or disabled by flag, try "name_2"
   return (void*)(Loader.getFont("name_2", Font);
}

最後に、integer/font/image/foobars/magic monkeys の特別な取得に関連する何らかのロジックが必要になります。私はそれを吸い上げて Download*() ファミリーを書きます。

于 2009-09-16T15:54:05.033 に答える