3

これは、私が達成しようとしていることを説明するサンプルコードです。

基本的に、クラスで利用可能ないくつかの基本的な操作に依存するアルゴリズムがあります。これらの操作は、純粋な抽象基本クラスで定義しました。特定のオブジェクトのクラスを派生させることにより、そのアルゴリズムをさまざまなオブジェクトに適用して、それらの操作を提供したいと考えています。

ただし、これらの操作に関する限り、異なる派生オブジェクトは互いに互換性がありません。私の質問は、RTTI の使用を避けて、たとえば、ブール派生 2::同一 (const base* other2)、アサート (または他の終了メカニズム) で、other2 が派生 2 型ではないことを確認できるかどうかです。

1 つの代替手段は、特定の派生オブジェクトで関数アルゴリズムをテンプレート化することですが、それは実装がヘッダー ファイルに存在する必要があることを意味します。これは、1) テスト目的でアルゴリズム コードを変更すると、コードの大部分の再コンパイル 2) アルゴリズムの実装は、エンドユーザーから隠されているソース ファイルに適切に存在するのではなく、ヘッダーで公開されます。

ヘッダーファイル

#include <list>

class base
{
public:
 virtual float difference(const base*) const = 0;
 virtual bool identical(const base*) const = 0; 
};


class derived1 : public base
{
 public:
 float difference(const base* other1) const
 {
  // other1 has to be of type derived1
            if(typeid(other1) == typeid(this))
            {
                    // process ...
            }
            else
            {
                    assert(0);
            }
  return 1;
 }

 bool identical(const base* other1) const
 {
  // other1 has to be of type derived1
            if(typeid(other1) == typeid(this))
            {
                    // compare...
            }
            else
            {
                    assert(0);
            }
  return true;
 }
};


class derived2 : public base
{
 public:
        float difference(const base* other2) const
        { 
             // process ...
  // other2 has to be of type derived2
            return 2;
        }

 bool identical(const base* other2) const
        {
                // do comparison
  // derived1 and derived2 cannot be compared
                return true;
        }
};

// Declaration
int algorithm(std::list<base*>& members);

アルゴリズムの実装 ソースファイル

#include "header_file_containing_base"
int algorithm(std::list<base*>& members)
{
 // This function only relies on the interface defined in base
 // process members;

 return 1;
}

メインプログラム

int main()
{
  // Create lists of derived1 and derived2
  // Run algorithm on these lists
}
4

3 に答える 3

2

ダブルディスパッチを使用できます(http://en.wikipedia.org/wiki/Double_dispatch

于 2010-07-08T05:56:24.043 に答える
1

簡単なことが 1 つあります。実際の型をメンバーとして格納します。

  • すべてのenumタイプをグループ化した 。たくさんあると大変なことになります。
  • ID を生成するためのファクトリ (テンプレートを使用してアイテムごとに 1 つの ID のみを生成)
  • ...

工場IDを説明します:

class IdFactory
{
public:
  template <class T>
  static size_t GetId(T const&) // argument deduction
  {
    static size_t const Id = GetIdImpl();
    return Id;
  }

private:
  static size_t GetIdImpl()
  {
    static size_t Id = 0;
    return ++Id;
  }
}; // class IdFactory

そして、次のように使用できます。

class Base
{
public:
  explicit Base(size_t id): mId(id) {}
  size_t const mId; // meaningless to change it afterward...

private:
};

class Derived: public Base
{
public:
  explicit Derived(): Base(IdFactory::GetId(*this)) {}
};

mIdその後、メンバーをテストに使用できます。const公開できるので注意してください...それ以外の場合は、インラインconstゲッターを作成できます...

float Derived::difference(const Base& rhs)
{
  assert( IdFactory::GetId(*this) == rhs.mId );

  // ...
}

ここでのコストはごくわずかです。

  • GetIdインライン化されているため、関数呼び出しはありません
  • GetId初期化を除けば、staticメンバーが初期化されたことを確認することになります。通常、if条件が常に true と評価されるステートメントとして実装されます (初回を除く)。
  • ==通常は高速です;)

唯一の欠点は、ID を正しく初期化する必要があることです。

仮想関数呼び出しを含む、保存しないソリューションもあります。

class Other: public Base
{
public:
  virtual size_t id() const { return IdFactory::GetId(*this); }
};

constメンバーを格納しないということは、割り当てを自分で作成する必要がないことを意味するため、実践するのが簡単です。

于 2010-07-08T07:05:40.803 に答える
0

テンプレート化された関数を使用できます。テンプレートを使用すると、別のヘッダー ファイルに別のテンプレート関数を追加するだけで、元のクラスを変更する必要なく、後でクラスを追加できます。唯一の問題がコンパイル速度である場合は、テンプレート関数をヘッダーとは別にソース ファイルに実装し、明示的なテンプレート インスタンス化を使用できます。

于 2010-07-09T08:33:38.877 に答える