1

3種類のペットを飼っているPersonというクラスがあるとします。

class Person
{
public:
    accept(Pet a);
private:
    Dog d;  // is a Pet
    Cat c;  // is a Pet
    Fish f; // is a Pet
}

Person::accept(Pet a)
{
    // if pet is Dog, then
    d = a;
    // if pet is Cat, then
    c = a;
    // if pet is Fish, then
    f = a;
};

ここで使えると思いtypeidます。しかし、それでも私には奇妙に見えます。

適用できるポリモーフィズム、仮想関数、またはOOPパターンのようなものはありますか?

- 編集 -

ここの悪い例でごめんなさい。別のものを試してみましょう:

// Usually a context contains three different resources:
class Context
{
public:
    setResource(Resource *r);
private:
    Buffer *b_;    // is a Resource
    Kernel *k_;    // is a Resource
    Sampler *s_;   // is a Resource
};
Context::setResource(Resource *r) { // same logic as Person::accept() above }
Context::handlingBuffer() { if (b_) b_->init(); ... }
Context::run() { 
    if (b_ && k_) { 
        k_.setBuffer(b_); 
        k_.run(); 
    }
}
...

この場合、を追加すると事態はさらに複雑Resource *r_[3]になるようです。Context

では、Resourceの基本クラスのポインタをに渡すことは可能で、setResource()どのリソースを設定するかを自動的に決定できますか?

4

5 に答える 5

5

値で保持Petsしているので、ポリモーフィズムを忘れて、acceptメンバー関数をオーバーロードすることができます。

class Person
{
public:
    accept(const Dog& a)  { d_ = a; }
    accept(const Cat& a)  { c_ = a; }
    accept(const Fish& a) { f_ = a; }
private:
    Dog d_;  // is a Pet
    Cat c_;  // is a Pet
    Fish f_; // is a Pet
};
于 2013-01-24T08:58:02.913 に答える
1

コードをランタイムタイプに依存させる一般的な方法は、ダブルディスパッチ、別名ビジターパターンです。

class ResourceContext
{
public:
    virtual void setResource(Buffer* r) = 0;
    virtual void setResource(Kernel* r) = 0;
    virtual void setResource(Sampler* r) = 0;
};

class Resource
{
public:
    virtual void AddToContext(ResourceContext* cxt) = 0;

    [... rest of Resource ...]
};

class Buffer : public Resource
{
public:
     void AddToContext(ResourceContext* cxt) { cxt->SetResource(this); }
};

// Likewise for Kernel and Sampler.

class Context : public ResourceContext
{
public:
    void setResource(Resource* r) { r->AddToContext(this); }
    void setResource(Buffer *r)  { b_ = r; }
    void setResource(Kernel *r)  { k_ = r; }
    void setResource(Sampler *r) { s_ = r; }
private:
    Buffer *b_;    // is a Resource
    Kernel *k_;    // is a Resource
    Sampler *s_;   // is a Resource
};
于 2013-01-24T10:17:46.533 に答える
0

@molbdniloに触発されて、私は自分の目標を達成するためのより簡単な方法を見つけました。

class Resource
{
public:
    virtual void AddToContext(Context* c) = 0;
};

class Buffer : public Resource
{
public:
    void AddToContext(Context* c) { c->SetResource(this); }
};

// Likewise for Kernel and Sampler

class Context
{
public:
    void SetResource(Resource *r) { r->AddToContext(this); }
    void SetResource(Buffer *b) { b_ = b; }
    void SetResource(Kernel *k) { k_ = k; }
    void SetResource(Sampler *s) { s_ = s; }
    ...
private:
    Buffer *b_;
    Kernel *k_;
    Sampler *s_;
};

現時点ではVisitorパターンとは異なりますが、比較的簡潔で優れた機能を発揮します。

于 2013-01-25T06:44:12.033 に答える
0

あなたの簡単なサンプルコードについては、私はjuanchopanzaに同意します。ただし、ポインタを使用して構造を維持したい場合は、次のようにclass Person使用できますdynamic_cast<>

struct Pet { /* ... */ };
struct Dog : public Pet { /* ... */ };
struct Cat : public Pet { /* ... */ };
struct Fish : public Pet { /* ... */ };
struct Spider : public Pet { /* ... */ };

class Person {
  Dog*dog;
  Cat*cat;
  Fish*fish;
  Spider*yuck;

  template<typename PetType>
  static bool accept_pet(Pet*pet, PetType*&my_pet)
  {
    PetType*p = dynamic_cast<PetType*>(pet);
    if(p) {
      my_pet = p;
      return true;
    }
    return false;
  }
public:
  Person()
  : dog(0), cat(0), fish(0), yuck(0) {}
  void accept(Pet*pet)
  {
    if(accept_pet(pet,dog)) return;
    if(accept_pet(pet,cat)) return;
    if(accept_pet(pet,fish)) return;
    if(accept_pet(pet,yuck)) return;
    throw unknown_pet();
  }
};

追加するdynamic_cast<>必要があります、それは可能であれば避けるべきです。多くの場合(常にではありませんが)、それdynamic_cast<>を回避するために必要な設計を改善することができます。これは、(juanchopanzaの回答のように)単にオーバーロードすることaccept()が代替手段である場合にも当てはまります。

于 2013-01-24T09:53:12.917 に答える
0

私にとって、アプローチ自体は間違っているように見えます。@LihOが彼のコメントで述べたように、ポリモーフィズムは異なるタイプのオブジェクトを同じように扱うのに役立ちます。したがって、ポリモーフィズムの観点から、デザインは次のようになります。

// Usually a context contains three different resources:
class Context
{
public:
    setResource(Resource *r);
private:
    std::vector<Resource*> resources_;
};

Resource残りは、クラスの仮想関数によって解決される必要があります。

頻繁に使用dynamic_castするということは、デザインが完璧ではないことを意味します。

于 2013-01-24T10:04:13.510 に答える