1

假设我有一个名为 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]inContext会使事情变得更加复杂。

那么,是否可以将 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

对于您的简单示例代码,我同意 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<>可以改进必要的设计以避免这种情况。这也适用于这里,当简单地重载accept()(如 juanchopanza 的答案)是一种替代方法时。

于 2013-01-24T09:53:12.917 回答
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_;
};

在这一点上,它不太像访问者模式,但它相对简洁并且效果很好。

于 2013-01-25T06:44:12.033 回答
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 回答