假设我有一个名为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()
,它可以自动决定要设置哪个资源?
答案 0 :(得分: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
};
答案 1 :(得分:1)
让代码依赖于运行时类型的常用方法是 double dispatch ,a.k.a。访问者模式:
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
};
答案 2 :(得分: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的答案中)是另一种选择。
答案 3 :(得分:0)
对我而言,方法本身看起来是错误的。正如@LihO在他的评论中所说,多态性有助于以相同的方式处理不同类型的对象。所以从多态性的角度来看,你的设计应该看起来像:
// Usually a context contains three different resources:
class Context
{
public:
setResource(Resource *r);
private:
std::vector<Resource*> resources_;
};
其余部分应通过Resource
类的虚函数来解决。
使用dynamic_cast
通常意味着您的设计并不完美。
答案 4 :(得分: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_;
};
此时它并不像访客模式,但它相对简洁且效果很好。